diff options
| author | goodNETnick <pknet@ya.ru> | 2022-05-08 21:01:28 -0400 | 
|---|---|---|
| committer | goodNETnick <pknet@ya.ru> | 2022-05-16 02:59:46 -0400 | 
| commit | ea83ba23b998408f14d7ac8d32c99de23768bb78 (patch) | |
| tree | f871a4b317a0129d0556fe347c841a7712603e51 | |
| parent | 9beeba732c2669024e76928cff12ef95e4f16c78 (diff) | |
| download | vyos-1x-ea83ba23b998408f14d7ac8d32c99de23768bb78.tar.gz vyos-1x-ea83ba23b998408f14d7ac8d32c99de23768bb78.zip | |
ocserv: T4420: show configured 2FA OTP key
| -rw-r--r-- | op-mode-definitions/openconnect.xml.in | 47 | ||||
| -rwxr-xr-x | src/completion/list_openconnect_users.py | 36 | ||||
| -rwxr-xr-x | src/op_mode/show_openconnect_otp.py | 109 | 
3 files changed, 192 insertions, 0 deletions
| diff --git a/op-mode-definitions/openconnect.xml.in b/op-mode-definitions/openconnect.xml.in index 6b0082b4c..9343637c0 100644 --- a/op-mode-definitions/openconnect.xml.in +++ b/op-mode-definitions/openconnect.xml.in @@ -13,6 +13,53 @@              </properties>              <command>${vyos_op_scripts_dir}/openconnect-control.py --action="show_sessions"</command>            </leafNode> +          <tagNode name="user"> +            <properties> +              <help>Show OpenConnect configured user settings</help> +              <completionHelp> +                <script>sudo ${vyos_completion_dir}/list_openconnect_users.py</script> +              </completionHelp> +            </properties> +            <children> +              <node name="otp"> +                <properties> +                  <help>Show OTP key information</help> +                </properties> +                <children> +                  <leafNode name="full"> +                    <properties> +                      <help>Show full settings, including QR code and commands for VyOS</help> +                    </properties> +                    <command>${vyos_op_scripts_dir}/show_openconnect_otp.py --user="$4" --info="full"</command> +                  </leafNode> +                  <leafNode name="key-hex"> +                    <properties> +                      <help>Show OTP authentication secret in Hex (used in VyOS config)</help> +                    </properties> +                    <command>${vyos_op_scripts_dir}/show_openconnect_otp.py --user="$4" --info="key-hex"</command> +                  </leafNode> +                  <leafNode name="key-b32"> +                    <properties> +                      <help>Show OTP authentication secret in Base32 (used in mobile apps)</help> +                    </properties> +                    <command>${vyos_op_scripts_dir}/show_openconnect_otp.py --user="$4" --info="key-b32"</command> +                  </leafNode> +                  <leafNode name="qrcode"> +                    <properties> +                      <help>Show OTP authentication QR code</help> +                    </properties> +                    <command>${vyos_op_scripts_dir}/show_openconnect_otp.py --user="$4" --info="qrcode"</command> +                  </leafNode> +                  <leafNode name="uri"> +                    <properties> +                      <help>Show OTP authentication otpauth URI</help> +                    </properties> +                    <command>${vyos_op_scripts_dir}/show_openconnect_otp.py --user="$4" --info="uri"</command> +                  </leafNode> +                </children> +              </node> +            </children> +          </tagNode>          </children>        </node>      </children> diff --git a/src/completion/list_openconnect_users.py b/src/completion/list_openconnect_users.py new file mode 100755 index 000000000..a266fd893 --- /dev/null +++ b/src/completion/list_openconnect_users.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# +# Copyright (C) 2019-2022 VyOS maintainers and contributors +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2 or later as +# published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program.  If not, see <http://www.gnu.org/licenses/>. + +from vyos.config import Config +from vyos.util import dict_search + +def get_user_from_ocserv(): +    config = Config() +    base = ['vpn', 'openconnect', 'authentication', 'local-users', 'username'] +    openconnect = config.get_config_dict(base, effective=True, key_mangling=('-', '_')) +    users = [] +    try: +        for user in (dict_search('username', openconnect) or []): +            users.append(user) +    except: +        pass +    return users + +if __name__ == "__main__": +    users = [] +    users = get_user_from_ocserv() +    print(" ".join(users)) + diff --git a/src/op_mode/show_openconnect_otp.py b/src/op_mode/show_openconnect_otp.py new file mode 100755 index 000000000..ae532ccc9 --- /dev/null +++ b/src/op_mode/show_openconnect_otp.py @@ -0,0 +1,109 @@ +#!/usr/bin/env python3 + +# Copyright 2017, 2022 VyOS maintainers and contributors <maintainers@vyos.io> +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library.  If not, see <http://www.gnu.org/licenses/>. + +import argparse +import os + +from vyos.config import Config +from vyos.xml import defaults +from vyos.configdict import dict_merge +from vyos.util import popen +from base64 import b32encode + +otp_file = '/run/ocserv/users.oath' + +def check_uname_otp(username): +    """ +    Check if "username" exists and have an OTP key +    """ +    config = Config() +    base_key = ['vpn', 'openconnect', 'authentication', 'local-users', 'username', username, 'otp', 'key'] +    if not config.exists(base_key): +        return None +    return True + +def get_otp_ocserv(username): +    config = Config() +    base = ['vpn', 'openconnect'] +    if not config.exists(base): +        return None +    ocserv = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True) +    # We have gathered the dict representation of the CLI, but there are default +    # options which we need to update into the dictionary retrived. +    default_values = defaults(base) +    ocserv = dict_merge(default_values, ocserv) +    # workaround a "know limitation" - https://phabricator.vyos.net/T2665 +    del ocserv['authentication']['local_users']['username']['otp'] +    if not ocserv["authentication"]["local_users"]["username"]: +        return None +    default_ocserv_usr_values = default_values['authentication']['local_users']['username']['otp'] +    for user, params in ocserv['authentication']['local_users']['username'].items(): +        # Not every configuration requires OTP settings +        if ocserv['authentication']['local_users']['username'][user].get('otp'): +            ocserv['authentication']['local_users']['username'][user]['otp'] = dict_merge(default_ocserv_usr_values, ocserv['authentication']['local_users']['username'][user]['otp']) +    result = ocserv['authentication']['local_users']['username'][username] +    return result + +def display_otp_ocserv(username, params, info): +    hostname = os.uname()[1] +    key_hex = params['otp']['key'] +    otp_length = params['otp']['otp_length'] +    interval = params['otp']['interval'] +    token_type = params['otp']['token_type'] +    if token_type == 'hotp-time': +        token_type_acrn = 'totp' +    key_base32 = b32encode(bytes.fromhex(key_hex)).decode() +    otp_url = ''.join(["otpauth://",token_type_acrn,"/",username,"@",hostname,"?secret=",key_base32,"&digits=",otp_length,"&period=",interval]) +    qrcode,err = popen('qrencode -t ansiutf8', input=otp_url) + +    if info == 'full': +        print("# You can share it with the user, he just needs to scan the QR in his OTP app") +        print("# username: ", username) +        print("# OTP KEY: ", key_base32) +        print("# OTP URL: ", otp_url) +        print(qrcode) +        print('# To add this OTP key to configuration, run the following commands:') +        print(f"set vpn openconnect authentication local-users username {username} otp key '{key_hex}'") +        if interval != "30": +            print(f"set vpn openconnect authentication local-users username {username} otp interval '{interval}'") +        if otp_length != "6": +            print(f"set vpn openconnect authentication local-users username {username} otp otp-length '{otp_length}'") +    elif info == 'key-hex': +        print("# OTP key in hexadecimal: ") +        print(key_hex) +    elif info == 'key-b32': +        print("# OTP key in Base32: ") +        print(key_base32) +    elif info == 'qrcode': +        print(f"# QR code for OpenConnect user '{username}'") +        print(qrcode) +    elif info == 'uri': +        print(f"# URI for OpenConnect user '{username}'") +        print(otp_url) + +if __name__ == '__main__': +    parser = argparse.ArgumentParser(add_help=False, description='Show OTP authentication information for selected user') +    parser.add_argument('--user', action="store", type=str, default='', help='Username') +    parser.add_argument('--info', action="store", type=str, default='full', help='Wich information to display') + +    args = parser.parse_args() +    check_otp = check_uname_otp(args.user) +    if check_otp: +        user_otp_params = get_otp_ocserv(args.user) +        display_otp_ocserv(args.user, user_otp_params, args.info) +    else: +        print(f'There is no such user ("{args.user}") with an OTP key configured') | 
