From 710175799b9b3db806939927fb4e5e50fcffa3b1 Mon Sep 17 00:00:00 2001 From: Michael Mol Date: Fri, 9 Jun 2017 17:03:29 -0400 Subject: [PATCH 1/2] Support compound matches Support complex compound matches in Match criteria. For example, be able to match against multiple Users for a given Match, or be able to match against address ranges. Or Groups. Or any combination thereof. Support for matching users can take one of several different appearances in pillar data: sshd_config: matches: match_1: type: User: one_user options: ChrootDirectory: /ex/%u match_2: type: User: - jim - bob - sally options: ChrootDirectory: /ex/%u match_3: type: User: jim: ~ bob: ~ sally: ~ options: ChrootDirectory: /ex/%u Note the syntax of match_3. By using empty dicts for each user, we can leverage Salt's pillar mergine. If we use simple lists, we cannot do this; Salt can't merge simple lists, because it doesn't know what order they ought to be in. --- openssh/files/sshd_config | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/openssh/files/sshd_config b/openssh/files/sshd_config index 9a75fe3..d9d44a1 100644 --- a/openssh/files/sshd_config +++ b/openssh/files/sshd_config @@ -61,6 +61,18 @@ {%- endif -%} {%- endmacro -%} +{#- macro for conditionally joining a string, list or dict(keys) to just a string -#} +{%- macro join_to_string(src, keyword, sep=',') -%} +{%- set srcval = src.get(keyword, '') -%} +{%- if srcval is string -%} + {{ srcval }} +{%- elif srcval is mapping -%} + {{ srcval.keys()|sort|join(sep) }} +{%- else -%} + {{ srcval|join(sep) }} +{%- endif -%} +{%- endmacro -%} + {%- if sshd_config.get('ConfigBanner', False) -%} {{ sshd_config['ConfigBanner'] }} {%- else -%} @@ -77,7 +89,7 @@ # What ports, IPs and protocols we listen for {{ option('Port', 22) }} # Use these options to restrict which interfaces/protocols sshd will bind to -{{ option('ListenAddress', ['::', '0.0.0.0']) }} +{{ option('ListenAddress', ['::', '1.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']) -}} @@ -216,7 +228,12 @@ {# 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] }} +Match + {#- Set up the match criteria -#} + {%- for criteria in match['type'].keys()|sort() -%} + {{- ' ' }}{{criteria }} {{ join_to_string(match['type'], criteria) -}} + {%- endfor -%} + {#- Set up the applied options -#} {%- for keyword in match['options'].keys() %} {{ render_option(keyword, '', config_dict=match['options']) }} {%- endfor %} From 6229a6d122670dce9f841ddf6c3f9c8f61f5a928 Mon Sep 17 00:00:00 2001 From: Michael Mol Date: Mon, 12 Jun 2017 12:05:46 -0400 Subject: [PATCH 2/2] Stabily sort matches OpenSSH's Match declarations are applied first-match-wins. However, we can't safely define two Matches that might overlap unless we first sort the keys, as Python (and Jinja) dicts don't guarantee the order of dict keys, We also won't scramble the match sequence every time the user adds, removes or renames a match, and so we give the user clearer, more concise diffs as when they apply changes. Finally, we leave a comment on the Match line identifying where the Match rule came from, to assist in troubleshooting. --- openssh/files/sshd_config | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openssh/files/sshd_config b/openssh/files/sshd_config index d9d44a1..763e261 100644 --- a/openssh/files/sshd_config +++ b/openssh/files/sshd_config @@ -227,12 +227,12 @@ {# Handle matches last as they need to go at the bottom #} {%- if 'matches' in sshd_config %} - {%- for match in sshd_config['matches'].values() %} + {%- for name, match in sshd_config['matches']|dictsort %} Match {#- Set up the match criteria -#} {%- for criteria in match['type'].keys()|sort() -%} {{- ' ' }}{{criteria }} {{ join_to_string(match['type'], criteria) -}} - {%- endfor -%} + {%- endfor %} #{{ name }} {#- Set up the applied options -#} {%- for keyword in match['options'].keys() %} {{ render_option(keyword, '', config_dict=match['options']) }}