diff --git a/openssh/config.sls b/openssh/config.sls index dc87341..e462888 100644 --- a/openssh/config.sls +++ b/openssh/config.sls @@ -13,6 +13,14 @@ sshd_config: - watch_in: - service: openssh +ssh_config: + file.managed: + - name: {{ openssh.ssh_config }} + - source: {{ openssh.ssh_config_src }} + - template: jinja + - user: root + - mode: 644 + {% for keyType in ['ecdsa', 'dsa', 'rsa', 'ed25519'] %} {% if salt['pillar.get']('openssh:generate_' ~ keyType ~ '_keys', False) %} ssh_generate_host_{{ keyType }}_key: diff --git a/openssh/defaults.yaml b/openssh/defaults.yaml index 3c1b379..6e9d837 100644 --- a/openssh/defaults.yaml +++ b/openssh/defaults.yaml @@ -1,6 +1,8 @@ openssh: sshd_config: /etc/ssh/sshd_config sshd_config_src: salt://openssh/files/sshd_config + ssh_config: /etc/ssh/ssh_config + ssh_config_src: salt://openssh/files/ssh_config banner: /etc/ssh/banner banner_src: salt://openssh/files/banner ssh_known_hosts: /etc/ssh/ssh_known_hosts diff --git a/openssh/files/ssh_config b/openssh/files/ssh_config new file mode 100644 index 0000000..46f0506 --- /dev/null +++ b/openssh/files/ssh_config @@ -0,0 +1,88 @@ +{%- set ssh_config = pillar.get('ssh_config', {}) -%} +{#- present in ssh_config and known in actual file options -#} +{%- set processed_options = [] -%} + +{#- generic renderer used for ssh matches, known options, -#} +{#- and unknown options -#} +{%- macro render_option(keyword, default, config_dict=ssh_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 ssh_config) }} +{%- endmacro -%} + +{#- macros for render option uncommented by default -#} +{%- macro option_default_uncommented(keyword, default, present) -%} +{{ option_impl(keyword, default, True) }} +{%- endmacro -%} + +# Do not edit this file manually! +# It will be overwritten by salt! + +{{ option_default_uncommented('Host', '*') }} +{{ option(' ForwardAgent', 'no') }} +{{ option(' ForwardX11', 'no') }} +{{ option(' RhostsRSAAuthentication', 'no') }} +{{ option(' RSAAuthentication', 'yes') }} +{{ option(' PasswordAuthentication', 'yes') }} +{{ option(' HostbasedAuthentication', 'no') }} +{{ option(' GSSAPIAuthentication', 'no') }} +{{ option(' GSSAPIDelegateCredentials', 'no') }} +{{ option(' BatchMode', 'no') }} +{{ option(' CheckHostIP', 'yes') }} +{{ option(' AddressFamily', 'any') }} +{{ option(' ConnectTimeout', 0) }} +{{ option(' StrictHostKeyChecking', 'ask') }} +{{ option(' IdentityFile', '~/.ssh/id_rsa') }} +{{ option(' Port', 22) }} +{{ option(' Protocol', 2) }} +{{ option(' Cipher', '3des') }} +{{ option(' Tunnel', 'no') }} +{{ option(' TunnelDevice', 'any:any') }} +{{ option(' PermitLocalCommand', 'no') }} +{{ option(' VisualHostKey', 'no') }} + +{# Handling unknown in salt template options #} +{%- for keyword in ssh_config.keys() %} + {#- Matches have to be at the bottom 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 %} + +{# Handle matches last as they need to go at the bottom #} +{%- if 'matches' in ssh_config %} + {%- for match in ssh_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/files/sshd_config b/openssh/files/sshd_config index c27cbe5..286200b 100644 --- a/openssh/files/sshd_config +++ b/openssh/files/sshd_config @@ -52,7 +52,6 @@ # Use these options to restrict which interfaces/protocols sshd will bind to {{ option('ListenAddress', ['::', '0.0.0.0']) }} {{ option_default_uncommented('Protocol', 2) }} - # HostKeys for protocol version 2 {{ 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']) -}} @@ -69,7 +68,7 @@ # Authentication: {{ option_default_uncommented('LoginGraceTime', 120) }} -{{ option_default_uncommented('PermitRootLogin', 'no') }} +{{ option_default_uncommented('PermitRootLogin', 'yes') }} {{ option_default_uncommented('StrictModes', 'yes') }} {{ option('DSAAuthentication', 'yes') }} @@ -167,7 +166,7 @@ {# Handling unknown in salt template options #} {%- for keyword in sshd_config.keys() %} - {#- Matches have to be at the bottem and should be handled differently -#} + {#- Matches have to be at the bottom 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, '') }} diff --git a/pillar.example b/pillar.example index 80af4a6..4895add 100644 --- a/pillar.example +++ b/pillar.example @@ -50,6 +50,29 @@ sshd_config: Ciphers: 'aes128-ctr,aes256-ctr' MACs: 'hmac-sha1' +ssh_config: + StrictHostKeyChecking: no + ForwardAgent: no + ForwardX11: no + RhostsRSAAuthentication: no + RSAAuthentication: yes + PasswordAuthentication: yes + HostbasedAuthentication: no + GSSAPIAuthentication: no + GSSAPIDelegateCredentials: no + BatchMode: 'yes' + CheckHostIP: 'yes' + AddressFamily: 'any' + ConnectTimeout: 0 + IdentityFile: '~/.ssh/id_rsa' + Port: 22 + Protocol: 2 + Cipher: '3des' + Tunnel: 'no' + TunnelDevice: 'any:any' + PermitLocalCommand: 'no' + VisualHostKey: 'no' + openssh: auth: joe-valid-ssh-key-desktop: