diff options
| -rw-r--r-- | data/templates/login/pam_otp_ga.conf.j2 | 2 | ||||
| -rw-r--r-- | debian/vyos-1x.postinst | 4 | ||||
| -rw-r--r-- | interface-definitions/system-login.xml.in | 108 | ||||
| -rwxr-xr-x | smoketest/scripts/cli/test_system_login.py | 8 | ||||
| -rwxr-xr-x | src/conf_mode/system-login.py | 24 | 
5 files changed, 68 insertions, 78 deletions
| diff --git a/data/templates/login/pam_otp_ga.conf.j2 b/data/templates/login/pam_otp_ga.conf.j2 index 4c1f411d1..cf51ce089 100644 --- a/data/templates/login/pam_otp_ga.conf.j2 +++ b/data/templates/login/pam_otp_ga.conf.j2 @@ -1,5 +1,5 @@  {% if authentication.otp.key is vyos_defined %} -{{ authentication.otp.key }} +{{ authentication.otp.key | upper }}  " RATE_LIMIT {{ authentication.otp.rate_limit }} {{ authentication.otp.rate_time }}  " WINDOW_SIZE {{ authentication.otp.window_size }}  " DISALLOW_REUSE diff --git a/debian/vyos-1x.postinst b/debian/vyos-1x.postinst index 9766c91b1..031e91595 100644 --- a/debian/vyos-1x.postinst +++ b/debian/vyos-1x.postinst @@ -23,11 +23,11 @@ fi  # Add 2FA support for SSH  sudo grep -qF -- "auth required pam_google_authenticator.so nullok" "/etc/pam.d/sshd" || \ -sudo sed -i '/^@include common-auth/a # Check OTP 2FA, if configured for the user\nauth required pam_google_authenticator.so nullok' /etc/pam.d/sshd +sudo sed -i '/^@include common-auth/a # Check OTP 2FA, if configured for the user\nauth       required     pam_google_authenticator.so nullok' /etc/pam.d/sshd  # Add 2FA support for local authentication  sudo grep -qF -- "auth required pam_google_authenticator.so nullok" "/etc/pam.d/login" || \ -sudo sed -i '/^@include common-auth/a # Check OTP 2FA, if configured for the user\nauth required pam_google_authenticator.so nullok' /etc/pam.d/login +sudo sed -i '/^@include common-auth/a # Check OTP 2FA, if configured for the user\nauth       required     pam_google_authenticator.so nullok' /etc/pam.d/login  # Add RADIUS operator user for RADIUS authenticated users to map to  if ! grep -q '^radius_user' /etc/passwd; then diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 7dd045e6c..def42544a 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -8,62 +8,6 @@            <priority>400</priority>          </properties>          <children> -          <node name="authentication"> -            <properties> -              <help>Global authentication settings</help> -            </properties> -            <children> -              <node name="otp"> -                <properties> -                  <help>2FA OTP authentication parameters</help> -                </properties> -                <children> -                  <leafNode name="rate-limit"> -                    <properties> -                      <help>Number of attempts. Limit logins to N per every M seconds</help> -                      <valueHelp> -                        <format>u32:1-10</format> -                        <description>Number of attempts. Limit logins to N per every M seconds</description> -                      </valueHelp> -                      <constraint> -                        <validator name="numeric" argument="--range 1-10"/> -                      </constraint> -                      <constraintErrorMessage>Number of login attempts must me between 1 and 10</constraintErrorMessage> -                    </properties> -                    <defaultValue>3</defaultValue> -                  </leafNode> -                  <leafNode name="rate-time"> -                    <properties> -                      <help>Time interval. Limit logins to N per every M seconds</help> -                      <valueHelp> -                        <format>u32:15-600</format> -                        <description>Time interval. Limit logins to N per every M seconds</description> -                      </valueHelp> -                      <constraint> -                        <validator name="numeric" argument="--range 15-600"/> -                      </constraint> -                      <constraintErrorMessage>Rate limit time interval must be between 15 and 600 seconds</constraintErrorMessage> -                    </properties> -                    <defaultValue>30</defaultValue> -                  </leafNode> -                  <leafNode name="window-size"> -                    <properties> -                      <help>Set window of concurrently valid codes</help> -                      <valueHelp> -                        <format>u32:1-21</format> -                        <description>Set window of concurrently valid codes</description> -                      </valueHelp> -                      <constraint> -                        <validator name="numeric" argument="--range 1-21"/> -                      </constraint> -                      <constraintErrorMessage>Window of concurrently valid codes must be between 1 and 21</constraintErrorMessage> -                    </properties> -                    <defaultValue>3</defaultValue> -                  </leafNode> -                </children> -              </node> -            </children> -          </node>            <tagNode name="user">              <properties>                <help>Local user account information</help> @@ -75,7 +19,7 @@              <children>                <node name="authentication">                  <properties> -                  <help>Password authentication</help> +                  <help>Authentication settings</help>                  </properties>                  <children>                    <leafNode name="encrypted-password"> @@ -94,18 +38,60 @@                    </leafNode>                    <node name="otp">                      <properties> -                      <help>2FA OTP authentication parameters</help> +                      <help>One-Time-Pad (two-factor) authentication parameters</help>                      </properties>                      <children> +                      <leafNode name="rate-limit"> +                        <properties> +                          <help>Limit number of logins (rate-limit) per rate-time</help> +                          <valueHelp> +                            <format>u32:1-10</format> +                            <description>Number of attempts</description> +                          </valueHelp> +                          <constraint> +                            <validator name="numeric" argument="--range 1-10"/> +                          </constraint> +                          <constraintErrorMessage>Number of login attempts must me between 1 and 10</constraintErrorMessage> +                        </properties> +                        <defaultValue>3</defaultValue> +                      </leafNode> +                      <leafNode name="rate-time"> +                        <properties> +                          <help>Limit number of logins (rate-limit) per rate-time</help> +                          <valueHelp> +                            <format>u32:15-600</format> +                            <description>Time interval</description> +                          </valueHelp> +                          <constraint> +                            <validator name="numeric" argument="--range 15-600"/> +                          </constraint> +                          <constraintErrorMessage>Rate limit time interval must be between 15 and 600 seconds</constraintErrorMessage> +                        </properties> +                        <defaultValue>30</defaultValue> +                      </leafNode> +                      <leafNode name="window-size"> +                        <properties> +                          <help>Set window of concurrently valid codes</help> +                          <valueHelp> +                            <format>u32:1-21</format> +                            <description>Window size</description> +                          </valueHelp> +                          <constraint> +                            <validator name="numeric" argument="--range 1-21"/> +                          </constraint> +                          <constraintErrorMessage>Window of concurrently valid codes must be between 1 and 21</constraintErrorMessage> +                        </properties> +                        <defaultValue>3</defaultValue> +                      </leafNode>                        <leafNode name="key">                          <properties> -                          <help>Token Key Secret key for the token algorithm (see RFC 4226)</help> +                          <help>Key/secret the token algorithm (see RFC4226)</help>                            <valueHelp>                              <format>txt</format> -                            <description>OTP key (base32 encoded secret)</description> +                            <description>Base32 encoded key/token</description>                            </valueHelp>                            <constraint> -                            <regex>[a-zA-Z2-7]{20,10000}</regex> +                            <regex>[a-zA-Z2-7]{26,10000}</regex>                            </constraint>                            <constraintErrorMessage>Key must only include base32 characters and be at least 26 characters long</constraintErrorMessage>                          </properties> diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index a99721d66..6006fe0f6 100755 --- a/smoketest/scripts/cli/test_system_login.py +++ b/smoketest/scripts/cli/test_system_login.py @@ -46,6 +46,14 @@ TTSb0X1zPGxPIRFy5GoGtO9Mm5h4OZk=  """  class TestSystemLogin(VyOSUnitTestSHIM.TestCase): +    @classmethod +    def setUpClass(cls): +        super(TestSystemLogin, cls).setUpClass() + +        # ensure we can also run this test on a live system - so lets clean +        # out the current configuration which will break this test +        cls.cli_delete(cls, base_path + ['radius']) +      def tearDown(self):          # Delete individual users from configuration          for user in users: diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index bd9cc3b89..e26b81e3d 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -253,23 +253,19 @@ def apply(login):                         user_config, permission=0o600,                         formater=lambda _: _.replace(""", '"'),                         user=user, group='users') -                #OTP 2FA key file generation -                if dict_search('authentication.otp.key', user_config): -                    user_config['authentication']['otp']['key'] = user_config['authentication']['otp']['key'].upper() -                    user_config['authentication']['otp']['rate_limit'] = login['authentication']['otp']['rate_limit'] -                    user_config['authentication']['otp']['rate_time'] = login['authentication']['otp']['rate_time'] -                    user_config['authentication']['otp']['window_size'] = login['authentication']['otp']['window_size'] -                    render(f'{home_dir}/.google_authenticator', 'login/pam_otp_ga.conf.j2', -                           user_config, permission=0o600, -                           formater=lambda _: _.replace(""", '"'), -                           user=user, group='users') -                #OTP 2FA key file deletion -                elif os.path.exists(f'{home_dir}/.google_authenticator'): -                    os.remove(f'{home_dir}/.google_authenticator') -		 +              except Exception as e:                  raise ConfigError(f'Adding user "{user}" raised exception: "{e}"') +            # Generate 2FA/MFA One-Time-Pad configuration +            if dict_search('authentication.otp.key', user_config): +                render(f'{home_dir}/.google_authenticator', 'login/pam_otp_ga.conf.j2', +                       user_config, permission=0o400, user=user, group='users') +            else: +                # delete configuration as it's not enabled for the user +                if os.path.exists(f'{home_dir}/.google_authenticator'): +                    os.remove(f'{home_dir}/.google_authenticator') +      if 'rm_users' in login:          for user in login['rm_users']:              try: | 
