-
Notifications
You must be signed in to change notification settings - Fork 222
feat: add early MFA functionality #188
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| --- | ||
| openvpn_mfa_enabled: false | ||
| openvpn_mfa_issuer: "MFAtest" | ||
| openvpn_mfa_oathtool_package_name: oathtool | ||
| openvpn_mfa_script: oath.sh.j2 | ||
| openvpn_mfa_oathsecrets_file: oath.secrets | ||
| mfa_new_users_map: [] |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| - name: generate userhash for user | ||
| shell: head -10 /dev/urandom | sha256sum | cut -b 1-30 | ||
| register: userhash | ||
|
|
||
| - name: generate secret for mfa authenticator | ||
| shell: oathtool --totp -v "{{ userhash.stdout }}" | grep Base32 | awk '{print $3}' | ||
| register: oathsecret_base32 | ||
|
|
||
| - debug: | ||
| msg: "Send this to the user {{ client_username }}: {{ oathsecret_base32.stdout }}" | ||
|
|
||
| - name: create new var | ||
| set_fact: | ||
| mfa_new_users_map: '{{ mfa_new_users_map | default([]) + [{"username": client_username, "secret": userhash.stdout }] }}' |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| - name: ensure oath secret file exists | ||
| copy: | ||
| content: "" | ||
| dest: "{{ openvpn_base_dir }}/{{ openvpn_mfa_oathsecrets_file }}" | ||
| force: no | ||
| owner: "{{ openvpn_service_user }}" | ||
| mode: 0700 | ||
|
|
||
| - name: fetch clients from oath secrets file | ||
| read_csv: | ||
| path: "{{ openvpn_base_dir }}/{{ openvpn_mfa_oathsecrets_file }}" | ||
| dialect: unix | ||
| fieldnames: username,secret | ||
| delimiter: ':' | ||
| register: oath_secrets_db | ||
|
|
||
| - name: delete clients in db those are not in clients list | ||
| set_fact: | ||
| cleaned_db: '{{ oath_secrets_db.list | selectattr("username", "in", clients) | list }}' | ||
|
|
||
| - name: check for newly added clients/mfa users | ||
| set_fact: | ||
| mfa_new_users: "{{ mfa_new_users | default([]) + [item] }}" | ||
| when: 'item not in {{ cleaned_db | map(attribute="username") | list }}' | ||
| with_items: "{{ clients }}" | ||
|
|
||
| - name: generate oath secrets entry for new clients | ||
| include_tasks: gen_oath_secret.yml | ||
| loop: "{{ mfa_new_users }}" | ||
| loop_control: | ||
| loop_var: client_username | ||
| when: mfa_new_users is defined | ||
|
|
||
| - name: merge new clients and existing client in db | ||
| set_fact: | ||
| clients_merged: "{{ cleaned_db + mfa_new_users_map }}" | ||
|
|
||
| - name: prepare list of username and secret to be written to secret file | ||
| set_fact: | ||
| username_list: '{{ clients_merged | map(attribute="username") | list }}' | ||
| secret_list: '{{ clients_merged | map(attribute="secret") | list }}' | ||
|
|
||
| - name: write lines to file | ||
| copy: | ||
| content: "{{ username_list | zip(secret_list) | map('join', ':') | join('\n') }}" | ||
| dest: "{{ openvpn_base_dir }}/{{ openvpn_mfa_oathsecrets_file }}" | ||
| owner: "{{ openvpn_service_uesr }}" | ||
| mode: 0700 | ||
|
Comment on lines
+38
to
+48
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should this be a template that loops over |
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| #!/bin/sh | ||
| # | ||
| # Sample script to verify MFA using oath-tool | ||
|
|
||
| passfile=$1 | ||
|
|
||
| # Get the user/pass from the tmp file | ||
| user=$(head -1 $passfile) | ||
| pass=$(tail -1 $passfile) | ||
|
|
||
| # Find the entry in our oath.secrets file, ignore case | ||
| secret=$(grep -i -m 1 "$user:" {{ openvpn_mfa_oathsecrets_file }} | cut -d: -f2) | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should the path have |
||
|
|
||
| # Calculate the code we should expect | ||
| code=$(oathtool --totp $secret) | ||
|
|
||
| if [ "$code" = "$pass" ]; | ||
|
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. $pass should be validated to be all numeric, 6 digits |
||
| then | ||
| exit 0 | ||
| fi | ||
|
Comment on lines
+12
to
+20
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is safe-ish. Only user gets interpolated and that has some character sanitization:
I'd much prefer this to be in a script to keep the values as values instead of potentially executing them. |
||
|
|
||
| # See if we have password and MFA, or just MFA | ||
|
|
||
| echo "$pass" | grep -q -i : | ||
|
|
||
| if [ $? -eq 0 ]; | ||
| then | ||
| realpass=$(echo "$pass" | cut -d: -f1) | ||
| mfatoken=$(echo "$pass" | cut -d: -f2) | ||
|
|
||
| # put code here to verify $realpass, the code below the if validates $mfatoken or $pass if false | ||
| # exit 0 if the password is correct, the exit below will deny access otherwise | ||
| fi | ||
|
|
||
|
Comment on lines
+21
to
+34
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this used at all? |
||
| # If we make it here, auth hasn't succeeded, don't grant access | ||
| exit 1 | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please prefix this with
openvpn_mfa_