Skip to content

Commit 6f070e9

Browse files
committed
Added example script
1 parent 4c4a754 commit 6f070e9

File tree

5 files changed

+240
-0
lines changed

5 files changed

+240
-0
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- Added Site-to-site example
99
- Removed `compression` from examples
1010
- Switched to bash wizards
11+
- Added auth-pass-verify example script
1112

1213
### 2.0.6 - Fixed bugs, added additonal parameters
1314

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,8 @@ For more infromation see:
164164
- **configuration example directory** (for more info about example)
165165
- [Contributing](CONTRIBUTING.md) (for explanation how container works, how to write an example config ...)
166166

167+
**Note:** OpenVPN documentation is located at `/usr/share/doc/openvpn`.
168+
167169
### Client
168170

169171
1. Run container to get config structure `docker run -it --rm -v PATH:/config slocomptech/openvpn`.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Authentication script
2+
3+
This is sample *authentication script* for **auth-user-pass-verify** hook.
4+
5+
## Configuration
6+
7+
``` bash
8+
cp auth.py /config/openvpn/hooks/auth
9+
10+
# Edit database name in copied auth.py script
11+
12+
# Add option to openvpn config
13+
echo 'auth-user-pass-verify "/app/hook.sh auth"' >> /config/openvpn/include-server.conf
14+
```
15+
16+
## Add user
17+
18+
``` bash
19+
# Note: db file must end with .csv or .json
20+
./adduser.py /config/openvpn/hooks/auth db.csv <username> <password>
21+
```
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#!/usr/bin/python
2+
3+
#
4+
# Script for adding username & password to database
5+
# @author Martin Dagarin
6+
# @version 1
7+
# @since 22/03/2020
8+
#
9+
# Usage:
10+
# adduser.py <databasepath> <username> <password>
11+
#
12+
13+
import binascii
14+
import hashlib
15+
import json
16+
import os
17+
import sys
18+
19+
def getExtensionFromPath(path):
20+
return os.path.splitext(path)[1][1:]
21+
22+
def hash_password(password):
23+
"""Hash a password for storing."""
24+
salt = hashlib.sha256(os.urandom(60)).hexdigest().encode('ascii')
25+
pwdhash = hashlib.pbkdf2_hmac('sha512', password.encode('utf-8'),
26+
salt, 100000)
27+
pwdhash = binascii.hexlify(pwdhash)
28+
return (salt + pwdhash).decode('ascii')
29+
30+
def main():
31+
if len(sys.argv) < 4:
32+
print('Usage: %s <database> <username> <password>' % sys.argv[0])
33+
sys.exit(1)
34+
35+
username = sys.argv[2].strip()
36+
password = hash_password(sys.argv[3].strip())
37+
38+
database = sys.argv[1].strip()
39+
dbextention = getExtensionFromPath(database)
40+
41+
if dbextention == 'csv':
42+
with open(database, 'a') as file:
43+
file.write('%s,%s\n' % (username, password))
44+
elif dbextention == 'json':
45+
if os.path.exists(database):
46+
with open(database, 'r') as file: # Open file
47+
data = json.loads(file.read())
48+
else:
49+
data = []
50+
data.append({
51+
'username': username,
52+
'password': password
53+
})
54+
with open(database, 'w') as file:
55+
file.write(json.dumps(data, indent=2))
56+
57+
if __name__ == '__main__':
58+
main()
Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
#!/usr/bin/python
2+
3+
#
4+
# Script for checking OpenVPN name & password
5+
# @author Martin Dagarin
6+
# @version 2
7+
# @since 22/03/2020
8+
#
9+
# Usage:
10+
# username=<username> password=<password> auth.py # via-env
11+
# auth.py <filepath> # via-file
12+
# # For OpenVPN auth-pass-verify hook just set path to stript
13+
#
14+
# NOTE: Please set DATABASE to use
15+
#
16+
17+
import argparse
18+
import binascii
19+
import hashlib
20+
import json
21+
import os
22+
import sys
23+
24+
25+
# Settings
26+
DATABASE = os.path.dirname(sys.argv[0]) + '/db.csv'
27+
28+
# Return code of program
29+
STATUS_SUCCESS = 0
30+
STATUS_ERROR = 1
31+
32+
def getExtensionFromPath(path):
33+
return os.path.splitext(path)[1][1:]
34+
35+
#
36+
# Verify stored hash password
37+
#
38+
def verify_password(stored_password, provided_password):
39+
"""Verify a stored password against one provided by user"""
40+
salt = stored_password[:64]
41+
stored_password = stored_password[64:]
42+
pwdhash = hashlib.pbkdf2_hmac('sha512',
43+
provided_password.encode('utf-8'),
44+
salt.encode('ascii'),
45+
100000)
46+
pwdhash = binascii.hexlify(pwdhash).decode('ascii')
47+
return pwdhash == stored_password
48+
#
49+
# Gets OpenVPN env vars
50+
# @returns username and password as dictionary
51+
#
52+
def ovpn_viaEnv():
53+
try:
54+
return {
55+
'username': os.environ['username'],
56+
'password': os.environ['password']
57+
}
58+
except KeyError:
59+
return { 'username': None, 'password': None }
60+
61+
62+
#
63+
# Reads OpenVPN auth file
64+
# @returns username and password as dictionary
65+
#
66+
def ovpn_viaFile(path):
67+
if not os.path.exists(path) or not os.path.isfile(path):
68+
return { 'username': None, 'password': None }
69+
70+
with open(path,"r") as file:
71+
return { 'username': file.readline(), 'password': file.readline() }
72+
73+
#
74+
# Authenticates user with database
75+
# @return True if OK, else False
76+
#
77+
def authenticate(database, credentials):
78+
# Check if username specified
79+
if credentials['username'] is None:
80+
return False
81+
82+
# Remove leading & tailing spaces
83+
credentials['username'] = credentials['username'].strip()
84+
if credentials['password'] is not None:
85+
credentials['password'] = credentials['password'].strip()
86+
87+
dbextension = getExtensionFromPath(database).lower()
88+
89+
try:
90+
if dbextension == 'json':
91+
#
92+
# .json user database
93+
# stored in format:
94+
# {
95+
# "username": "username",
96+
# "password": "password"
97+
# }
98+
#
99+
with open(database, 'r') as file: # Open file
100+
data = json.loads(file.read()) # Parse JSON content
101+
for user in data: # Iterate through list of users
102+
if user['username'].strip() == credentials['username']:
103+
if verify_password(user['password'], credentials['password']): # Success
104+
print('Login: User %s' % credentials['username'])
105+
return True
106+
else: # Wrong password
107+
print('Login: Wrong password for %s' % credentials['username'])
108+
return False
109+
# Username not found
110+
print('Login: Username %s not found' % credentials['username'])
111+
return False
112+
elif dbextension == 'csv':
113+
#
114+
# .csv user database
115+
# format:
116+
# username,password
117+
#
118+
with open(database, 'r') as file: # Open file
119+
for line in file:
120+
line = line.strip()
121+
if len(line) == 0: # Skip empty lines
122+
continue
123+
cells = line.split(',')
124+
if len(cells) < 2: # Skip faulty lines
125+
continue
126+
if cells[0].strip() == credentials['username']:
127+
if verify_password(cells[1].strip(), credentials['password']): # Success
128+
print('Login: User %s' % credentials['username'])
129+
return True
130+
else: # Wrong password
131+
print('Login: Wrong password for %s' % credentials['username'])
132+
return False
133+
# Username not found
134+
print('Login: Username %s not found' % credentials['username'])
135+
return False
136+
except IOError:
137+
pass
138+
139+
# Authentication method not found
140+
print('Authentication method not found')
141+
return False
142+
143+
144+
def main():
145+
if len(sys.argv) < 2: # File not specified
146+
credentials = ovpn_viaEnv()
147+
else: # File specified
148+
credentials = ovpn_viaFile(sys.argv[1])
149+
150+
if authenticate(DATABASE, credentials):
151+
print('Success')
152+
sys.exit(STATUS_SUCCESS)
153+
else:
154+
print('Fail')
155+
sys.exit(STATUS_ERROR)
156+
157+
if __name__ == '__main__':
158+
main()

0 commit comments

Comments
 (0)