Skip to content

Commit

Permalink
Merge pull request #1452 from stdweird/gombasg-ssh_metaconfig
Browse files Browse the repository at this point in the history
ncm-metaconfig: ssh daemon configuration support
  • Loading branch information
jrha authored Oct 4, 2023
2 parents 51568e3 + 41d25cb commit ca060d2
Show file tree
Hide file tree
Showing 15 changed files with 577 additions and 79 deletions.
2 changes: 1 addition & 1 deletion ncm-metaconfig/src/main/metaconfig/ssh/client.tt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
[% INCLUDE metaconfig/ssh/client_attrs.tt data=main -%]

[% FOREACH mt IN Match -%]
Match [% mt.matches.join(' ') %]
[% INCLUDE metaconfig/ssh/match.tt %]
[% INCLUDE metaconfig/ssh/client_attrs.tt data=mt FILTER indent %]
[% END -%]

Expand Down
38 changes: 18 additions & 20 deletions ncm-metaconfig/src/main/metaconfig/ssh/client_attrs.tt
Original file line number Diff line number Diff line change
@@ -1,30 +1,28 @@
[% spacelist = ['SendEnv', 'GlobalKnownHostsFile', 'IgnoreUnknown', 'UserKnownHostsFile' ] -%]
[% commalist = ['Ciphers', 'HostbasedKeyTypes','HostKeyAlgorithms', 'KbdInteractiveDevices', 'KbdInteractiveDevices',
'MACs', 'PreferredAuthentications', 'CanonicalDomains', 'CanonicalizePermittedCNAMEs', 'KexAlgorithms',
] -%]
[% multilinelist = ['IdentityFile', 'RevokedHostKeys'] -%]
[% booleans = ['BatchMode', 'CanonicalizeFallbackLocal', 'ChallengeResponseAuthentication', 'CheckHostIP',
'ClearAllForwardings', 'Compression', 'EnableSSHKeysign', 'ExitOnForwardFailure', 'ForwardAgent', 'ForwardX11',
'ForwardX11Trusted', 'GatewayPorts', 'GSSAPIAuthentication', 'GSSAPIDelegateCredentials', 'HashKnownHosts',
'HostbasedAuthentication', 'IdentitiesOnly', 'KbdInteractiveAuthentication', 'NoHostAuthenticationForLocalhost',
'PasswordAuthentication', 'PermitLocalCommand', 'ProxyUseFdpass', 'PubkeyAuthentication', 'RhostsRSAAuthentication',
'RSAAuthentication', 'StreamLocalBindUnlink', 'TCPKeepAlive', 'UsePrivilegedPort', 'VisualHostKey',
] -%]

[% # different forms of list handling, default for list type is comma separated
spacelist = ['SendEnv', 'GlobalKnownHostsFile', 'IgnoreUnknown', 'Include', 'UserKnownHostsFile' ];
multilinelist = ['CertificateFile', 'IdentityFile', 'RevokedHostKeys'];
-%]
[%- FOREACH pair IN data.pairs -%]
[% NEXT IF pair.key == 'hostnames' || pair.key == 'matches' -%]
[% SWITCH pair.key -%]
[% CASE booleans -%]
[% pair.key %] [% pair.value ? 'Yes' : 'No' %]
[% CASE ['hostnames', 'matches'] %][% # Do nothing -%]
[% CASE spacelist -%]
[% pair.key %] [% pair.value.join(' ') %]
[% CASE commalist -%]
[% pair.key %] [% pair.value.join(',') %]
[% CASE multilinelist -%]
[% FOREACH line IN pair.value -%]
[% pair.key %] [% line %]
[% END -%]
[% CASE 'SetEnv' -%]
[% FOREACH item IN pair.value.pairs -%]
SetEnv [% item.key %]="[% item.value %]"
[% END -%]
[% CASE -%]
[% pair.key %] [% pair.value %]
[% END -%]
[% pair.key %] [% -%]
[% IF pair.value.is_boolean -%]
[% pair.value ? 'yes' : 'no' -%]
[% ELSIF CCM.is_list(pair.value) -%]
[% pair.value.join(',') -%]
[% ELSE -%]
[% pair.value -%]
[% END -%]
[% END %]
[% END -%]
8 changes: 8 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/match.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Match[% -%]
[% FOREACH pair IN mt.criteria.pairs -%]
[% IF pair.value.is_boolean -%]
[% pair.key -%]
[% ELSE -%]
[% pair.key %] [% CCM.is_list(pair.value) ? pair.value.join(',') : pair.value -%]
[% END -%]
[% END -%]
190 changes: 179 additions & 11 deletions ncm-metaconfig/src/main/metaconfig/ssh/pan/schema.pan
Original file line number Diff line number Diff line change
Expand Up @@ -5,29 +5,43 @@ include 'pan/types';
# rename these types to prevent conflicts
# we will remove these in an upcoming pr after template-library-core
# has been updated with the new types from ncm-ssh
type temp_ssh_ciphers = string with match (SELF, "^((blowfish|3des|aes128|aes192|aes256|cast128)-cbc" +
"|(aes128|aes192|aes256)-ctr|arcfour|arcfour(128|256)|(aes128-gcm|aes256-gcm|chacha20-poly1305)@openssh.com)$");
type temp_ssh_hostkeyalgorithms = string with match(SELF, "^(ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp(256|384|521)|" +
"(ssh-rsa-cert-v01|ssh-dss-cert-v01|ecdsa-sha2-nistp256-cert-v01|ecdsa-sha2-nistp384-cert-v01|" +
"ecdsa-sha2-nistp521-cert-v01|ssh-rsa-cert-v00|ssh-dss-cert-v00|ssh-ed25519-cert-v01)@openssh.com)$");
type temp_ssh_ciphers = string with match (SELF, "^[+-]?(" +
"(blowfish|3des|aes(128|192|256)|cast128)-cbc" +
"|aes(128|192|256)-ctr|arcfour|arcfour(128|256)" +
"|(aes(128|256)-gcm|chacha20-poly1305)@openssh.com)$");
type temp_ssh_hostkeyalgorithms = string with match(SELF, "^[+-]?(" +
"ssh-(rsa|dss|ed25519)|ecdsa-sha2-nistp(256|384|521)|" +
"(ssh-rsa-cert-v0[01]|ssh-dss-cert-v01|ecdsa-sha2-nistp(256|384|521)-cert-v01|" +
"ssh-dss-cert-v00|ssh-ed25519-cert-v01)@openssh.com)$");
type temp_ssh_kbdinteractivedevices = string with match (SELF, "^(bsdauth|pam|skey)$");
type temp_ssh_kexalgorithms = string with match (SELF, "^(diffie-hellman-group(1-sha1|14-sha1|-exchange-sha1|" +
"-exchange-sha256)|ecdh-sha2-nistp(256|384|521)|[email protected]|gss-gex-sha1-|" +
"gss-group1-sha1-|gss-group14-sha1-)$");
type temp_ssh_MACs = string with match(SELF, "^(hmac-(sha1|sha1-96|sha2-256|sha2-512|md5|md5-96|ripemd160)|" +
# Recent versions have distinct GSSAPIKexAlgorithms
type temp_ssh_gss_kexalgorithms = string with match (SELF, "^[+-]?(gss-(gex|group1|group14)-sha1-" +
"|gss-group14-sha256-|gss-group16-sha512-|gss-nistp256-sha256-|gss-curve25519-sha256-)$");
# Older versions include GSSAPI mechanisms in KEXAlgorithms, but only the SHA1 variants
type temp_ssh_kexalgorithms = string with match (SELF, "^[+-]?(" +
"diffie-hellman-group(1-sha1|14-sha1|-exchange-sha1|-exchange-sha256)" +
"|ecdh-sha2-nistp(256|384|521)|[email protected]" +
"|gss-(gex|group1|group14)-sha1-)$");
type temp_ssh_MACs = string with match(SELF, "^[+-]?(hmac-(sha1|sha1-96|sha2-256|sha2-512|md5|md5-96|ripemd160)|" +
"(hmac-ripemd160|umac-64|umac-128|hmac-sha1-etm|hmac-sha1-96-etm|hmac-sha2-256-etm|hmac-sha2-512-etm|" +
"hmac-md5-etm|hmac-md5-96-etm|hmac-ripemd160-etm|umac-64-etm|umac-128-etm)@openssh.com)$");
type temp_ssh_CAAlgorithms = string with match(SELF, "^[+-]?(" +
"ecdsa-sha2-nistp(256|384|521)|ssh-ed25519|rsa-sha2-(256|512)|ssh-rsa)$");


type ssh_config_opts = {
'AddKeysToAgent' ? string with match (SELF, "^(yes|no|ask|confirm)$")
'AddressFamily' ? string with match (SELF, "^(any|inet|inet6)$")
'BatchMode' ? boolean
'BindAddress' ? string
'BindInterface' ? string
'CanonicalDomains' ? string[]
'CanonicalizeFallbackLocal' ? boolean
'CanonicalizeHostname' ? string with match (SELF, "^(yes|no|always)$")
'CanonicalizeMaxDots' ? long(0..)
'CanonicalizePermittedCNAMEs' ? string[]
'CASignatureAlgorithms' ? temp_ssh_CAAlgorithms[]
'CertificateFile' ? string[]
'ChallengeResponseAuthentication' ? boolean
'CheckHostIP' ? boolean
'Cipher' ? string with match (SELF, "^(blowfish|3des|des)$")
Expand All @@ -52,16 +66,24 @@ type ssh_config_opts = {
'GatewayPorts' ? boolean
'GlobalKnownHostsFile' ? string[]
'GSSAPIAuthentication' ? boolean
'GSSAPIClientIdentity' ? string
'GSSAPIDelegateCredentials' ? boolean
'GSSAPIKeyExchange' ? boolean
'GSSAPIKexAlgorithms' ? temp_ssh_gss_kexalgorithms[]
'GSSAPIRenewalForcesRekey' ? boolean
'GSSAPIServerIdentity' ? string
'GSSAPITrustDns' ? boolean
'HashKnownHosts' ? boolean
'HostbasedAuthentication' ? boolean
'HostbasedKeyTypes' ? string[]
'HostKeyAlgorithms' ? temp_ssh_hostkeyalgorithms[]
'HostKeyAlias' ? string
'HostName' ? string
'IdentitiesOnly' ? boolean
'IdentityAgent' ? string
'IdentityFile' ? string[]
'IgnoreUnknown' ? string[]
'Include' ? string[]
'IPQoS' ? string with match (SELF, "^(af[1234][123]|cs[0-7]|ef|lowdelay|throughput|reliability)$")
'KbdInteractiveAuthentication' ? boolean
'KbdInteractiveDevices' ? temp_ssh_kbdinteractivedevices[]
Expand All @@ -79,9 +101,12 @@ type ssh_config_opts = {
'PreferredAuthentications' ? string[]
'Protocol' ? long(1..2)
'ProxyCommand' ? string
'ProxyJump' ? string[]
'ProxyUseFdpass' ? boolean
'PubkeyAcceptedKeyTypes' ? temp_ssh_hostkeyalgorithms[]
'PubkeyAuthentication' ? boolean
'RekeyLimit' ? string
'RemoteCommand' ? string
'RemoteForward' ? string
'RequestTTY' ? string with match (SELF, "^(yes|no|force|auto)$")
'RevokedHostKeys' ? string[]
Expand All @@ -90,9 +115,11 @@ type ssh_config_opts = {
'SendEnv' ? string[]
'ServerAliveCountMax' ? long(0..)
'ServerAliveInterval' ? long(0..)
'SetEnv' ? string{}
'StreamLocalBindMask' ? string
'StreamLocalBindUnlink' ? boolean
'StrictHostKeyChecking' ? string with match (SELF, "^(yes|no|ask)$")
'SyslogFacility' ? string with match(SELF, "^(DAEMON|USER|AUTH(PRIV)?|LOCAL[0-7])$")
'TCPKeepAlive' ? boolean
'Tunnel' ? string with match (SELF, "^(yes|no|point-to-point|ethernet)$")
'TunnelDevice' ? string
Expand All @@ -108,13 +135,27 @@ type ssh_config_opts = {
type ssh_config_host = {
"hostnames" : string[]
include ssh_config_opts
};

type ssh_config_match_criteria = {
"all" ? boolean with SELF
"canonical" ? boolean with SELF
"final" ? boolean with SELF
"user" ? string[]
"localuser" ? string[]
"host" ? string[]
"originalhost" ? string[]
"exec" ? string
} with {
if (exists(SELF['all']) && length(SELF) > 1) {
error('You can only set all, no other options allowed');
};
true;
};

type ssh_config_match = {
"matches" : string[]
"criteria" : ssh_config_match_criteria with length(SELF) > 0
include ssh_config_opts

};

type ssh_config_file = {
Expand All @@ -123,3 +164,130 @@ type ssh_config_file = {
'main' ? ssh_config_opts
};

# Not all options may appear inside a Match block
type sshd_config_match_opts = {
'AcceptEnv' ? string[]
'AllowAgentForwarding' ? boolean
'AllowGroups' ? string[]
'AllowStreamLocalForwarding' ? string with match (SELF, "^(yes|all|no|local|remote)$")
'AllowTcpForwarding' ? string with match (SELF, "^(yes|all|no|local|remote)$")
'AllowUsers' ? string[]
'AuthenticationMethods' ? string[] # Don't go into details - it does not seem to worth the effort
'AuthorizedKeysCommand' ? absolute_file_path
'AuthorizedKeysCommandUser' ? string
'AuthorizedKeysFile' ? string[]
'AuthorizedPrincipalsCommand' ? absolute_file_path
'AuthorizedPrincipalsCommandUser' ? string
'AuthorizedPrincipalsFile' ? string[]
'Banner' ? string
'ChrootDirectory' ? string
'ClientAliveCountMax' ? long(1..)
'ClientAliveInterval' ? long(0..)
'DenyGroups' ? string[]
'DenyUsers' ? string[]
'ForceCommand' ? string
'GatewayPorts' ? string with match (SELF, "^(yes|no|clientspecified)$")
'GSSAPIAuthentication' ? boolean
'HostbasedAcceptedKeyTypes' ? temp_ssh_hostkeyalgorithms[]
'HostbasedAuthentication' ? boolean
'HostbasedUsesNameFromPacketOnly' ? boolean
'IPQoS' ? string[] with length(SELF) == 1 || length(SELF) == 2
'KbdInteractiveAuthentication' ? boolean
'KerberosAuthentication' ? boolean
'LogLevel' ? string with match (SELF, "^(QUIET|FATAL|ERROR|INFO|VERBOSE|DEBUG[123]?)$")
'MaxAuthTries' ? long(1..)
'MaxSessions' ? long(0..)
'PasswordAuthentication' ? boolean
'PermitEmptyPasswords' ? boolean
'PermitListen' ? string[] # type_hostport would not allow wildcards
'PermitOpen' ? string[] # type_hostport would not allow wildcards
'PermitRootLogin' ? string with match (SELF, "^(yes|prohibit-password|without-password|forced-commands-only|no)$")
'PermitTTY' ? boolean
'PermitTunnel' ? string with match (SELF, "^(yes|point-to-point|ethernet|no)$")
'PermitUserRC' ? boolean
'PubkeyAcceptedKeyTypes' ? temp_ssh_hostkeyalgorithms[]
'PubkeyAuthentication' ? boolean
'RekeyLimit' ? string[] with length(SELF) == 1 || length(SELF) == 2
'RSAAuthentication' ? boolean
'RhostsRSAAuthentication' ? boolean
'RevokedKeys' ? string
'RDomain' ? string
'SetEnv' ? string{}
'StreamLocalBindMask' ? string with match (SELF, "^[0-7]{3,5}$")
'StreamLocalBindUnlink' ? boolean
'TrustedUserCAKeys' ? string
'X11DisplayOffset' ? long(0..)
'X11Forwarding' ? boolean
'X11UseLocalHost' ? boolean
};

type sshd_config_match_criteria = {
"All" ? boolean with SELF
"User" ? string[]
"Group" ? string[]
"Host" ? string[]
"LocalAddress" ? string[]
"LocalPort" ? string[]
"RDomain" ? string[]
"Address" ? string[]
} with {
if (exists(SELF['All']) && length(SELF) > 1) {
error('You can only set All, no other options allowed');
};
true;
};

type sshd_config_match = {
"criteria" : sshd_config_match_criteria with length(SELF) > 0
include sshd_config_match_opts
};

type sshd_config_opts = {
include sshd_config_match_opts
'AddressFamily' ? string with match (SELF, "^(any|inet|inet6)$")
'CASignatureAlgorithms' ? temp_ssh_CAAlgorithms[]
'ChallengeResponseAuthentication' ? boolean
'Ciphers' ? temp_ssh_ciphers[]
'Compression' ? boolean
'DisableForwarding' ? boolean
'ExposeAuthInfo' ? boolean
'FingerprintHash' ? string with match (SELF, "^(md5|sha256)$")
'GSSAPICleanupCredentials' ? boolean
'GSSAPIKeyExchange' ? boolean
'GSSAPIKexAlgorithms' ? temp_ssh_gss_kexalgorithms[]
'GSSAPIStrictAcceptorCheck' ? boolean
'GSSAPIStoreCredentialsOnRekey' ? boolean
'HostCertificate' ? string
'HostKey' ? string[]
'HostKeyAgent' ? string
'HostKeyAlgorithms' ? temp_ssh_hostkeyalgorithms[]
'IgnoreRhosts' ? boolean
'IgnoreUserKnownHosts' ? boolean
'KerberosGetAFSToken' ? boolean
'KerberosOrLocalPasswd' ? boolean
'KerberosTicketCleanup' ? boolean
'KexAlgorithms' ? temp_ssh_kexalgorithms[]
'ListenAddress' ? type_hostport[]
'LoginGraceTime' ? long(0..)
'MACs' ? temp_ssh_MACs[]
'Match' ? sshd_config_match[]
'MaxStartups' ? string with match (SELF, "^[0-9]+(:[0-9]+:[0-9]+)?$")
'PermitUserEnvironment' ? boolean
'PidFile' ? absolute_file_path
'Port' ? long(1..)[]
'PrintLastLog' ? boolean
'PrintMotd' ? boolean
'StrictModes' ? boolean
'Subsystem' ? string{}
'SyslogFacility' ? string with match (SELF, "^(DAEMON|USER|AUTH|LOCAL[0-7])$")
'TCPKeepAlive' ? boolean
'UseDNS' ? boolean
'UsePAM' ? boolean
'VersionAddendum' ? string
'XAuthLocation' ? absolute_file_path
};

type sshd_config_file = {
'Match' ? sshd_config_match[]
'main' ? sshd_config_opts
};
14 changes: 14 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/pan/server_config.pan
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
unique template metaconfig/ssh/server_config;

include 'metaconfig/ssh/schema';

bind "/software/components/metaconfig/services/{/etc/ssh/sshd_config}/contents" = sshd_config_file;

# since final locks the whole path, bind it to a fix value and set it as default too
bind "/software/components/metaconfig/commands/sshd_test_stdin" =
string = "/usr/sbin/sshd -t -f /dev/stdin" with SELF == "/usr/sbin/sshd -t -f /dev/stdin";

prefix "/software/components/metaconfig/services/{/etc/ssh/sshd_config}";
"module" = "ssh/server";
"actions/test" = "sshd_test_stdin";
"daemons/sshd" = "restart";
7 changes: 7 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/server.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

[% INCLUDE metaconfig/ssh/server_attrs.tt data=main -%]

[% FOREACH mt IN Match -%]
[% INCLUDE metaconfig/ssh/match.tt %]
[% INCLUDE metaconfig/ssh/server_attrs.tt data=mt FILTER indent %]
[% END -%]
32 changes: 32 additions & 0 deletions ncm-metaconfig/src/main/metaconfig/ssh/server_attrs.tt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[% # different forms of list handling, default for list type is space separated
commalist = ['Ciphers', 'HostKeyAlgorithms', 'HostbasedAcceptedKeyTypes', 'KexAlgorithms', 'MACs', 'PubkeyAcceptedKeyTypes' ];
multilinelist = ['HostKey', 'ListenAddress', 'Port' ]
-%]
[%- FOREACH pair IN data.pairs -%]
[% SWITCH pair.key -%]
[% CASE 'criteria' %][% # do nothing -%]
[% CASE commalist -%]
[% pair.key %] [% pair.value.join(',') %]
[% CASE multilinelist -%]
[% FOREACH line IN pair.value -%]
[% pair.key %] [% line %]
[% END -%]
[% CASE 'Subsystem' -%]
[% FOREACH item IN pair.value.pairs -%]
Subsystem [% item.key %] [% item.value %]
[% END -%]
[% CASE 'SetEnv' -%]
[% FOREACH item IN pair.value.pairs -%]
SetEnv [% item.key %]="[% item.value %]"
[% END -%]
[% CASE -%]
[% pair.key %] [% -%]
[% IF pair.value.is_boolean -%]
[% pair.value ? 'yes' : 'no' -%]
[% ELSIF CCM.is_list(pair.value) -%]
[% pair.value.join(' ') -%]
[% ELSE -%]
[% pair.value -%]
[% END -%]
[% END %]
[% END -%]
Loading

0 comments on commit ca060d2

Please sign in to comment.