diff options
author | Sam Chudnick <sam@chudnick.com> | 2022-07-02 15:35:50 -0400 |
---|---|---|
committer | Sam Chudnick <sam@chudnick.com> | 2022-07-02 15:35:50 -0400 |
commit | 8472b394ee44cd46cc36fd4fe0a4882364cab602 (patch) | |
tree | 301fcb5e0becbebff4486b556e561afac61e11ca | |
parent | 01c24eb1f6f6a54bb780940c7665acd280b42aaf (diff) |
Read options from config file
Set a standardized configuration file location and read options from
there. Allow for specifiying alternate location on command line.
Options can still be specified on the command line, and any command line
options take priority over those given in the configuration file.
-rwxr-xr-x | client/client.py | 64 | ||||
-rwxr-xr-x | pam/pam.py | 40 | ||||
-rwxr-xr-x | server/mfad.py | 73 |
3 files changed, 143 insertions, 34 deletions
diff --git a/client/client.py b/client/client.py index b2429b6..1c7e155 100755 --- a/client/client.py +++ b/client/client.py | |||
@@ -4,6 +4,7 @@ import socket | |||
4 | import time | 4 | import time |
5 | import argparse | 5 | import argparse |
6 | import sys | 6 | import sys |
7 | import os | ||
7 | 8 | ||
8 | HEADER_LENGTH = 64 | 9 | HEADER_LENGTH = 64 |
9 | KEY_LENGTH = 64 | 10 | KEY_LENGTH = 64 |
@@ -11,6 +12,7 @@ DISCONNECT_LENGTH = ACK_LENGTH = 3 | |||
11 | ACK_MESSAGE = "ACK" | 12 | ACK_MESSAGE = "ACK" |
12 | DISCONNECT_MESSAGE = "BYE" | 13 | DISCONNECT_MESSAGE = "BYE" |
13 | FORMAT = "utf-8" | 14 | FORMAT = "utf-8" |
15 | import configparser | ||
14 | 16 | ||
15 | def parse_arguments(): | 17 | def parse_arguments(): |
16 | parser = argparse.ArgumentParser() | 18 | parser = argparse.ArgumentParser() |
@@ -18,7 +20,7 @@ def parse_arguments(): | |||
18 | parser.add_argument("--port",type=int,help="Port to connect to") | 20 | parser.add_argument("--port",type=int,help="Port to connect to") |
19 | parser.add_argument("--config",type=str,help="Path to config file",\ | 21 | parser.add_argument("--config",type=str,help="Path to config file",\ |
20 | default="/etc/mfa/mfa.conf") | 22 | default="/etc/mfa/mfa.conf") |
21 | parser.add_argument("--key",type=str,help="Client connection key",required=True) | 23 | parser.add_argument("--key",type=str,help="Client connection key") |
22 | return parser.parse_args() | 24 | return parser.parse_args() |
23 | 25 | ||
24 | def prompt_user(prompt): | 26 | def prompt_user(prompt): |
@@ -53,32 +55,48 @@ def init_connection(mfa_server, client_port, client_key): | |||
53 | 55 | ||
54 | 56 | ||
55 | def read_config(config_file): | 57 | def read_config(config_file): |
56 | # Read config file for server and port info | 58 | parser = configparser.ConfigParser(inline_comment_prefixes="#") |
57 | # Return tuple (server,port) | 59 | parser.read(config_file) |
58 | server = "" | 60 | return parser |
59 | port = 0 | 61 | |
60 | with open(config_file) as conf: | 62 | |
61 | line = None | 63 | def get_vars(args,confparser): |
62 | while line != "": | 64 | if not os.path.exists(args.config): |
63 | line = conf.readline() | 65 | print("Unable to open config file") |
64 | if line.startswith("server ="): | 66 | sys.exit(1) |
65 | server = line.split("=")[1].strip() | 67 | |
66 | if line.startswith("port ="): | 68 | server = None |
67 | port = int(line.split("=")[1].strip()) | 69 | port = None |
68 | return (server,port) | 70 | key = None |
71 | |||
72 | # Set values from config file first | ||
73 | if confparser.has_section("client"): | ||
74 | server = confparser.get("client","server",fallback=None) | ||
75 | port = confparser.get("client","port",fallback=None) | ||
76 | key = confparser.get("client","key",fallback=None) | ||
77 | |||
78 | # Let command line args overwrite any values | ||
79 | if args.server: | ||
80 | server = args.server | ||
81 | if args.port: | ||
82 | port = args.port | ||
83 | if args.key: | ||
84 | key = args.key | ||
85 | |||
86 | # Exit if any value is null | ||
87 | if None in [server,port,key]: | ||
88 | print("error: one or more items unspecified") | ||
89 | sys.exit(1) | ||
90 | |||
91 | return server,port,key | ||
92 | |||
69 | 93 | ||
70 | def main(): | 94 | def main(): |
71 | # Get arguments, exit if unable to connect | 95 | # Get arguments, exit if unable to connect |
72 | args = parse_arguments() | 96 | args = parse_arguments() |
73 | client_key = args.key | 97 | confparser = read_config(args.config) |
74 | 98 | ||
75 | # Read server and port from config file but allow command line options | 99 | mfa_server,client_port,client_key = get_vars(args,confparser) |
76 | # to override those settings | ||
77 | mfa_server, client_port = read_config(args.config) | ||
78 | if args.server != None: | ||
79 | mfa_server = args.server | ||
80 | if args.port != None: | ||
81 | client_port = args.port | ||
82 | 100 | ||
83 | # Exit if invalid key is provided | 101 | # Exit if invalid key is provided |
84 | if len(client_key) != KEY_LENGTH: | 102 | if len(client_key) != KEY_LENGTH: |
@@ -3,6 +3,8 @@ import socket | |||
3 | import argparse | 3 | import argparse |
4 | import time | 4 | import time |
5 | import sys | 5 | import sys |
6 | import configparser | ||
7 | import os | ||
6 | 8 | ||
7 | # Sends authentication request to MFA server | 9 | # Sends authentication request to MFA server |
8 | # Receive either pass or fail response from MFA server | 10 | # Receive either pass or fail response from MFA server |
@@ -40,7 +42,6 @@ def init_connection(mfa_server, pam_port): | |||
40 | while connection == None and timeout < timeout_length: | 42 | while connection == None and timeout < timeout_length: |
41 | try: | 43 | try: |
42 | connection = socket.create_connection((mfa_server,pam_port)) | 44 | connection = socket.create_connection((mfa_server,pam_port)) |
43 | print("connected to mfa server") | ||
44 | return connection | 45 | return connection |
45 | except (ConnectionError,ConnectionRefusedError): | 46 | except (ConnectionError,ConnectionRefusedError): |
46 | time.sleep(sleep_length) | 47 | time.sleep(sleep_length) |
@@ -63,19 +64,54 @@ def read_config(config_file): | |||
63 | port = int(line.split("=")[1].strip()) | 64 | port = int(line.split("=")[1].strip()) |
64 | return (server,port) | 65 | return (server,port) |
65 | 66 | ||
67 | |||
68 | def read_config(config_file): | ||
69 | parser = configparser.ConfigParser(inline_comment_prefixes="#") | ||
70 | parser.read(config_file) | ||
71 | return parser | ||
72 | |||
73 | |||
74 | def get_vars(args,confparser): | ||
75 | if not os.path.exists(args.config): | ||
76 | print("Unable to open config file") | ||
77 | sys.exit(1) | ||
78 | |||
79 | server = None | ||
80 | port = None | ||
81 | |||
82 | # Set values from config file first | ||
83 | if confparser.has_section("pam"): | ||
84 | server = confparser.get("pam","server",fallback=None) | ||
85 | port = confparser.get("pam","port",fallback=None) | ||
86 | |||
87 | # Let command line args overwrite any values | ||
88 | if args.server: | ||
89 | server = args.server | ||
90 | if args.port: | ||
91 | port = args.port | ||
92 | |||
93 | # Exit if any value is null | ||
94 | if None in [server,port]: | ||
95 | print("error: one or more items unspecified") | ||
96 | sys.exit(1) | ||
97 | |||
98 | return server,port | ||
99 | |||
100 | |||
66 | def main(): | 101 | def main(): |
67 | authed = "0" | 102 | authed = "0" |
68 | failed = "1" | 103 | failed = "1" |
69 | 104 | ||
70 | # Get arguments | 105 | # Get arguments |
71 | args = parse_arguments() | 106 | args = parse_arguments() |
107 | confparser = read_config(args.config) | ||
108 | mfa_server,pam_port = get_vars(args,confparser) | ||
72 | user = args.user | 109 | user = args.user |
73 | service = args.service | 110 | service = args.service |
74 | 111 | ||
75 | # Compile data to send to server | 112 | # Compile data to send to server |
76 | # Read server and port from config file but allow command line options | 113 | # Read server and port from config file but allow command line options |
77 | # to override those settings | 114 | # to override those settings |
78 | mfa_server, pam_port = read_config(args.config) | ||
79 | if args.server != None: | 115 | if args.server != None: |
80 | mfa_server = args.server | 116 | mfa_server = args.server |
81 | if args.port != None: | 117 | if args.port != None: |
diff --git a/server/mfad.py b/server/mfad.py index d045e14..46fc0cc 100755 --- a/server/mfad.py +++ b/server/mfad.py | |||
@@ -7,6 +7,8 @@ import threading | |||
7 | import pyotp | 7 | import pyotp |
8 | import sqlite3 | 8 | import sqlite3 |
9 | import re | 9 | import re |
10 | import configparser | ||
11 | import argparse | ||
10 | 12 | ||
11 | ## Listens for authentication request from PAM module | 13 | ## Listens for authentication request from PAM module |
12 | ## Recevies connection from client | 14 | ## Recevies connection from client |
@@ -16,7 +18,7 @@ import re | |||
16 | ## Return pass or fail response to PAM moudle | 18 | ## Return pass or fail response to PAM moudle |
17 | 19 | ||
18 | 20 | ||
19 | DB_NAME = "mfa.db" | 21 | DB_NAME = "" |
20 | HEADER_LENGTH = 64 | 22 | HEADER_LENGTH = 64 |
21 | KEY_LENGTH = 64 | 23 | KEY_LENGTH = 64 |
22 | DISCONNECT_LENGTH = ACK_LENGTH = 3 | 24 | DISCONNECT_LENGTH = ACK_LENGTH = 3 |
@@ -41,6 +43,22 @@ CLIENT_SECRET_INDEX = 2 | |||
41 | # key and a tuple of (socket,(addr,port)) as the value | 43 | # key and a tuple of (socket,(addr,port)) as the value |
42 | client_connections = dict() | 44 | client_connections = dict() |
43 | 45 | ||
46 | def parse_arguments(): | ||
47 | parser = argparse.ArgumentParser() | ||
48 | parser.add_argument("--address",type=str,help="Bind Address") | ||
49 | parser.add_argument("--pam-port",type=int,help="Port to listen for PAM requests") | ||
50 | parser.add_argument("--client-port",type=int,help="Port for client connections") | ||
51 | parser.add_argument("--database",type=str,help="Path to alternate database file") | ||
52 | parser.add_argument("--config",type=str,help="Alternate config file location",\ | ||
53 | default="/etc/mfa/mfa.conf") | ||
54 | return parser.parse_args() | ||
55 | |||
56 | |||
57 | def read_config(config): | ||
58 | parser = configparser.ConfigParser(inline_comment_prefixes="#") | ||
59 | parser.read(config) | ||
60 | return parser | ||
61 | |||
44 | 62 | ||
45 | def eval_mfa(client_key, mfa_methods, client_response): | 63 | def eval_mfa(client_key, mfa_methods, client_response): |
46 | print("response: " + client_response) | 64 | print("response: " + client_response) |
@@ -228,14 +246,14 @@ def listen_pam(addr, port): | |||
228 | 246 | ||
229 | ################################################################################ | 247 | ################################################################################ |
230 | 248 | ||
231 | def create_db(): | 249 | def create_db(db): |
232 | with sqlite3.connect(DB_NAME) as conn: | 250 | with sqlite3.connect(db) as conn: |
233 | c = conn.cursor() | 251 | c = conn.cursor() |
234 | c.execute("""CREATE TABLE applications ( | 252 | c.execute("""CREATE TABLE applications ( |
235 | username text, | 253 | username text, |
236 | hostname text, | 254 | hostname text, |
237 | service text, | 255 | service text, |
238 | client_key text, | 256 | alias text, |
239 | mfa_methods text | 257 | mfa_methods text |
240 | )""") | 258 | )""") |
241 | c.execute("""CREATE TABLE clients ( | 259 | c.execute("""CREATE TABLE clients ( |
@@ -243,16 +261,53 @@ def create_db(): | |||
243 | key text, | 261 | key text, |
244 | totp_secret text | 262 | totp_secret text |
245 | )""") | 263 | )""") |
264 | conn.commit() | ||
265 | |||
266 | |||
267 | def get_vars(args,confparser): | ||
268 | if not os.path.exists(args.config): | ||
269 | print("Unable to open config file") | ||
270 | sys.exit(1) | ||
271 | |||
272 | bind_addr = None | ||
273 | client_port = None | ||
274 | pam_port = None | ||
275 | database = None | ||
276 | |||
277 | # Set values from config file first | ||
278 | if confparser.has_section("mfad"): | ||
279 | bind_addr = confparser.get("mfad","address",fallback=None) | ||
280 | client_port = confparser.get("mfad","client-port",fallback=None) | ||
281 | pam_port = confparser.get("mfad","pam-port",fallback=None) | ||
282 | database = confparser.get("mfad","database",fallback=None) | ||
283 | |||
284 | # Let command line args overwrite any values | ||
285 | if args.address: | ||
286 | bind_addr = args.address | ||
287 | if args.client_port: | ||
288 | client_port = args.client_port | ||
289 | if args.pam_port: | ||
290 | pam_port = args.pam_port | ||
291 | if args.database: | ||
292 | database = args.database | ||
293 | |||
294 | # Exit if any value is null | ||
295 | if None in [bind_addr,client_port,pam_port,database]: | ||
296 | print("error: one or more items unspecified") | ||
297 | sys.exit(1) | ||
298 | |||
299 | return bind_addr, int(client_port), int(pam_port), database | ||
246 | 300 | ||
247 | 301 | ||
248 | def main(): | 302 | def main(): |
249 | global connection_list | 303 | args = parse_arguments() |
250 | bind_addr = "127.0.0.1" | 304 | confparser = read_config(args.config) |
251 | pam_port = 8000 | 305 | |
252 | client_port = 8001 | 306 | bind_addr, client_port, pam_port, DB_NAME = get_vars(args,confparser) |
253 | 307 | ||
254 | if not os.path.exists(DB_NAME): | 308 | if not os.path.exists(DB_NAME): |
255 | create_db() | 309 | print("Creating DB") |
310 | create_db(DB_NAME) | ||
256 | 311 | ||
257 | clients = threading.Thread(target=listen_client,args=(bind_addr,client_port)) | 312 | clients = threading.Thread(target=listen_client,args=(bind_addr,client_port)) |
258 | pam = threading.Thread(target=listen_pam,args=(bind_addr,pam_port)) | 313 | pam = threading.Thread(target=listen_pam,args=(bind_addr,pam_port)) |