Additional options for supporting duo 2FA solution
https://duo.com/docs/duounix

Change-Id: I0a4284fc148fc31c5e03f45e090726567688c3f4
diff --git a/README.rst b/README.rst
index 8254184..c2d3a3b 100644
--- a/README.rst
+++ b/README.rst
@@ -154,6 +154,37 @@
       server:
         dss_enabled: true
 
+* The OpenSSH server configuration with the duo 2FA
+https://duo.com/docs/duounix
+with Match User 2FA can be bypassed for some accounts
+
+  .. code-block:: yaml
+
+    openssh:
+      server:
+        use_dns: false
+        password_auth: false
+        challenge_response_auth: true
+        ciphers:
+          aes256-ctr:
+            enabled: true
+          aes192-ctr:
+            enabled: true
+          aes128-ctr:
+            enabled: true
+        authentication_methods:
+          publickey:
+            enabled: true
+          keyboard-interactive:
+            enabled: true
+        match_user:
+          jenkins:
+            authentication_methods:
+              publickey:
+                enabled: true
+
+
+
 * OpenSSH server configuration supports AllowUsers, DenyUsers, AllowGroup,
 DenyGroups via allow_users, deny_users, allow_groups, deny_groups keys respectively.
 
diff --git a/openssh/files/sshd_config b/openssh/files/sshd_config
index 7fff6e2..b388384 100755
--- a/openssh/files/sshd_config
+++ b/openssh/files/sshd_config
@@ -111,7 +111,11 @@
 
 # Change to yes to enable challenge-response passwords (beware issues with
 # some PAM modules and threads)
+{%- if server.challenge_response_auth or server.challenge_response_auth == 'yes' %}
+ChallengeResponseAuthentication yes
+{%- else %}
 ChallengeResponseAuthentication no
+{%- endif %}
 
 # Change to no to disable tunnelled clear text passwords
 PasswordAuthentication {{ 'yes' if server.get('password_auth', True) else 'no' }}
@@ -134,8 +138,10 @@
 {%- endif %}
 #GSSAPICleanupCredentials yes
 
-{%- if server.use_dns is defined %}
-UseDNS {{ server.use_dns }}
+{%- if server.use_dns or server.use_dns == 'yes' %}
+UseDNS yes
+{%- else %}
+UseDNS no
 {%- endif %}
 
 X11Forwarding {{ 'yes' if server.get('x11', {}).get('forwarding', True) else 'no' }}
@@ -193,6 +199,7 @@
 # If you just want the PAM account and session checks to run without
 # PAM authentication, then enable this but set PasswordAuthentication
 # and ChallengeResponseAuthentication to 'no'.
+
 UsePAM yes
 
 {%- if server.get('dss_enabled', false) %}
@@ -227,3 +234,33 @@
 {%- endif %}
 {%- endif %}
 
+
+{%- if server.ciphers is defined %}
+{%- set ciphers_list = [] %}
+{%- for k, v in server.ciphers.items() %}
+{%- set _ = ciphers_list.append(k) if v.get('enabled', False) %}
+{%- endfor %}
+Ciphers {{ ciphers_list|join(',') }}
+{%- endif %}
+
+{%- if server.authentication_methods is defined %}
+{%- set auth_methods = [] %}
+{%- for k, v in server.authentication_methods.items() %}
+{%- set _ = auth_methods.append(k) if v.get('enabled', False) %}
+{%- endfor %}
+AuthenticationMethods {{ auth_methods|join(',') }}
+{%- endif %}
+
+{%- if server.match_user is defined %}
+{%- for name, user in server.match_user.iteritems() %}
+Match User {{ name }}
+{%- if user.authentication_methods is defined %}
+{%- set auth_methods = [] %}
+{%- for k, v in user.authentication_methods.items() %}
+{%- set _ = auth_methods.append(k) if v.get('enabled', False) %}
+{%- endfor %}
+  AuthenticationMethods {{ auth_methods|join(',') }}
+{%- endif %}
+{%- endfor %}
+{%- endif %}
+
diff --git a/openssh/map.jinja b/openssh/map.jinja
index 6b1e11b..5cbda1d 100644
--- a/openssh/map.jinja
+++ b/openssh/map.jinja
@@ -5,6 +5,8 @@
         'config': '/etc/ssh/sshd_config',
         'lib_dir': '/usr/lib/openssh',
         'user': {},
+        'use_dns': True,
+        'challenge_response_auth': False,
     },
     'Debian': {
         'pkgs': ['openssh-server'],
@@ -12,6 +14,8 @@
         'config': '/etc/ssh/sshd_config',
         'lib_dir': '/usr/lib/openssh',
         'user': {},
+        'use_dns': True,
+        'challenge_response_auth': False,
     },
     'MacOS': {
         'pkgs': ['openssh'],
@@ -19,6 +23,8 @@
         'config': '/etc/sshd_config',
         'lib_dir': '/usr/lib/openssh',
         'user': {},
+        'use_dns': True,
+        'challenge_response_auth': False,
     },
     'RedHat': {
         'pkgs': ['openssh'],
@@ -26,6 +32,8 @@
         'config': '/etc/ssh/sshd_config',
         'lib_dir': '/usr/libexec/openssh',
         'user': {},
+        'use_dns': True,
+        'challenge_response_auth': False,
     },
 }, merge=salt['pillar.get']('openssh:server')) %}
 
diff --git a/openssh/schemas/server.yaml b/openssh/schemas/server.yaml
index 110782e..c11f851 100644
--- a/openssh/schemas/server.yaml
+++ b/openssh/schemas/server.yaml
@@ -365,6 +365,60 @@
       Specifies whether sshd should look up the remote host name, and to
       check that the resolved host name for the remote IP address maps back to the very same IP address
     type: boolean
+  challenge_response_auth:
+    description: |
+      ChallengeResponseAuthentication controls support for the 'keyboard-interactive' authentication scheme defined in RFC-4256.
+      The 'keyboard-interactive' authentication scheme could, in theory, ask a user any number of multi-facited questions.
+      It's using for duo 2FA authorization.
+    type: boolean
+  ciphers:
+    description: |
+      Symmetric algorithms for encrypting the bulk of transferred data are configured using the Ciphers option.
+      A good value is aes128-ctr,aes192-ctr,aes256-ctr. This should also provide good interoperability.
+    type: object
+    properties:
+      enabled:
+        description: |
+          Enables / disabled specific algorithm.
+        type: boolean
+  authentication_methods:
+    description: |
+      AuthenticationMethods
+      ---------------------
+      Specifies the authentication methods that must be successfully
+      completed for a user to be granted access. This option must be
+      followed by one or more comma-separated lists of authentication
+      method names, or by the single string any to indicate the default
+      behaviour of accepting any single authentication method. If the
+      default is overridden, then successful authentication requires
+      completion of every method in at least one of these lists.
+    type: object
+    properties:
+      enabled:
+        description: |
+          Enables / disabled specific method.
+        type: boolean
+  match_user:
+    description: |
+      Match User can be used to select individual users to alter the PasswordAuthentication directive for.
+      It's usefull for disabling 2FA for specific users.
+    type: object
+    properties:
+      user:
+        description: |
+          List of openssh user's, to be configured.
+          Example 'Match User root,foo,bar'
+        type: object
+        properties:
+          authentication_methods:
+            description: |
+              AuthenticationMethods (see description above) for specific user
+            type: object
+            properties:
+              enabled:
+                description: |
+                  Enables / disabled specific method.
+                type: boolean
 
 
 definitions:
diff --git a/tests/pillar/server.sls b/tests/pillar/server.sls
index bc2b3f0..734717f 100644
--- a/tests/pillar/server.sls
+++ b/tests/pillar/server.sls
@@ -12,7 +12,26 @@
 openssh:
   server:
     enabled: true
-    use_dns: yes
+    use_dns: false
+    password_auth: false
+    challenge_response_auth: true
+    ciphers:
+      aes256-ctr:
+        enabled: true
+      aes192-ctr:
+        enabled: true
+      aes128-ctr:
+        enabled: true
+    authentication_methods:
+      publickey:
+        enabled: true
+      keyboard-interactive:
+        enabled: true
+    match_user:
+      jenkins:
+        authentication_methods:
+          publickey:
+            enabled: true
     syslog_facility: auth
     user:
       testusername: