From f5988527fb356e4a6726b49b939aa2a094a007a1 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 28 Oct 2022 15:15:02 -0700 Subject: [PATCH] add sshd_conf parser --- CHANGELOG | 3 + jc/lib.py | 1 + jc/parsers/sshd_conf.py | 189 ++++++++++++++++++++++ tests/fixtures/generic/sshd-T-centos7.out | 104 ++++++++++++ tests/fixtures/generic/sshd-T.out | 92 +++++++++++ tests/fixtures/generic/sshd_config | 139 ++++++++++++++++ 6 files changed, 528 insertions(+) create mode 100644 jc/parsers/sshd_conf.py create mode 100644 tests/fixtures/generic/sshd-T-centos7.out create mode 100644 tests/fixtures/generic/sshd-T.out create mode 100644 tests/fixtures/generic/sshd_config diff --git a/CHANGELOG b/CHANGELOG index 5007caa4..f2ec532b 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,11 @@ jc changelog 20221025 v1.22.2 +- add `sshd_conf` parser - fix `csv` and `csv-s` parsers for UTF-8 encoded CSV files with leading BOM bytes - fix exit code to be non-zero on interrupt +- allow parser module objects to be used as arguments to `jc.get_help()` and `jc.parser_info()` +- catch unexpected exceptions in the CLI - add error message on interrupt to STDERR - add python 3.11 tests to github actions diff --git a/jc/lib.py b/jc/lib.py index d3e3a80f..b962993f 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -148,6 +148,7 @@ parsers: List[str] = [ 'sfdisk', 'shadow', 'ss', + 'sshd-conf', 'stat', 'stat-s', 'sysctl', diff --git a/jc/parsers/sshd_conf.py b/jc/parsers/sshd_conf.py new file mode 100644 index 00000000..affb2f7f --- /dev/null +++ b/jc/parsers/sshd_conf.py @@ -0,0 +1,189 @@ +"""jc - JSON Convert `sshd -T` command output parser + +<> + +Usage (cli): + + $ sshd -T | jc --sshd-conf + +or + + $ jc sshd -T + +Usage (module): + + import jc + result = jc.parse('sshd_conf', sshd_command_output) + +Schema: + + [ + { + "sshd_conf": string, + "bar": boolean, + "baz": integer + } + ] + +Examples: + + $ sshd_conf | jc --sshd_conf -p + [] + + $ sshd_conf | jc --sshd_conf -p -r + [] +""" +from typing import Set, List, Dict +from jc.jc_types import JSONDictType +import jc.utils + + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`sshd -T` command parser' + author = 'Kelly Brazil' + author_email = 'kellyjonbrazil@gmail.com' + compatible = ['linux', 'darwin', 'freebsd'] + magic_commands = ['sshd -T'] + + +__version__ = info.version + + +def _process(proc_data: JSONDictType) -> JSONDictType: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured to conform to the schema. + """ + split_fields_space: Set[str] = { + 'authorizedkeysfile', 'include', 'ipqos', 'permitlisten', 'permitopen' + } + + split_fields_comma: Set[str] = { + 'casignaturealgorithms', 'ciphers', 'gssapikexalgorithms', 'hostbasedacceptedalgorithms', + 'hostbasedacceptedkeytypes', 'hostkeyalgorithms', 'kexalgorithms', 'macs', + 'pubkeyacceptedalgorithms', 'pubkeyacceptedkeytypes' + } + + int_list: Set[str] = {'clientalivecountmax', 'clientaliveinterval', 'logingracetime', + 'maxauthtries', 'maxsessions', 'maxstartups', 'maxstartups_rate', 'maxstartups_full', + 'rekeylimit', 'rekeylimit_time', 'x11displayoffset', 'x11maxdisplays' + } + + dict_copy = proc_data.copy() + for key, val in dict_copy.items(): + # this is a list value + if key == 'acceptenv': + new_list: List[str] = [] + for item in val: # type: ignore + new_list.extend(item.split()) + proc_data[key] = new_list + continue + + if key == 'maxstartups': + maxstart_split = val.split(':', maxsplit=2) # type: ignore + proc_data[key] = maxstart_split[0] + if len(maxstart_split) > 1: + proc_data[key + '_rate'] = maxstart_split[1] + if len(maxstart_split) > 2: + proc_data[key + '_full'] = maxstart_split[2] + continue + + if key == 'port': + port_list: List[int] = [] + for item in val: # type: ignore + port_list.append(int(item)) + proc_data[key] = port_list + continue + + if key == 'rekeylimit': + rekey_split = val.split(maxsplit=1) # type: ignore + proc_data[key] = rekey_split[0] + if len(rekey_split) > 1: + proc_data[key + '_time'] = rekey_split[1] + continue + + if key == 'subsystem': + rekey_split = val.split(maxsplit=1) # type: ignore + proc_data[key] = rekey_split[0] + if len(rekey_split) > 1: + proc_data[key + '_command'] = rekey_split[1] + continue + + if key in split_fields_space: + proc_data[key] = val.split() # type: ignore + continue + + if key in split_fields_comma: + proc_data[key] = val.split(',') # type: ignore + continue + + for key, val in proc_data.items(): + if key in int_list: + proc_data[key] = jc.utils.convert_to_int(val) + + return proc_data + + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> JSONDictType: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output: Dict = {} + multi_fields: Set[str] = {'acceptenv', 'hostkey', 'listenaddress', 'port'} + modified_fields: Set[str] = {'casignaturealgorithms', 'ciphers', 'hostbasedacceptedalgorithms', + 'kexalgorithms', 'macs', 'pubkeyacceptedalgorithms' + } + modifiers: Set[str] = {'+', '-', '^'} + + if jc.utils.has_data(data): + + for line in filter(None, data.splitlines()): + # support configuration file by skipping commented lines + if line.strip().startswith('#'): + continue + + key, val = line.split(maxsplit=1) + # support configuration file by converting to lower case + key = key.lower() + + if key in multi_fields: + if key not in raw_output: + raw_output[key] = [] + raw_output[key].append(val) + continue + + if key in modified_fields and val[0] in modifiers: + raw_output[key] = val[1:] + raw_output[key + '_strategy'] = val[0] + continue + + raw_output[key] = val + continue + + return raw_output if raw else _process(raw_output) diff --git a/tests/fixtures/generic/sshd-T-centos7.out b/tests/fixtures/generic/sshd-T-centos7.out new file mode 100644 index 00000000..417a60ad --- /dev/null +++ b/tests/fixtures/generic/sshd-T-centos7.out @@ -0,0 +1,104 @@ +port 22 +addressfamily any +listenaddress [::]:22 +listenaddress 0.0.0.0:22 +usepam yes +logingracetime 120 +x11displayoffset 10 +x11maxdisplays 1000 +maxauthtries 6 +maxsessions 10 +clientaliveinterval 0 +clientalivecountmax 3 +streamlocalbindmask 0177 +permitrootlogin yes +ignorerhosts yes +ignoreuserknownhosts no +hostbasedauthentication no +hostbasedusesnamefrompacketonly no +pubkeyauthentication yes +kerberosauthentication no +kerberosorlocalpasswd yes +kerberosticketcleanup yes +gssapiauthentication yes +gssapicleanupcredentials no +gssapikeyexchange no +gssapistrictacceptorcheck yes +gssapistorecredentialsonrekey no +gssapikexalgorithms gss-gex-sha1-,gss-group1-sha1-,gss-group14-sha1- +passwordauthentication yes +kbdinteractiveauthentication no +challengeresponseauthentication no +printmotd yes +printlastlog yes +x11forwarding yes +x11uselocalhost yes +permittty yes +permituserrc yes +strictmodes yes +tcpkeepalive yes +permitemptypasswords no +permituserenvironment no +compression yes +gatewayports no +showpatchlevel no +usedns yes +allowtcpforwarding yes +allowagentforwarding yes +disableforwarding no +allowstreamlocalforwarding yes +streamlocalbindunlink no +useprivilegeseparation sandbox +kerberosusekuserok yes +gssapienablek5users no +exposeauthenticationmethods never +fingerprinthash SHA256 +pidfile /var/run/sshd.pid +xauthlocation /usr/bin/xauth +ciphers chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com,aes128-cbc,aes192-cbc,aes256-cbc,blowfish-cbc,cast128-cbc,3des-cbc +macs umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1 +banner none +forcecommand none +chrootdirectory none +trustedusercakeys none +revokedkeys none +authorizedprincipalsfile none +versionaddendum none +authorizedkeyscommand none +authorizedkeyscommanduser none +authorizedprincipalscommand none +authorizedprincipalscommanduser none +hostkeyagent none +kexalgorithms curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha1,diffie-hellman-group14-sha256,diffie-hellman-group14-sha1,diffie-hellman-group1-sha1 +hostbasedacceptedkeytypes ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +hostkeyalgorithms ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +pubkeyacceptedkeytypes ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,ssh-rsa-cert-v01@openssh.com,ssh-dss-cert-v01@openssh.com,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,ssh-ed25519,rsa-sha2-512,rsa-sha2-256,ssh-rsa,ssh-dss +loglevel INFO +syslogfacility AUTHPRIV +authorizedkeysfile .ssh/authorized_keys +hostkey /etc/ssh/ssh_host_rsa_key +hostkey /etc/ssh/ssh_host_ecdsa_key +hostkey /etc/ssh/ssh_host_ed25519_key +acceptenv LANG +acceptenv LC_CTYPE +acceptenv LC_NUMERIC +acceptenv LC_TIME +acceptenv LC_COLLATE +acceptenv LC_MONETARY +acceptenv LC_MESSAGES +acceptenv LC_PAPER +acceptenv LC_NAME +acceptenv LC_ADDRESS +acceptenv LC_TELEPHONE +acceptenv LC_MEASUREMENT +acceptenv LC_IDENTIFICATION +acceptenv LC_ALL +acceptenv LANGUAGE +acceptenv XMODIFIERS +authenticationmethods any +subsystem sftp /usr/libexec/openssh/sftp-server +maxstartups 10:30:100 +permittunnel no +ipqos lowdelay throughput +rekeylimit 0 0 +permitopen any diff --git a/tests/fixtures/generic/sshd-T.out b/tests/fixtures/generic/sshd-T.out new file mode 100644 index 00000000..c191325e --- /dev/null +++ b/tests/fixtures/generic/sshd-T.out @@ -0,0 +1,92 @@ +acceptenv LANG +acceptenv LC_* +acceptenv test1 test2 +addressfamily any +allowagentforwarding yes +allowstreamlocalforwarding yes +allowtcpforwarding yes +authenticationmethods any +authorizedkeyscommand none +authorizedkeyscommanduser none +authorizedkeysfile .ssh/authorized_keys .ssh/authorized_keys2 +authorizedprincipalscommand none +authorizedprincipalscommanduser none +authorizedprincipalsfile none +banner none +casignaturealgorithms ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256 +chrootdirectory none +ciphers +chacha20-poly1305@openssh.com,aes128-ctr,aes192-ctr,aes256-ctr,aes128-gcm@openssh.com,aes256-gcm@openssh.com +clientalivecountmax 3 +clientaliveinterval 0 +compression yes +disableforwarding no +exposeauthinfo no +fingerprinthash SHA256 +forcecommand none +gatewayports no +gssapiauthentication no +gssapicleanupcredentials yes +gssapikexalgorithms gss-group14-sha256-,gss-group16-sha512-,gss-nistp256-sha256-,gss-curve25519-sha256-,gss-group14-sha1-,gss-gex-sha1- +gssapikeyexchange no +gssapistorecredentialsonrekey no +gssapistrictacceptorcheck yes +hostbasedacceptedalgorithms ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256 +hostbasedauthentication no +hostbasedusesnamefrompacketonly no +hostkeyagent none +hostkeyalgorithms ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256 +hostkey /etc/ssh/ssh_host_ecdsa_key +hostkey /etc/ssh/ssh_host_ed25519_key +hostkey /etc/ssh/ssh_host_rsa_key +ignorerhosts yes +ignoreuserknownhosts no +ipqos lowdelay throughput +kbdinteractiveauthentication no +kerberosauthentication no +kerberosorlocalpasswd yes +kerberosticketcleanup yes +kexalgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521,diffie-hellman-group-exchange-sha256,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group14-sha256 +listenaddress 0.0.0.0:22 +listenaddress [::]:22 +logingracetime 120 +loglevel INFO +macs ^umac-64-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,hmac-sha1-etm@openssh.com,umac-64@openssh.com,umac-128@openssh.com,hmac-sha2-256,hmac-sha2-512,hmac-sha1 +maxauthtries 6 +maxsessions 10 +maxstartups 10:30:100 +modulifile /etc/ssh/moduli +passwordauthentication yes +permitemptypasswords no +permitlisten any +permitopen any +permitrootlogin without-password +permittty yes +permittunnel no +permituserenvironment no +permituserrc yes +persourcemaxstartups none +persourcenetblocksize 32:128 +pidfile /run/sshd.pid +port 22 +printlastlog yes +printmotd no +pubkeyacceptedalgorithms ssh-ed25519-cert-v01@openssh.com,ecdsa-sha2-nistp256-cert-v01@openssh.com,ecdsa-sha2-nistp384-cert-v01@openssh.com,ecdsa-sha2-nistp521-cert-v01@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,sk-ecdsa-sha2-nistp256-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,ssh-ed25519,ecdsa-sha2-nistp256,ecdsa-sha2-nistp384,ecdsa-sha2-nistp521,sk-ssh-ed25519@openssh.com,sk-ecdsa-sha2-nistp256@openssh.com,rsa-sha2-512,rsa-sha2-256 +pubkeyauthentication yes +pubkeyauthoptions none +rekeylimit 0 0 +revokedkeys none +securitykeyprovider internal +streamlocalbindmask 0177 +streamlocalbindunlink no +strictmodes yes +subsystem sftp /usr/lib/openssh/sftp-server +syslogfacility AUTH +tcpkeepalive yes +trustedusercakeys none +usedns no +usepam yes +versionaddendum none +x11displayoffset 10 +x11forwarding yes +x11uselocalhost yes +xauthlocation /usr/bin/xauth diff --git a/tests/fixtures/generic/sshd_config b/tests/fixtures/generic/sshd_config new file mode 100644 index 00000000..31949154 --- /dev/null +++ b/tests/fixtures/generic/sshd_config @@ -0,0 +1,139 @@ +# $OpenBSD: sshd_config,v 1.100 2016/08/15 12:32:04 naddy Exp $ + +# This is the sshd server system-wide configuration file. See +# sshd_config(5) for more information. + +# This sshd was compiled with PATH=/usr/local/bin:/usr/bin + +# The strategy used for options in the default sshd_config shipped with +# OpenSSH is to specify options with their default value where +# possible, but leave them commented. Uncommented options override the +# default value. + +# If you want to change the port on a SELinux system, you have to tell +# SELinux about this change. +# semanage port -a -t ssh_port_t -p tcp #PORTNUMBER +# +#Port 22 +#AddressFamily any +#ListenAddress 0.0.0.0 +#ListenAddress :: + +HostKey /etc/ssh/ssh_host_rsa_key +#HostKey /etc/ssh/ssh_host_dsa_key +HostKey /etc/ssh/ssh_host_ecdsa_key +HostKey /etc/ssh/ssh_host_ed25519_key + +# Ciphers and keying +#RekeyLimit default none + +# Logging +#SyslogFacility AUTH +SyslogFacility AUTHPRIV +#LogLevel INFO + +# Authentication: + +#LoginGraceTime 2m +#PermitRootLogin yes +#StrictModes yes +#MaxAuthTries 6 +#MaxSessions 10 + +#PubkeyAuthentication yes + +# The default is to check both .ssh/authorized_keys and .ssh/authorized_keys2 +# but this is overridden so installations will only check .ssh/authorized_keys +AuthorizedKeysFile .ssh/authorized_keys + +#AuthorizedPrincipalsFile none + +#AuthorizedKeysCommand none +#AuthorizedKeysCommandUser nobody + +# For this to work you will also need host keys in /etc/ssh/ssh_known_hosts +#HostbasedAuthentication no +# Change to yes if you don't trust ~/.ssh/known_hosts for +# HostbasedAuthentication +#IgnoreUserKnownHosts no +# Don't read the user's ~/.rhosts and ~/.shosts files +#IgnoreRhosts yes + +# To disable tunneled clear text passwords, change to no here! +#PasswordAuthentication yes +#PermitEmptyPasswords no +PasswordAuthentication yes + +# Change to no to disable s/key passwords +#ChallengeResponseAuthentication yes +ChallengeResponseAuthentication no + +# Kerberos options +#KerberosAuthentication no +#KerberosOrLocalPasswd yes +#KerberosTicketCleanup yes +#KerberosGetAFSToken no +#KerberosUseKuserok yes + +# GSSAPI options +GSSAPIAuthentication yes +GSSAPICleanupCredentials no +#GSSAPIStrictAcceptorCheck yes +#GSSAPIKeyExchange no +#GSSAPIEnablek5users no + +# Set this to 'yes' to enable PAM authentication, account processing, +# and session processing. If this is enabled, PAM authentication will +# be allowed through the ChallengeResponseAuthentication and +# PasswordAuthentication. Depending on your PAM configuration, +# PAM authentication via ChallengeResponseAuthentication may bypass +# the setting of "PermitRootLogin without-password". +# 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'. +# WARNING: 'UsePAM no' is not supported in Red Hat Enterprise Linux and may cause several +# problems. +UsePAM yes + +#AllowAgentForwarding yes +#AllowTcpForwarding yes +#GatewayPorts no +X11Forwarding yes +#X11DisplayOffset 10 +#X11UseLocalhost yes +#PermitTTY yes +#PrintMotd yes +#PrintLastLog yes +#TCPKeepAlive yes +#UseLogin no +#UsePrivilegeSeparation sandbox +#PermitUserEnvironment no +#Compression delayed +#ClientAliveInterval 0 +#ClientAliveCountMax 3 +#ShowPatchLevel no +#UseDNS yes +#PidFile /var/run/sshd.pid +#MaxStartups 10:30:100 +#PermitTunnel no +#ChrootDirectory none +#VersionAddendum none + +# no default banner path +#Banner none + +# Accept locale-related environment variables +AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES +AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT +AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE +AcceptEnv XMODIFIERS + +# override default of no subsystems +Subsystem sftp /usr/libexec/openssh/sftp-server + +# Example of overriding settings on a per-user basis +#Match User anoncvs +# X11Forwarding no +# AllowTcpForwarding no +# PermitTTY no +# ForceCommand cvs server