diff --git a/README.rst b/README.rst index d41cb55..d33b9ab 100644 --- a/README.rst +++ b/README.rst @@ -5,7 +5,7 @@ Install and configure an openssh server. .. note:: See the full `Salt Formulas installation and usage instructions - `_. + `_. Available states ================ @@ -41,3 +41,42 @@ Installs the ssh daemon configuration file included in this formula by values from pillar. ``pillar.example`` results in the generation of the default ``sshd_config`` file on Debian Wheezy. +``openssh.known_hosts`` +----------------------- + +Manages the site-wide ssh_known_hosts file and fills it with the +public SSH host keys of all minions. You can restrict the set of minions +whose keys are listed by using the pillar data ``openssh:known_hosts:target`` +and ``openssh:known_hosts:expr_form`` (those fields map directly to the +corresponding attributes of the ``mine.get`` function). + +The Salt mine is used to share the public SSH host keys, you must thus +configure it accordingly on all hosts that must export their keys. Two +mine functions are required, one that exports the keys (one key per line, +as they are stored in ``/etc/ssh/ssh_host_*_key.pub``) and one that defines +the public hostname that the keys are associated to. Here's the way to +setup those functions through pillar:: + + # Required for openssh.known_hosts + mine_functions: + public_ssh_host_keys: + mine_function: cmd.run + cmd: cat /etc/ssh/ssh_host_*_key.pub + public_ssh_hostname: + mine_function: grains.get + key: id + +The above example assumes that the minion identifier is a valid DNS name +that can be used to connect to the host. If that's not the case, you might +want to use the ``fqdn`` grain instead of the ``id`` one. The above example +also uses the default mine function names used by this formula. If you have to +use other names, then you should indicate the names to use in pillar keys +``openssh:known_hosts:mine_keys_function`` and +``openssh:known_hosts:mine_hostname_function``. + +You can also integrate alternate DNS names of the various hosts in the +ssh_known_hosts files. You just have to list all the alternate DNS names as a +list in the ``openssh:known_hosts:aliases`` pillar key. Whenever the IPv4 or +IPv6 behind one of those DNS entries matches an IPv4 or IPv6 behind the +official hostname of a minion, the alternate DNS name will be associated to the +minion's public SSH host key. diff --git a/openssh/auth.sls b/openssh/auth.sls index f7690b3..8825991 100644 --- a/openssh/auth.sls +++ b/openssh/auth.sls @@ -1,43 +1,49 @@ +{%- macro print_name(identifier, key) -%} +{%- if 'name' in key %} +{{ key['name'] }}: +{%- else %} +{{ identifier }}: +{%- endif %} +{%- endmacro -%} + +{%- macro print_ssh_auth(identifier, key) -%} + {%- if 'user' in key %} + - user: {{ key['user'] }} + {%- else %} + - user: {{ identifier }} + {%- endif %} + {%- if 'present' in key and key['present'] and 'source' in key %} + - source: {{ key['source'] }} + {%- else %} + {%- if 'enc' in key %} + - enc: {{ key['enc'] }} + {%- endif -%} + {%- if 'comment' in key %} + - comment: {{ key['comment'] }} + {%- endif -%} + {%- if 'options' in key %} + - options: {{ key['options'] }} + {%- endif -%} + {%- endif %} +{%- endmacro -%} + include: - openssh - -{% from "openssh/map.jinja" import openssh with context %} -{% set openssh_pillar = pillar.get('openssh', {}) %} -{% set auth = openssh_pillar.get('auth', {}) %} -{% for user,keys in auth.items() -%} - {% for key in keys -%} +{%- from "openssh/map.jinja" import openssh with context -%} +{%- set openssh_pillar = pillar.get('openssh', {}) -%} +{%- set auth = openssh_pillar.get('auth', {}) -%} +{%- for identifier,keys in auth.items() -%} + {%- for key in keys -%} {% if 'present' in key and key['present'] %} -{{ key['name'] }}: +{{ print_name(identifier, key) }}: ssh_auth.present: - - user: {{ user }} - {% if 'source' in key %} - - source: {{ key['source'] }} - {% else %} - {% if 'enc' in key %} - - enc: {{ key['enc'] }} - {% endif %} - {% if 'comment' in key %} - - comment: {{ key['comment'] }} - {% endif %} - {% if 'options' in key %} - - options: {{ key['options'] }} - {% endif %} - {% endif %} + {{ print_ssh_auth(identifier, key) }} - require: - service: {{ openssh.service }} - {% else %} -{{ key['name'] }}: + {%- else %} +{{ print_name(identifier, key) }}: ssh_auth.absent: - - user: {{ user }} - {% if 'enc' in key %} - - enc: {{ key['enc'] }} - {% endif %} - {% if 'comment' in key %} - - comment: {{ key['comment'] }} - {% endif %} - {% if 'options' in key %} - - options: {{ key['options'] }} - {% endif %} - {% endif %} - {% endfor %} -{% endfor %} + {{ print_ssh_auth(identifier, key) }} + {%- endif -%} + {%- endfor -%} +{%- endfor -%} diff --git a/openssh/config.sls b/openssh/config.sls index b16ab28..13c3e96 100644 --- a/openssh/config.sls +++ b/openssh/config.sls @@ -9,47 +9,35 @@ sshd_config: - source: {{ openssh.sshd_config_src }} - template: jinja - user: root - - group: root - mode: 644 - watch_in: - service: openssh -{% if salt['pillar.get']('openssh:provide_dsa_keys', False) %} -ssh_host_dsa_key: +{% for keyType in ['ecdsa', 'dsa', 'rsa', 'ed25519'] %} +{% if salt['pillar.get']('openssh:generate_' ~ keyType ~ '_keys', False) %} +ssh_generate_host_{{ keyType }}_key: + cmd.run: + - name: ssh-keygen -t {{ keyType }} -N '' -f /etc/ssh/ssh_host_{{ keyType }}_key + - creates: /etc/ssh/ssh_host_{{ keyType }}_key + - user: root + +{% elif salt['pillar.get']('openssh:provide_' ~ keyType ~ '_keys', False) %} +ssh_host_{{ keyType }}_key: file.managed: - - name: /etc/ssh/ssh_host_dsa_key - - contents_pillar: 'openssh:dsa:private_key' + - name: /etc/ssh/ssh_host_{{ keyType }}_key + - contents_pillar: 'openssh:{{ keyType }}:private_key' - user: root - mode: 600 - require_in: - service: {{ openssh.service }} -ssh_host_dsa_key.pub: +ssh_host_{{ keyType }}_key.pub: file.managed: - - name: /etc/ssh/ssh_host_dsa_key.pub - - contents_pillar: 'openssh:dsa:public_key' - - user: root - - mode: 600 - - require_in: - - service: {{ openssh.service }} -{% endif %} - -{% if salt['pillar.get']('openssh:provide_rsa_keys', False) %} -ssh_host_rsa_key: - file.managed: - - name: /etc/ssh/ssh_host_rsa_key - - contents_pillar: 'openssh:rsa:private_key' - - user: root - - mode: 600 - - require_in: - - service: {{ openssh.service }} - -ssh_host_rsa_key.pub: - file.managed: - - name: /etc/ssh/ssh_host_rsa_key.pub - - contents_pillar: 'openssh:rsa:public_key' + - name: /etc/ssh/ssh_host_{{ keyType }}_key.pub + - contents_pillar: 'openssh:{{ keyType }}:public_key' - user: root - mode: 600 - require_in: - service: {{ openssh.service }} {% endif %} +{% endfor %} diff --git a/openssh/files/ssh_known_hosts b/openssh/files/ssh_known_hosts new file mode 100644 index 0000000..62849d4 --- /dev/null +++ b/openssh/files/ssh_known_hosts @@ -0,0 +1,34 @@ +{%- set target = salt['pillar.get']('openssh:known_hosts:target', '*') -%} +{%- set expr_form = salt['pillar.get']('openssh:known_hosts:expr_form', 'glob') -%} +{%- set keys_function = salt['pillar.get']('openssh:known_hosts:mine_keys_function', 'public_ssh_host_keys') -%} +{%- set hostname_function = salt['pillar.get']('openssh:known_hosts:mine_hostname_function', 'public_ssh_hostname') -%} +{#- Lookup IP of all aliases so that when we have a matching IP, we inject the alias name + in the SSH known_hosts entry -#} +{%- set aliases = salt['pillar.get']('openssh:known_hosts:aliases', []) -%} +{%- set aliases_ips = {} -%} +{%- for alias in aliases -%} + {%- for ip in salt['dig.A'](alias) + salt['dig.AAAA'](alias) -%} + {%- do aliases_ips.setdefault(ip, []).append(alias) -%} + {%- endfor -%} +{%- endfor -%} +{#- Loop over targetted minions -#} +{%- set host_keys = salt['mine.get'](target, keys_function, expr_form=expr_form) -%} +{%- set host_names = salt['mine.get'](target, hostname_function, expr_form=expr_form) -%} +{%- for host, keys in host_keys|dictsort -%} + {%- set ip4 = salt['dig.A'](host) -%} + {%- set ip6 = salt['dig.AAAA'](host) -%} + {%- set names = [host_names.get(host, host)] -%} + {%- for ip in ip4 + ip6 -%} + {%- do names.append(ip) -%} + {%- for alias in aliases_ips.get(ip, []) -%} + {%- if alias not in names -%} + {%- do names.append(alias) -%} + {%- endif -%} + {%- endfor -%} + {%- endfor -%} + {%- for line in keys.split('\n') -%} + {%- if line -%} +{{ ','.join(names) }} {{ line }} +{% endif -%} + {%- endfor -%} +{%- endfor -%} diff --git a/openssh/files/sshd_config b/openssh/files/sshd_config index f26f29d..f0be13a 100644 --- a/openssh/files/sshd_config +++ b/openssh/files/sshd_config @@ -1,97 +1,127 @@ -{% set sshd_config = pillar.get('sshd_config', {}) %} +{%- set sshd_config = pillar.get('sshd_config', {}) -%} +{#- present in sshd_config and known in actual file options -#} +{%- set processed_options = [] -%} + +{#- generic renderer used for sshd matches, known options, -#} +{#- and unknown options -#} +{%- macro render_option(keyword, default, config_dict=sshd_config) -%} + {%- set value = config_dict.get(keyword, default) -%} + {%- if value is sameas true -%} +{{ keyword }} yes + {%- elif value is sameas false -%} +{{ keyword }} no + {%- elif value is string or value is number -%} +{{ keyword }} {{ value }} + {%- else -%} +{%- for single_value in value -%} +{{ keyword }} {{ single_value }} +{% endfor -%} + {%- endif -%} +{%- endmacro -%} + +{#- macros for render option according to present -#} +{%- macro option_impl(keyword, default, present) -%} + {%- if present -%} + {%- do processed_options.append(keyword) -%} + {%- set prefix='' -%} + {%- else -%} + {%- set prefix='#' -%} + {%- endif -%} + {#- add prefix to keyword -#} + {%- set keyword = prefix ~ keyword -%} +{{ render_option(keyword, default) }} +{%- endmacro -%} + +{#- macros for render option commented by default -#} +{%- macro option(keyword, default, present) -%} +{{ option_impl(keyword, default, keyword in sshd_config) }} +{%- endmacro -%} + +{#- macros for render option uncommented by default -#} +{%- macro option_default_uncommented(keyword, default, present) -%} +{{ option_impl(keyword, default, True) }} +{%- endmacro -%} # This file is managed by salt. Manual changes risk being overwritten. # The contents of the original sshd_config are kept on the bottom for # quick reference. # See the sshd_config(5) manpage for details -{% for keyword, argument in sshd_config.iteritems() %} - {%- if argument is sameas true %} -{{ keyword }} yes - {%- elif argument is sameas false %} -{{ keyword }} no - {%- elif argument is string or argument is number %} -{{ keyword }} {{ argument }} - {%- else %} - {%- for item in argument %} -{{ keyword }} {{ item }} - {%- endfor %} - {%- endif %} -{%- endfor %} - # What ports, IPs and protocols we listen for -#Port 22 +{{ option('Port', 22) }} # Use these options to restrict which interfaces/protocols sshd will bind to -#ListenAddress :: -#ListenAddress 0.0.0.0 -#Protocol 2 +{{ option('ListenAddress', ['::', '0.0.0.0']) }} +{{ option_default_uncommented('Protocol', 2) }} + # HostKeys for protocol version 2 -#HostKey /etc/ssh/ssh_host_rsa_key -#HostKey /etc/ssh/ssh_host_dsa_key -#HostKey /etc/ssh/ssh_host_ecdsa_key +{{ option_default_uncommented('HostKey', ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_dsa_key', '/etc/ssh/ssh_host_ecdsa_key', '/etc/ssh/ssh_host_ed25519_key']) -}} + #Privilege Separation is turned on for security -#UsePrivilegeSeparation yes +{{ option_default_uncommented('UsePrivilegeSeparation', 'yes') }} # Lifetime and size of ephemeral version 1 server key -#KeyRegenerationInterval 3600 -#ServerKeyBits 768 +{{ option_default_uncommented('KeyRegenerationInterval', 3600) }} +{{ option_default_uncommented('ServerKeyBits', 768) }} # Logging -#SyslogFacility AUTH -#LogLevel INFO +{{ option_default_uncommented('SyslogFacility', 'AUTH') }} +{{ option_default_uncommented('LogLevel', 'INFO') }} # Authentication: -#LoginGraceTime 120 -#PermitRootLogin yes -#StrictModes yes +{{ option_default_uncommented('LoginGraceTime', 120) }} +{{ option_default_uncommented('PermitRootLogin', 'no') }} +{{ option_default_uncommented('StrictModes', 'yes') }} -#RSAAuthentication yes -#PubkeyAuthentication yes -#AuthorizedKeysFile %h/.ssh/authorized_keys +{{ option('DSAAuthentication', 'yes') }} +{{ option_default_uncommented('RSAAuthentication', 'yes') }} +{{ option_default_uncommented('PubkeyAuthentication', 'yes') }} +{{ option('AuthorizedKeysFile', '%h/.ssh/authorized_keys') }} # Don't read the user's ~/.rhosts and ~/.shosts files -#IgnoreRhosts yes +{{ option_default_uncommented('IgnoreRhosts', 'yes') }} # For this to work you will also need host keys in /etc/ssh_known_hosts -#RhostsRSAAuthentication no +{{ option_default_uncommented('RhostsRSAAuthentication', 'no') }} # similar for protocol version 2 -#HostbasedAuthentication no +{{ option_default_uncommented('HostbasedAuthentication', 'no') }} # Uncomment if you don't trust ~/.ssh/known_hosts for RhostsRSAAuthentication -#IgnoreUserKnownHosts yes +{{ option('IgnoreUserKnownHosts', 'yes') }} # To enable empty passwords, change to yes (NOT RECOMMENDED) -#PermitEmptyPasswords no +{{ option_default_uncommented('PermitEmptyPasswords', 'no') }} # Change to yes to enable challenge-response passwords (beware issues with # some PAM modules and threads) -#ChallengeResponseAuthentication no +{{ option_default_uncommented('ChallengeResponseAuthentication', 'no') }} +{{ option('AuthenticationMethods', 'publickey,keyboard-interactive') }} # Change to no to disable tunnelled clear text passwords -#PasswordAuthentication yes +{{ option('PasswordAuthentication', 'yes') }} # Kerberos options -#KerberosAuthentication no -#KerberosGetAFSToken no -#KerberosOrLocalPasswd yes -#KerberosTicketCleanup yes +{{ option('KerberosAuthentication', 'no') }} +{{ option('KerberosGetAFSToken', 'no') }} +{{ option('KerberosOrLocalPasswd', 'yes') }} +{{ option('KerberosTicketCleanup', 'yes') }} # GSSAPI options -#GSSAPIAuthentication no -#GSSAPICleanupCredentials yes +{{ option('GSSAPIAuthentication', 'no') }} +{{ option('GSSAPICleanupCredentials', 'yes') }} -#X11Forwarding yes -#X11DisplayOffset 10 -#PrintMotd no -#PrintLastLog yes -#TCPKeepAlive yes -#UseLogin no +{{ option_default_uncommented('X11Forwarding', 'yes') }} +{{ option('AllowTcpForwarding', 'yes') }} +{{ option_default_uncommented('X11DisplayOffset', '10') }} +{{ option_default_uncommented('PrintMotd', 'no') }} +{{ option_default_uncommented('PrintLastLog', 'yes') }} +{{ option_default_uncommented('TCPKeepAlive', 'yes') }} +{{ option('UseLogin', 'no') }} -#MaxStartups 10:30:60 -#Banner /etc/issue.net +{{ option('MaxStartups', '10:30:60') }} +{{ option('Banner', '/etc/issue.net') }} # Allow client to pass locale environment variables -#AcceptEnv LANG LC_* +{{ option_default_uncommented('AcceptEnv', 'LANG LC_*') }} -#Subsystem sftp /usr/lib/openssh/sftp-server +{{ option_default_uncommented('Subsystem', 'sftp /usr/lib/openssh/sftp-server') }} # Set this to 'yes' to enable PAM authentication, account processing, # and session processing. If this is enabled, PAM authentication will @@ -102,42 +132,45 @@ # 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 +{{ option_default_uncommented('UsePAM', 'yes') }} -#AllowAgentForwarding yes -#AllowTcpForwarding yes -#GatewayPorts no -X11Forwarding yes -#X11DisplayOffset 10 -#X11UseLocalhost yes -PrintMotd no # pam does that -#PrintLastLog yes -#TCPKeepAlive yes -#UseLogin no -{% if grains['os_family'] == 'RedHat' %} -UsePrivilegeSeparation yes # RedHat/Centos 6.4 and earlier currently ship 5.3 (sandbox introduced in OpenSSH 5.9) -{% else %} -UsePrivilegeSeparation sandbox # Default for new installations. -{% endif %} -#PermitUserEnvironment no -#Compression delayed -#ClientAliveInterval 0 -#ClientAliveCountMax 3 -#UseDNS yes -#PidFile /run/sshd.pid -#MaxStartups 10:30:100 -#PermitTunnel no -#ChrootDirectory none -#VersionAddendum none +# DNS resolve and map remote IP addresses +{{ option('UseDNS', 'yes') }} -# no default banner path -Banner /etc/ssh/banner +# Restricting Users and Hosts +# example: +# AllowUsers vader@10.0.0.1 maul@sproing.evil.com luke +# AllowGroups wheel staff +# +# Keep in mind that using AllowUsers or AllowGroups means that anyone +# not Matching one of the supplied patterns will be denied access by default. +# Also, in order for sshd to allow access based on full or partial hostnames it +# needs to to a DNS lookup +# +# DenyUsers +{{ option('DenyUsers', '') }} +# AllowUsers +{{ option('AllowUsers', '') }} +# DenyGroups +{{ option('DenyGroups', '') }} +# AllowGroups +{{ option('AllowGroups', '') }} -# override default of no subsystems -Subsystem sftp /usr/lib/ssh/sftp-server +{# Handling unknown in salt template options #} +{%- for keyword in sshd_config.keys() %} + {#- Matches have to be at the bottem and should be handled differently -#} + {%- if not keyword in processed_options and keyword != 'matches' -%} +{#- send a blank default as it doesn't matter #} +{{ render_option(keyword, '') }} + {%- endif -%} +{%- endfor %} -# Example of overriding settings on a per-user basis -#Match User anoncvs -# X11Forwarding no -# AllowTcpForwarding no -# ForceCommand cvs server +{# Handle matches last as they need to go at the bottom #} +{%- if 'matches' in sshd_config %} + {%- for match in sshd_config['matches'].values() %} +Match {{ match['type'].keys()[0] }} {{ match['type'].values()[0] }} + {%- for keyword in match['options'].keys() %} + {{ render_option(keyword, '', config_dict=match['options']) }} + {%- endfor %} + {%- endfor %} +{%- endif %} diff --git a/openssh/init.sls b/openssh/init.sls index 6d447fe..3cf9be6 100644 --- a/openssh/init.sls +++ b/openssh/init.sls @@ -1,10 +1,14 @@ {% from "openssh/map.jinja" import openssh with context %} openssh: + {% if openssh.server is defined %} pkg.installed: - name: {{ openssh.server }} + {% endif %} service.running: - enable: True - name: {{ openssh.service }} + {% if openssh.server is defined %} - require: - pkg: {{ openssh.server }} + {% endif %} diff --git a/openssh/known_hosts.sls b/openssh/known_hosts.sls new file mode 100644 index 0000000..8f8d2a8 --- /dev/null +++ b/openssh/known_hosts.sls @@ -0,0 +1,16 @@ +{% from "openssh/map.jinja" import openssh with context %} + +ensure dig is available: + pkg.installed: + - name: {{ openssh.dig_pkg }} + +manage ssh_known_hosts file: + file.managed: + - name: {{ openssh.ssh_known_hosts }} + - source: salt://openssh/files/ssh_known_hosts + - template: jinja + - user: root + - group: root + - mode: 644 + - require: + - pkg: ensure dig is available diff --git a/openssh/map.jinja b/openssh/map.jinja index 0ba5b62..17e9f8d 100644 --- a/openssh/map.jinja +++ b/openssh/map.jinja @@ -1,4 +1,15 @@ {% set openssh = salt['grains.filter_by']({ + 'Arch': { + 'server': 'openssh', + 'client': 'openssh', + 'service': 'sshd.socket', + 'sshd_config': '/etc/ssh/sshd_config', + 'sshd_config_src': 'salt://openssh/files/sshd_config', + 'banner': '/etc/ssh/banner', + 'banner_src': 'salt://openssh/files/banner', + 'dig_pkg': 'dnsutils', + 'ssh_known_hosts': '/etc/ssh/ssh_known_hosts', + }, 'Debian': { 'server': 'openssh-server', 'client': 'openssh-client', @@ -7,6 +18,28 @@ 'sshd_config_src': 'salt://openssh/files/sshd_config', 'banner': '/etc/ssh/banner', 'banner_src': 'salt://openssh/files/banner', + 'dig_pkg': 'dnsutils', + 'ssh_known_hosts': '/etc/ssh/ssh_known_hosts', + }, + 'FreeBSD': { + 'service': 'sshd', + 'sshd_config': '/etc/ssh/sshd_config', + 'sshd_config_src': 'salt://openssh/files/sshd_config', + 'banner': '/etc/ssh/banner', + 'banner_src': 'salt://openssh/files/banner', + 'dig_pkg': 'bind-tools', + 'ssh_known_hosts': '/etc/ssh/ssh_known_hosts', + }, + 'Gentoo': { + 'server': 'net-misc/openssh', + 'client': 'net-misc/openssh', + 'service': 'sshd', + 'sshd_config': '/etc/ssh/sshd_config', + 'sshd_config_src': 'salt://openssh/files/sshd_config', + 'banner': '/etc/ssh/banner', + 'banner_src': 'salt://openssh/files/banner', + 'dig_pkg': 'net-dns/bind-tools', + 'ssh_known_hosts': '/etc/ssh/ssh_known_hosts', }, 'RedHat': { 'server': 'openssh-server', @@ -16,5 +49,18 @@ 'sshd_config_src': 'salt://openssh/files/sshd_config', 'banner': '/etc/ssh/banner', 'banner_src': 'salt://openssh/files/banner', + 'dig_pkg': 'bind-utils', + 'ssh_known_hosts': '/etc/ssh/ssh_known_hosts', }, + 'Suse': { + 'server': 'openssh', + 'client': 'openssh', + 'service': 'sshd', + 'sshd_config': '/etc/ssh/sshd_config', + 'sshd_config_src': 'salt://openssh/files/sshd_config', + 'banner': '/etc/ssh/banner', + 'banner_src': 'salt://openssh/files/banner', + 'dig_pkg': 'bind-utils', + 'ssh_known_hosts': '/etc/ssh/ssh_known_hosts', + }, }, merge=salt['pillar.get']('openssh:lookup')) %} diff --git a/pillar.example b/pillar.example index cd89e7b..720b8c4 100644 --- a/pillar.example +++ b/pillar.example @@ -5,41 +5,68 @@ sshd_config: - /etc/ssh/ssh_host_rsa_key - /etc/ssh/ssh_host_dsa_key - /etc/ssh/ssh_host_ecdsa_key - UsePrivilegeSeparation: yes + - /etc/ssh/ssh_host_ed25519_key + UsePrivilegeSeparation: 'yes' KeyRegenerationInterval: 3600 ServerKeyBits: 768 SyslogFacility: AUTH LogLevel: INFO LoginGraceTime: 120 - PermitRootLogin: yes - StrictModes: yes - RSAAuthentication: yes - PubkeyAuthentication: yes - IgnoreRhosts: yes - RhostsRSAAuthentication: no - HostbasedAuthentication: no - PermitEmptyPasswords: no - ChallengeResponseAuthentication: no - X11Forwarding: yes + PermitRootLogin: 'yes' + PasswordAuthentication: 'no' + StrictModes: 'yes' + RSAAuthentication: 'yes' + PubkeyAuthentication: 'yes' + IgnoreRhosts: 'yes' + RhostsRSAAuthentication: 'no' + HostbasedAuthentication: 'no' + PermitEmptyPasswords: 'no' + ChallengeResponseAuthentication: 'no' + AuthenticationMethods 'publickey,keyboard-interactive' + X11Forwarding: 'yes' X11DisplayOffset: 10 - PrintMotd: no - PrintLastLog: yes - TCPKeepAlive: yes + PrintMotd: 'no' + PrintLastLog: 'yes' + TCPKeepAlive: 'yes' AcceptEnv: "LANG LC_*" Subsystem: "sftp /usr/lib/openssh/sftp-server" - UsePAM: yes + UsePAM: 'yes' + UseDNS: 'yes' + AllowUsers: 'vader@10.0.0.1 maul@evil.com sidious luke' + DenyUsers: 'yoda chewbaca@112.10.21.1' + AllowGroups: 'wheel staff imperial' + DenyGroups: 'rebel' + Deny + matches: + sftp_chroot: + type: + Group: sftpusers + options: + ChrootDirectory: /sftp-chroot/%u + X11Forwarding: no + AllowTcpForwarding: no + ForceCommand: internal-sftp openssh: auth: - joe: - - name: JOE_VALID_SSH_PUBLIC_KEY + joe-valid-ssh-key-desktop: + - user: joe present: True enc: ssh-rsa - comment: main key - - name: JOE_NON_VALID_SSH_PUBLIC_KEY + comment: main key - desktop + joe-valid-ssh-key-notebook: + - user: joe + present: True + enc: ssh-rsa + comment: main key - notebook + joe-non-valid-ssh-key: + - user: joe present: False enc: ssh-rsa comment: obsolete key - removed + + generate_dsa_keys: False + provide_dsa_keys: False dsa: private_key: | -----BEGIN DSA PRIVATE KEY----- @@ -47,7 +74,18 @@ openssh: -----END DSA PRIVATE KEY----- public_key: | ssh-dss NOT_DEFINED - provide_dsa_keys: False + + generate_ecdsa_keys: False + provide_ecdsa_keys: False + ecdsa: + private_key: | + -----BEGIN EC PRIVATE KEY----- + NOT_DEFINED + -----END EC PRIVATE KEY----- + public_key: | + ecdsa-sha2-nistp256 NOT_DEFINED + + generate_rsa_keys: False provide_rsa_keys: False rsa: private_key: | @@ -56,3 +94,37 @@ openssh: -----END RSA PRIVATE KEY----- public_key: | ssh-rsa NOT_DEFINED + + generate_ed25519_keys: False + provide_ed25519_keys: False + ed25519: + private_key: | + -----BEGIN OPENSSH PRIVATE KEY----- + NOT_DEFINED + -----END OPENSSH PRIVATE KEY----- + public_key: | + ssh-ed25519 NOT_DEFINED + + known_hosts: + # The next 2 settings restrict the set of minions that will be added in + # the generated ssh_known_hosts files (the default is to match all minions) + target: '*' + expr_form: 'glob' + # Name of mining functions used to gather public keys and hostnames + # (the default values are shown here) + mine_keys_function: public_ssh_host_keys + mine_hostname_function: public_ssh_hostname + # List of DNS entries also pointing to our managed machines and that we want + # to inject in our generated ssh_known_hosts file + aliases: + - cname-to-minion.example.org + - alias.example.org + +# Required for openssh.known_hosts +mine_functions: + public_ssh_host_keys: + mine_function: cmd.run + cmd: cat /etc/ssh/ssh_host_*_key.pub + public_ssh_hostname: + mine_function: grains.get + key: id