mirror of
https://github.com/jtesta/ssh-audit.git
synced 2026-05-25 07:21:23 +02:00
Compare commits
12 Commits
v3.3.0
..
62d41ea35f
| Author | SHA1 | Date | |
|---|---|---|---|
| 62d41ea35f | |||
| f15e53ef17 | |||
| 6b8b20aebb | |||
| 231a47959a | |||
| 48b9ee7deb | |||
| 9814d18baf | |||
| a01baadfa8 | |||
| 45abc3aaf4 | |||
| 99c64787d9 | |||
| 3fa62c3ac5 | |||
| d7fff591fa | |||
| 84647ecb32 |
+9
-9
@@ -15,10 +15,10 @@ An executable can only be made on a Windows host because the PyInstaller tool (h
|
||||
|
||||
# PyPI
|
||||
|
||||
To create package and upload to test server (hint: use username '\_\_token\_\_' and API token for test.pypi.org):
|
||||
To create package and upload to test server (hint: use API token for test.pypi.org):
|
||||
|
||||
```
|
||||
$ sudo apt install python3-virtualenv python3.10-venv
|
||||
$ sudo apt install python3-virtualenv python3.12-venv
|
||||
$ make -f Makefile.pypi
|
||||
$ make -f Makefile.pypi uploadtest
|
||||
```
|
||||
@@ -26,12 +26,12 @@ To create package and upload to test server (hint: use username '\_\_token\_\_'
|
||||
To download from test server and verify:
|
||||
|
||||
```
|
||||
$ virtualenv -p /usr/bin/python3 /tmp/pypi_test
|
||||
$ virtualenv /tmp/pypi_test
|
||||
$ cd /tmp/pypi_test; source bin/activate
|
||||
$ pip3 install --index-url https://test.pypi.org/simple ssh-audit
|
||||
```
|
||||
|
||||
To upload to production server (hint: use username '\_\_token\_\_' and API token for production pypi.org):
|
||||
To upload to production server (hint: use API token for production pypi.org):
|
||||
|
||||
```
|
||||
$ make -f Makefile.pypi uploadprod
|
||||
@@ -40,7 +40,7 @@ To upload to production server (hint: use username '\_\_token\_\_' and API token
|
||||
To download from production server and verify:
|
||||
|
||||
```
|
||||
$ virtualenv -p /usr/bin/python3 /tmp/pypi_prod
|
||||
$ virtualenv /tmp/pypi_prod
|
||||
$ cd /tmp/pypi_prod; source bin/activate
|
||||
$ pip3 install ssh-audit
|
||||
```
|
||||
@@ -48,14 +48,14 @@ To download from production server and verify:
|
||||
|
||||
# Snap
|
||||
|
||||
To create the snap package, run a fully-updated Ubuntu Server 22.04 VM.
|
||||
To create the Snap package, run a fully-updated Ubuntu Server 24.04 VM.
|
||||
|
||||
Create the snap package with:
|
||||
Create the Snap package with:
|
||||
```
|
||||
$ ./build_snap.sh
|
||||
```
|
||||
|
||||
Upload the snap with:
|
||||
Upload the Snap with:
|
||||
|
||||
```
|
||||
$ snapcraft export-login ~/snap_creds.txt
|
||||
@@ -68,7 +68,7 @@ Upload the snap with:
|
||||
|
||||
# Docker
|
||||
|
||||
Ensure that the buildx plugin is available by following the installation instructions available at: https://docs.docker.com/engine/install/ubuntu/
|
||||
Ensure that the `buildx` plugin is available by following the installation instructions available at: https://docs.docker.com/engine/install/ubuntu/
|
||||
|
||||
Build a local image with:
|
||||
|
||||
|
||||
@@ -41,64 +41,61 @@
|
||||
|
||||
## Usage
|
||||
```
|
||||
usage: ssh-audit.py [options] <host>
|
||||
usage: ssh-audit.py [-h] [-1] [-2] [-4] [-6] [-b] [-c] [-d]
|
||||
[-g <min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>] [-j] [-l {info,warn,fail}] [-L]
|
||||
[-M custom_policy.txt] [-m] [-n] [-P "Built-In Policy Name" / custom_policy.txt] [-p N]
|
||||
[-T targets.txt] [-t N] [-v] [--conn-rate-test N[:max_rate]] [--dheat N[:kex[:e_len]]]
|
||||
[--lookup alg1[,alg2,...]] [--skip-rate-test] [--threads N]
|
||||
[host]
|
||||
|
||||
-h, --help print this help
|
||||
positional arguments:
|
||||
host target hostname or IPv4/IPv6 address
|
||||
|
||||
optional arguments:
|
||||
-h, --help show this help message and exit
|
||||
-1, --ssh1 force ssh version 1 only
|
||||
-2, --ssh2 force ssh version 2 only
|
||||
-4, --ipv4 enable IPv4 (order of precedence)
|
||||
-6, --ipv6 enable IPv6 (order of precedence)
|
||||
-b, --batch batch output
|
||||
-c, --client-audit starts a server on port 2222 to audit client
|
||||
software config (use -p to change port;
|
||||
use -t to change timeout)
|
||||
--conn-rate-test=N[:max_rate] perform a connection rate test (useful
|
||||
for collecting metrics related to
|
||||
susceptibility of the DHEat vuln).
|
||||
Testing is conducted with N concurrent
|
||||
sockets with an optional maximum rate
|
||||
of connections per second.
|
||||
-d, --debug Enable debug output.
|
||||
--dheat=N[:kex[:e_len]] continuously perform the DHEat DoS attack
|
||||
(CVE-2002-20001) against the target using N
|
||||
concurrent sockets. Optionally, a specific
|
||||
key exchange algorithm can be specified
|
||||
instead of allowing it to be automatically
|
||||
chosen. Additionally, a small length of
|
||||
the fake e value sent to the server can
|
||||
be chosen for a more efficient attack (such
|
||||
as 4).
|
||||
-g, --gex-test=<x[,y,...]> dh gex modulus size test
|
||||
<min1:pref1:max1[,min2:pref2:max2,...]>
|
||||
<x-y[:step]>
|
||||
-j, --json JSON output (use -jj to enable indents)
|
||||
-l, --level=<level> minimum output level (info|warn|fail)
|
||||
-L, --list-policies list all the official, built-in policies. Use with -v
|
||||
to view policy change logs.
|
||||
--lookup=<alg1,alg2,...> looks up an algorithm(s) without
|
||||
connecting to a server
|
||||
-m, --manual print the man page (Docker, PyPI, Snap, and Windows
|
||||
builds only)
|
||||
-M, --make-policy=<policy.txt> creates a policy based on the target server
|
||||
(i.e.: the target server has the ideal
|
||||
configuration that other servers should
|
||||
adhere to)
|
||||
-n, --no-colors disable colors
|
||||
-p, --port=<port> port to connect
|
||||
-P, --policy=<"policy name" | policy.txt> run a policy test using the
|
||||
specified policy
|
||||
--skip-rate-test skip the connection rate test during standard audits
|
||||
(used to safely infer whether the DHEat attack
|
||||
is viable)
|
||||
-t, --timeout=<secs> timeout (in seconds) for connection and reading
|
||||
(default: 5)
|
||||
-T, --targets=<hosts.txt> a file containing a list of target hosts (one
|
||||
per line, format HOST[:PORT]). Use -p/--port
|
||||
to set the default port for all hosts. Use
|
||||
--threads to control concurrent scans.
|
||||
--threads=<threads> number of threads to use when scanning multiple
|
||||
targets (-T/--targets) (default: 32)
|
||||
-v, --verbose verbose output
|
||||
-c, --client-audit starts a server on port 2222 to audit client software config (use -p to change port; use -t
|
||||
to change timeout)
|
||||
-d, --debug enable debugging output
|
||||
-g <min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>, --gex-test <min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>
|
||||
conducts a very customized Diffie-Hellman GEX modulus size test. Tests an array of minimum,
|
||||
preferred, and maximum values, or a range of values with an optional incremental step amount
|
||||
-j, --json enable JSON output (use -jj to enable indentation for better readability)
|
||||
-l {info,warn,fail}, --level {info,warn,fail}
|
||||
minimum output level (default: info)
|
||||
-L, --list-policies list all the official, built-in policies. Combine with -v to view policy change logs
|
||||
-M custom_policy.txt, --make-policy custom_policy.txt
|
||||
creates a policy based on the target server (i.e.: the target server has the ideal
|
||||
configuration that other servers should adhere to), and stores it in the file path specified
|
||||
-m, --manual print the man page (Docker, PyPI, Snap, and Windows builds only)
|
||||
-n, --no-colors disable colors (automatic when the NO_COLOR environment variable is set)
|
||||
-P "Built-In Policy Name" / custom_policy.txt, --policy "Built-In Policy Name" / custom_policy.txt
|
||||
run a policy test using the specified policy (use -L to see built-in policies, or specify
|
||||
filesystem path to custom policy created by -M)
|
||||
-p N, --port N the TCP port to connect to (or to listen on when -c is used)
|
||||
-T targets.txt, --targets targets.txt
|
||||
a file containing a list of target hosts (one per line, format HOST[:PORT]). Use -p/--port
|
||||
to set the default port for all hosts. Use --threads to control concurrent scans
|
||||
-t N, --timeout N timeout (in seconds) for connection and reading (default: 5)
|
||||
-v, --verbose enable verbose output
|
||||
--conn-rate-test N[:max_rate]
|
||||
perform a connection rate test (useful for collecting metrics related to susceptibility of
|
||||
the DHEat vuln). Testing is conducted with N concurrent sockets with an optional maximum
|
||||
rate of connections per second
|
||||
--dheat N[:kex[:e_len]]
|
||||
continuously perform the DHEat DoS attack (CVE-2002-20001) against the target using N
|
||||
concurrent sockets. Optionally, a specific key exchange algorithm can be specified instead
|
||||
of allowing it to be automatically chosen. Additionally, a small length of the fake e value
|
||||
sent to the server can be chosen for a more efficient attack (such as 4).
|
||||
--lookup alg1[,alg2,...]
|
||||
looks up an algorithm(s) without connecting to a server.
|
||||
--skip-rate-test skip the connection rate test during standard audits (used to safely infer whether the DHEat
|
||||
attack is viable)
|
||||
--threads N number of threads to use when scanning multiple targets (-T/--targets) (default: 32)
|
||||
```
|
||||
* if both IPv4 and IPv6 are used, order of precedence can be set by using either `-46` or `-64`.
|
||||
* batch flag `-b` will output sections without header and without empty lines (implies verbose flag).
|
||||
@@ -219,6 +216,9 @@ For convenience, a web front-end on top of the command-line tool is available at
|
||||
|
||||
## ChangeLog
|
||||
|
||||
### v3.4.0-dev
|
||||
- Migrated from deprecated `getopt` module to `argparse`; partial credit [oam7575](https://github.com/oam7575).
|
||||
|
||||
### v3.3.0 (2024-10-15)
|
||||
- Added Python 3.13 support.
|
||||
- Added built-in policies for Ubuntu 24.04 LTS server & client, OpenSSH 9.8, and OpenSSH 9.9.
|
||||
|
||||
@@ -111,18 +111,9 @@ echo "Processing man page at ${MAN_PAGE} and placing output into ${GLOBALS_PY}..
|
||||
# * 'MAN_KEEP_FORMATTING' preserves the backspace-overwrite sequence when
|
||||
# redirected to a file or a pipe.
|
||||
# * sed converts unicode hyphens into an ASCI equivalent.
|
||||
# * The 'ul' command converts the backspace-overwrite sequence to an ANSI
|
||||
# escape sequence. Not required under Cygwin because man outputs ANSI escape
|
||||
# codes automatically.
|
||||
|
||||
echo BUILTIN_MAN_PAGE = '"""' >> "${GLOBALS_PY}"
|
||||
|
||||
if [[ "${PLATFORM}" == CYGWIN* ]]; then
|
||||
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
||||
else
|
||||
MANWIDTH=80 MAN_KEEP_FORMATTING=1 man "${MAN_PAGE}" | ul | sed $'s/\u2010/-/g' >> "${GLOBALS_PY}"
|
||||
fi
|
||||
|
||||
echo '"""' >> "${GLOBALS_PY}"
|
||||
|
||||
echo "Done."
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
# The version to display.
|
||||
VERSION = 'v3.3.0'
|
||||
VERSION = 'v3.4.0-dev'
|
||||
|
||||
# SSH software to impersonate
|
||||
SSH_HEADER = 'SSH-{0}-OpenSSH_8.2'
|
||||
|
||||
@@ -0,0 +1,477 @@
|
||||
"""
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (C) 2020-2024 Joe Testa (jtesta@positronsecurity.com)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
from typing import Dict, Any
|
||||
import sys
|
||||
|
||||
from ssh_audit import exitcodes
|
||||
from ssh_audit.globals import VERSION
|
||||
|
||||
|
||||
|
||||
BUILTIN_GUIDES: Dict[str, Dict[str, Any]] = {
|
||||
|
||||
# Server
|
||||
# Amazon Server
|
||||
'Amazon 2023 Server (version 1)': {'version': '1', 'changelog': {'2024-10-01': 'Re-ordered host keys to prioritize ED25519 due to efficiency. Re-ordered cipher list to prioritize larger key sizes as a countermeasure to quantum attacks', '2024-04-22': 'added connection throttling instructions to counteract the DHEat denial-of-service attack.', '2024-03-15': 'Initial revision'}, 'server_policy': True},
|
||||
|
||||
# Debian Server
|
||||
'Debian Bullseye Server (version 1)': {'version': '1', 'changelog': {'2021-09-17': 'Initial Revision.'}, 'server_policy': True},
|
||||
'Debian Bookworm Server (version 1)': {'version': '1', 'changelog': {'2021-09-17': 'Initial Revision.'}, 'server_policy': True},
|
||||
|
||||
# Rocky Linux
|
||||
'Rocky 9 Server (version 1)': {'version': '1', 'changelog': {'2024-10-01': 'Re-ordered host keys to prioritize ED25519 due to efficiency. Re-ordered cipher list to prioritize larger key sizes as a countermeasure to quantum attacks', '\n2024-04-24': 'Added connection throttling instructions to counteract the DHEat denial-of-service attack.'}, 'server_policy': True},
|
||||
|
||||
# Ubuntu Server
|
||||
'Ubuntu 2004 Server (version 1)': {'version': '1', 'changelog': {'2024-04-24': '\nAdded connection throttling instructions to counteract the DHEat denial-of-service attack.'}, 'server_policy': True},
|
||||
'Ubuntu 2204 Server (version 1)': {'version': '1', 'changelog': {'2024-10-01': '\nRe-ordered host keys to prioritize ED25519 due to efficiency. \nRe-ordered cipher list to prioritize larger key sizes as a countermeasure to quantum attacks', '\n2024-04-22': '\nAdded connection throttling instructions to counteract the DHEat denial-of-service attack.'}, 'server_policy': True},
|
||||
'Ubuntu 2404 Server (version 1)': {'version': '1', 'changelog': {'2024-10-01': '\nAdded Required RSASize directive to enforce a minimum of 3072-bit user and host-based authentication keys.', '\n2024-04-29': '\nInitial revision. In comparison to Ubuntu 22.04 LTS guide, the following changes were made: \n1.) For key exchanges, diffie-hellman-group18-sha512 and diffie-hellman-group-exchange-sha256 were prioritized over diffie-hellman-group16-sha512 due to greater security strength; GSS algorithms were prioritized over their non-GSS equivalents in order to match the client guide, \n2.) For ciphers, 256-bit AES ciphers were prioritized over 192 and 128-bit AES ciphers due to their increased resistence against quantum computing attacks (previously, weaker GCM ciphers had priority over CTR ciphers), \n3.) The HostbasedAcceptedAlgorithms and PubkeyAcceptedAlgorithms settings are now the same as HostKeyAlgorithms setting, \n4.) The hmac-sha2-512-etm@openssh.com MAC was increased in priority due to its increased resistence against quantum computing attacks, and \n5.) The ED25519 host keys were given priority over RSA host keys due to their greater efficiency.'}, 'server_policy': True},
|
||||
|
||||
# Client
|
||||
# Amazon
|
||||
'Amazon 2023 Client (version 1)': {'version': '1', 'changelog': {'2024-10-01': 'Re-ordered cipher list to prioritize larger key sizes as a countermeasure to quantum attacks.', '2024-04-22': 'added connection throttling instructions to counteract the DHEat denial-of-service attack.', '2024-03-15': 'Initial revision'}, 'server_policy': False},
|
||||
|
||||
# Debian
|
||||
'Debian Bookworm Client (version 1)': {'version': '1', 'changelog': {'2024-10-01': 'Added RequiredRSASize directive to enforce a minimum of 3072-bit user and host-based authentication keys. Re-ordered cipher list to prioritize larger key sizes as a countermeasure to quantum attacks.', '2024-03-15': 'Initial Revision'}, 'server_policy': False},
|
||||
|
||||
# Rocky Linux
|
||||
'Rocky 9 Client (version 1)': {'version': '1', 'changelog': {'2024-10-01': 'Added RequiredRSASize directive to enforce a minimum of 3072-bit user and host-based authentication keys. Re-ordered cipher list to prioritize larger key sizes as a countermeasure to quantum attacks.', '2024-03-15': 'Initial Revision'}, 'server_policy': False},
|
||||
|
||||
# Mint
|
||||
'Mint 20 Client (version 1)': {'version': '1', 'changelog': {'2020-10-20': 'Initial Revision'}, 'server_policy': False},
|
||||
'Mint 21 Client (version 1)': {'version': '1', 'changelog': {'2020-10-20': 'Initial Revision'}, 'server_policy': False},
|
||||
'Mint 22 Client (version 1)': {'version': '1', 'changelog': {'2020-10-20': 'Initial Revision'}, 'server_policy': False},
|
||||
|
||||
# Ubuntu
|
||||
'Ubuntu 2004 Client (version 1)': {'version': '1', 'changelog': {'2020-10-20': 'Initial Revision'}, 'server_policy': False},
|
||||
'Ubuntu 2204 Client (version 1)': {'version': '1', 'changelog': {'2020-10-20': 'Initial Revision'}, 'server_policy': False},
|
||||
'Ubuntu 2404 Client (version 1)': {'version': '1', 'changelog': {'2020-10-20': 'Initial Revision'}, 'server_policy': False},
|
||||
}
|
||||
|
||||
|
||||
|
||||
class PrintHardeningGuides:
|
||||
def __init__(self, os_type: str, os_ver: str, clientserver: str) -> None:
|
||||
self.os_type = os_type
|
||||
self.os_ver = os_ver
|
||||
self.clientserver = clientserver
|
||||
|
||||
self.get_config()
|
||||
|
||||
def get_config(self) -> None:
|
||||
|
||||
retval = exitcodes.GOOD
|
||||
|
||||
os_type = self.os_type
|
||||
os_ver = self.os_ver
|
||||
clientserver = self.clientserver
|
||||
policy_name = os_type + " " + os_ver + " " + clientserver
|
||||
|
||||
supported_os = ["Amazon", "Debian", "Mint", "Rocky", "Ubuntu"]
|
||||
supported_edition = ["2404", "2204", "2004", "1804", "2023", "22", "21", "20", "9", "Bookworm", "Bullseye"]
|
||||
if clientserver not in ["Server", "Client"] or os_type not in supported_os and os_ver not in supported_edition:
|
||||
print(" ")
|
||||
print(f"\033[1mssh-audit Version : {VERSION}\033[0m")
|
||||
print(" ")
|
||||
print(f"\033[1mConfiguration : {os_type} {os_ver} {clientserver} is not supported\033[0m")
|
||||
PrintHardeningGuides.supported_varient()
|
||||
sys.exit(retval)
|
||||
|
||||
# Server Configs
|
||||
if clientserver in ["Server"]:
|
||||
# Amazon Linux
|
||||
if os_type in ["Amazon"] and os_ver in ["2023"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.amazon_server_2023()
|
||||
sys.exit(retval)
|
||||
# Debian
|
||||
elif os_type in ["Debian"] and os_ver in ["Bookworm"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.bookworm_server()
|
||||
PrintHardeningGuides.debian_ubuntu_rate_throttling()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Debian"] and os_ver in ["Bullseye"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.bullseye_server()
|
||||
sys.exit(retval)
|
||||
# Rocky Linux
|
||||
elif os_type in ["Rocky"] and os_ver in ["9"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.rocky_9_server()
|
||||
sys.exit(retval)
|
||||
# Ubuntu
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["2404"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.ubuntu_server_2404()
|
||||
PrintHardeningGuides.debian_ubuntu_rate_throttling()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["2204"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.ubuntu_server_2204()
|
||||
PrintHardeningGuides.debian_ubuntu_rate_throttling()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["2004"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_modern_common()
|
||||
PrintHardeningGuides.ubuntu_server_2004()
|
||||
PrintHardeningGuides.debian_ubuntu_rate_throttling()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["1804"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.server_legacy_common()
|
||||
PrintHardeningGuides.ubuntu_server_1804()
|
||||
sys.exit(retval)
|
||||
else:
|
||||
PrintHardeningGuides.supported_varient()
|
||||
sys.exit(retval)
|
||||
|
||||
|
||||
# Client Configs
|
||||
if clientserver in ["Client"]:
|
||||
# Amazon
|
||||
if os_type in ["Amazon"] and os_ver in ["2023"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.amazon_2023_client()
|
||||
sys.exit(retval)
|
||||
# Debian
|
||||
elif os_type in ["Debian"] and os_ver in ["Bookworm"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.debian_bookworm_client()
|
||||
sys.exit(retval)
|
||||
# Mint
|
||||
elif os_type in ["Mint"] and os_ver in ["22"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.ubuntu_2404_mint_22_client()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Mint"] and os_ver in ["21"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.ubuntu_2204_mint_21_client()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Mint"] and os_ver in ["20"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.ubuntu_2004_mint_20_client()
|
||||
sys.exit(retval)
|
||||
# Rocky
|
||||
elif os_type in ["Rocky"] and os_ver in ["9"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.rocky_9_client()
|
||||
sys.exit(retval)
|
||||
# Ubuntu
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["2404"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.ubuntu_2404_mint_22_client()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["2204"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.ubuntu_2204_mint_21_client()
|
||||
sys.exit(retval)
|
||||
elif os_type in ["Ubuntu"] and os_ver in ["2004"]:
|
||||
PrintHardeningGuides.print_ver_changelog(policy_name)
|
||||
PrintHardeningGuides.ubuntu_2004_mint_20_client()
|
||||
sys.exit(retval)
|
||||
else:
|
||||
PrintHardeningGuides.supported_varient()
|
||||
sys.exit(retval)
|
||||
|
||||
|
||||
|
||||
@staticmethod
|
||||
def supported_varient() -> None:
|
||||
retval = exitcodes.GOOD
|
||||
print(" ")
|
||||
print("For current, community developed and legacy guides")
|
||||
print("check the website : https://www.ssh-audit.com/hardening_guides.html")
|
||||
print(" ")
|
||||
print("\033[1mSupported Server Configurations : \033[0m")
|
||||
print(" ")
|
||||
print(r"Amazon 2023 Server")
|
||||
print(r"Debian Bookworm Server")
|
||||
print(r"Debian Bullseye Server")
|
||||
print(r"Rocky 9 Server")
|
||||
print(r"Ubuntu 2404 Server")
|
||||
print(r"Ubuntu 2204 Server")
|
||||
print(r"Ubuntu 2004 Server")
|
||||
print(" ")
|
||||
print("\033[1mSupported Client Configurations : \033[0m")
|
||||
print(r"Amazon 2023 Client")
|
||||
print(r"Debian Bookworm Client")
|
||||
print(r"Mint 22 Client")
|
||||
print(r"Mint 21 Client")
|
||||
print(r"Mint 20 Client")
|
||||
print(r"Rocky 9 Client")
|
||||
print(r"Ubuntu 2404 Client")
|
||||
print(r"Ubuntu 2204 Client")
|
||||
print(r"Ubuntu 2004 Client")
|
||||
print(" ")
|
||||
print("\033[1mExample Usage : \033[0m ")
|
||||
print(r"python3 ssh-audit.py --get-hardening-guides Ubuntu 2404 Server")
|
||||
print(" ")
|
||||
sys.exit(retval)
|
||||
|
||||
|
||||
# Client Configurations
|
||||
|
||||
|
||||
@staticmethod
|
||||
def amazon_2023_client() -> None:
|
||||
print(" ")
|
||||
print("\033[1mRun the following in a terminal to harden the SSH client for the local user:\033[0m")
|
||||
print(" ")
|
||||
print(r'mkdir -p -m 0700 ~/.ssh; echo -e "\nHost *\n Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\n KexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,gss-group16-sha512-,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\n MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\n HostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n CASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n GSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\n HostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n PubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n" >> ~/.ssh/config')
|
||||
|
||||
@staticmethod
|
||||
def debian_bookworm_client() -> None:
|
||||
print(" ")
|
||||
print("\033[1mRun the following in a terminal to harden the SSH client for the local user:\033[0m")
|
||||
print(" ")
|
||||
print(r'mkdir -p -m 0700 ~/.ssh; echo -e "\nHost *\n Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\n KexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,gss-group16-sha512-,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\n MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\n RequiredRSASize 3072\n\n HostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n CASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n GSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\n HostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n PubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n" >> ~/.ssh/config')
|
||||
|
||||
@staticmethod
|
||||
def rocky_9_client() -> None:
|
||||
print("\033[1mRun the following in a terminal to harden the SSH client for the local user:\033[0m")
|
||||
print(" ")
|
||||
print(r'mkdir -p -m 0700 ~/.ssh; echo -e "\nHost *\n Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\n KexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,gss-group16-sha512-,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\n MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\n RequiredRSASize 3072\n\n HostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n CASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n GSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\n HostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n PubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n" >> ~/.ssh/config')
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_2404_mint_22_client() -> None:
|
||||
print("\033[1mRun the following in a terminal to harden the SSH client for the local user:\033[0m")
|
||||
print(" ")
|
||||
print(r'mkdir -p -m 0700 ~/.ssh; echo -e "\nHost *\n Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\n KexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256,gss-group16-sha512-,diffie-hellman-group16-sha512\n\n MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com\n\n RequiredRSASize 3072\n\n HostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n CASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n GSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\n HostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n PubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n" >> ~/.ssh/config')
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_2204_mint_21_client() -> None:
|
||||
print("\033[1mRun the following in a terminal to harden the SSH client for the local user:\033[0m")
|
||||
print(" ")
|
||||
print(r'mkdir -p -m 0700 ~/.ssh; echo -e "\nHost *\n Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\n KexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,gss-group16-sha512-,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\n MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\n HostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n CASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n GSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\n HostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n PubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\n" >> ~/.ssh/config')
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_2004_mint_20_client() -> None:
|
||||
print("\033[1mRun the following in a terminal to harden the SSH client for the local user:\033[0m")
|
||||
print(" ")
|
||||
print(r'mkdir -p -m 0700 ~/.ssh; echo -e "\nHost *\n Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr\n KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n MACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n HostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,sk-ssh-ed25519-cert-v01@openssh.com,rsa-sha2-256,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-512-cert-v01@openssh.com\n" >> ~/.ssh/config')
|
||||
|
||||
|
||||
# Server Configurations
|
||||
|
||||
|
||||
@staticmethod
|
||||
def server_modern_common() -> None:
|
||||
print("\033[1mRe-generate the ED25519 and RSA keys\033[0m")
|
||||
print(" ")
|
||||
print("rm /etc/ssh/ssh_host_*")
|
||||
print(r'ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""')
|
||||
print(r'ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""')
|
||||
print(" ")
|
||||
print("\033[1mRemove small Diffie-Hellman moduli\033[0m")
|
||||
print(" ")
|
||||
print(r"awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe")
|
||||
print("mv /etc/ssh/moduli.safe /etc/ssh/moduli")
|
||||
print(" ")
|
||||
print("\033[1mEnable the ED25519 and RSA keys\033[0m")
|
||||
print(" ")
|
||||
print("Enable the ED25519 and RSA HostKey directives in the /etc/ssh/sshd_config file:")
|
||||
print(" ")
|
||||
print(r'echo -e "\nHostKey /etc/ssh/ssh_host_ed25519_key\nHostKey /etc/ssh/ssh_host_rsa_key" >> /etc/ssh/sshd_config')
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def server_legacy_common() -> None:
|
||||
print("\033[1mRe-generate the ED25519 and RSA keys\033[0m")
|
||||
print(" ")
|
||||
print(r"rm /etc/ssh/ssh_host_*")
|
||||
print(r'ssh-keygen -t rsa -b 4096 -f /etc/ssh/ssh_host_rsa_key -N ""')
|
||||
print(r'ssh-keygen -t ed25519 -f /etc/ssh/ssh_host_ed25519_key -N ""')
|
||||
print(" ")
|
||||
print("\033[1mRemove small Diffie-Hellman moduli\033[0m")
|
||||
print(" ")
|
||||
print(r"awk '$5 >= 3071' /etc/ssh/moduli > /etc/ssh/moduli.safe")
|
||||
print("mv /etc/ssh/moduli.safe /etc/ssh/moduli")
|
||||
print(" ")
|
||||
print("\033[1mDisable the DSA and ECDSA host keys\033[0m")
|
||||
print(" ")
|
||||
print("Comment out the DSA and ECDSA HostKey directives in the /etc/ssh/sshd_config file:")
|
||||
print(" ")
|
||||
print(r"sed -i 's/^HostKey \/etc\/ssh\/ssh_host_\(dsa\|ecdsa\)_key$/\#HostKey \/etc\/ssh\/ssh_host_\1_key/g' /etc/ssh/sshd_config")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def debian_ubuntu_rate_throttling() -> None:
|
||||
print("\033[1mImplement connection rate throttling\033[0m")
|
||||
print(" ")
|
||||
print("iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set")
|
||||
print("iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 10 --hitcount 10 -j DROP")
|
||||
print("ip6tables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set")
|
||||
print("ip6tables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 10 --hitcount 10 -j DROP")
|
||||
print(" ")
|
||||
print("\033[1mEnable persistence of the iptables rules across server reboots: \033[0m")
|
||||
print(" ")
|
||||
print("DEBIAN_FRONTEND=noninteractive apt install -q -y netfilter-persistent iptables-persistent service netfilter-persistent save")
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_server_2404() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256,gss-group16-sha512-,diffie-hellman-group16-sha512\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com\n\nRequiredRSASize 3072\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_server_2204() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,gss-curve25519-sha256-,diffie-hellman-group16-sha512,gss-group16-sha512-,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_server_2004() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256,gss-group16-sha512-,diffie-hellman-group16-sha512\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com\n\nRequiredRSASize 3072\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def ubuntu_server_1804() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group16-sha512,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\nHostKeyAlgorithms ssh-ed25519,ssh-ed25519-cert-v01@openssh.com" >> /etc/ssh/sshd_config')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def bookworm_server() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,gss-curve25519-sha256-,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256,gss-group16-sha512-,diffie-hellman-group16-sha512\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com\n\nRequiredRSASize 3072\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def bullseye_server() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,gss-curve25519-sha256-,diffie-hellman-group16-sha512,gss-group16-sha512-,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256" > /etc/ssh/sshd_config.d/ssh-audit_hardening.conf')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("service ssh restart")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def rocky_9_server() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,gss-curve25519-sha256-,diffie-hellman-group16-sha512,gss-group16-sha512-,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nRequiredRSASize 3072\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n" > /etc/crypto-policies/back-ends/opensshserver.config')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("systemctl restart sshd")
|
||||
print(" ")
|
||||
print("\033[1mImplement connection rate throttling\033[0m")
|
||||
print(" ")
|
||||
print("firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 0 -p tcp --dport 22 -m state --state NEW -m recent --set")
|
||||
print("firewall-cmd --permanent --direct --add-rule ipv4 filter INPUT 1 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 10 --hitcount 10 -j DROP")
|
||||
print("firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 0 -p tcp --dport 22 -m state --state NEW -m recent --set")
|
||||
print("firewall-cmd --permanent --direct --add-rule ipv6 filter INPUT 1 -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 10 --hitcount 10 -j DROP")
|
||||
print(" ")
|
||||
print("\033[1mReload firewalld to enable new rules:\033[0m")
|
||||
print(" ")
|
||||
print("systemctl reload firewalld")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def amazon_server_2023() -> None:
|
||||
print("\033[1mRestrict supported key exchange, cipher, and MAC algorithms\033[0m")
|
||||
print(" ")
|
||||
print(r'echo -e "# Restrict key exchange, cipher, and MAC algorithms, as per sshaudit.com\n# hardening guide.\nKexAlgorithms sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,gss-curve25519-sha256-,diffie-hellman-group16-sha512,gss-group16-sha512-,diffie-hellman-group18-sha512,diffie-hellman-group-exchange-sha256\n\nCiphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-gcm@openssh.com,aes128-ctr\n\nMACs hmac-sha2-256-etm@openssh.com,hmac-sha2-512-etm@openssh.com,umac-128-etm@openssh.com\n\nHostKeyAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nCASignatureAlgorithms sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\nGSSAPIKexAlgorithms gss-curve25519-sha256-,gss-group16-sha512-\n\nHostbasedAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256\n\nPubkeyAcceptedAlgorithms sk-ssh-ed25519-cert-v01@openssh.com,ssh-ed25519-cert-v01@openssh.com,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-256-cert-v01@openssh.com,sk-ssh-ed25519@openssh.com,ssh-ed25519,rsa-sha2-512,rsa-sha2-256\n\n" > /etc/crypto-policies/back-ends/opensshserver.config')
|
||||
print(" ")
|
||||
print("\033[1mRestart OpenSSH server\033[0m")
|
||||
print(" ")
|
||||
print("systemctl restart sshd")
|
||||
print(" ")
|
||||
print("\033[1mImplement connection rate throttling\033[0m")
|
||||
print(" ")
|
||||
print("dnf install -y iptables")
|
||||
print("iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set")
|
||||
print("iptables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 10 --hitcount 10 -j DROP")
|
||||
print("ip6tables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --set")
|
||||
print("ip6tables -I INPUT -p tcp --dport 22 -m state --state NEW -m recent --update --seconds 10 --hitcount 10 -j DROP")
|
||||
print(" ")
|
||||
print("\033[1mEnable persistence of the iptables rules across server reboots:\033[0m")
|
||||
print(" ")
|
||||
print("dnf install -y iptables-services")
|
||||
print("iptables-save > /etc/sysconfig/iptables")
|
||||
print("ip6tables-save > /etc/sysconfig/ip6tables")
|
||||
print("systemctl enable iptables")
|
||||
print("systemctl enable ip6tables")
|
||||
print("systemctl start iptables")
|
||||
print("systemctl start ip6tables")
|
||||
print(" ")
|
||||
|
||||
@staticmethod
|
||||
def print_ver_changelog(policy_name: str) -> None:
|
||||
'''Prints ssh-audit version and change log for a supported configuration'''
|
||||
|
||||
for key_name, policy in BUILTIN_GUIDES.items():
|
||||
if policy_name in key_name:
|
||||
|
||||
policy_struct = policy
|
||||
policy_name_without_version = policy_name.split('(')[0]
|
||||
name = policy_name_without_version # pylint: disable=protected-access
|
||||
changelog_struct = policy_struct['changelog'] # pylint: disable=protected-access
|
||||
print(" ")
|
||||
print(f"\033[1mssh-audit Version : {VERSION}\033[0m")
|
||||
print(" ")
|
||||
print(f"\033[1mLocating configuration for {name}\033[0m")
|
||||
print(" ")
|
||||
print("\033[1mChange Log :\033[0m")
|
||||
for date, change in changelog_struct.items():
|
||||
print(f"\033[1m{date} : {change}\033[0m")
|
||||
print(" ")
|
||||
@@ -145,8 +145,10 @@ class OutputBuffer:
|
||||
self._print('head', s, line_ended)
|
||||
return self
|
||||
|
||||
def fail(self, s: str, line_ended: bool = True) -> 'OutputBuffer':
|
||||
def fail(self, s: str, line_ended: bool = True, write_now: bool = False) -> 'OutputBuffer':
|
||||
self._print('fail', s, line_ended)
|
||||
if write_now:
|
||||
self.write()
|
||||
return self
|
||||
|
||||
def warn(self, s: str, line_ended: bool = True) -> 'OutputBuffer':
|
||||
|
||||
+159
-153
@@ -23,9 +23,9 @@
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
"""
|
||||
import argparse
|
||||
import concurrent.futures
|
||||
import copy
|
||||
import getopt # pylint: disable=deprecated-module
|
||||
import json
|
||||
import multiprocessing
|
||||
import os
|
||||
@@ -33,6 +33,7 @@ import re
|
||||
import sys
|
||||
import traceback
|
||||
|
||||
|
||||
# pylint: disable=unused-import
|
||||
from typing import Dict, List, Set, Sequence, Tuple, Iterable # noqa: F401
|
||||
from typing import cast, Callable, Optional, Union, Any # noqa: F401
|
||||
@@ -52,6 +53,7 @@ from ssh_audit.gextest import GEXTest
|
||||
from ssh_audit.hostkeytest import HostKeyTest
|
||||
from ssh_audit.outputbuffer import OutputBuffer
|
||||
from ssh_audit.policy import Policy
|
||||
from ssh_audit.hardeningguides import PrintHardeningGuides
|
||||
from ssh_audit.product import Product
|
||||
from ssh_audit.protocol import Protocol
|
||||
from ssh_audit.software import Software
|
||||
@@ -82,61 +84,6 @@ if sys.platform == 'win32':
|
||||
# no_idna_workaround = True
|
||||
|
||||
|
||||
def usage(uout: OutputBuffer, err: Optional[str] = None) -> None:
|
||||
retval = exitcodes.GOOD
|
||||
p = os.path.basename(sys.argv[0])
|
||||
uout.head('# {} {}, https://github.com/jtesta/ssh-audit\n'.format(p, VERSION))
|
||||
if err is not None and len(err) > 0:
|
||||
uout.fail(err + '\n')
|
||||
retval = exitcodes.UNKNOWN_ERROR
|
||||
uout.info('usage: {0} [options] <host>\n'.format(p))
|
||||
uout.info(' -h, --help print this help')
|
||||
uout.info(' -1, --ssh1 force ssh version 1 only')
|
||||
uout.info(' -2, --ssh2 force ssh version 2 only')
|
||||
uout.info(' -4, --ipv4 enable IPv4 (order of precedence)')
|
||||
uout.info(' -6, --ipv6 enable IPv6 (order of precedence)')
|
||||
uout.info(' -b, --batch batch output')
|
||||
uout.info(' -c, --client-audit starts a server on port 2222 to audit client\n software config (use -p to change port;\n use -t to change timeout)')
|
||||
uout.info(' --conn-rate-test=N[:max_rate] perform a connection rate test (useful')
|
||||
uout.info(' for collecting metrics related to')
|
||||
uout.info(' susceptibility of the DHEat vuln).')
|
||||
uout.info(' Testing is conducted with N concurrent')
|
||||
uout.info(' sockets with an optional maximum rate')
|
||||
uout.info(' of connections per second.')
|
||||
uout.info(' -d, --debug debug output')
|
||||
uout.info(' --dheat=N[:kex[:e_len]] continuously perform the DHEat DoS attack')
|
||||
uout.info(' (CVE-2002-20001) against the target using N')
|
||||
uout.info(' concurrent sockets. Optionally, a specific')
|
||||
uout.info(' key exchange algorithm can be specified')
|
||||
uout.info(' instead of allowing it to be automatically')
|
||||
uout.info(' chosen. Additionally, a small length of')
|
||||
uout.info(' the fake e value sent to the server can')
|
||||
uout.info(' be chosen for a more efficient attack (such')
|
||||
uout.info(' as 4).')
|
||||
uout.info(' -g, --gex-test=<x[,y,...]> dh gex modulus size test')
|
||||
uout.info(' <min1:pref1:max1[,min2:pref2:max2,...]>')
|
||||
uout.info(' <x-y[:step]>')
|
||||
uout.info(' -j, --json JSON output (use -jj to enable indents)')
|
||||
uout.info(' -l, --level=<level> minimum output level (info|warn|fail)')
|
||||
uout.info(' -L, --list-policies list all the official, built-in policies. Use with -v')
|
||||
uout.info(' to view policy change logs.')
|
||||
uout.info(' --lookup=<alg1,alg2,...> looks up an algorithm(s) without\n connecting to a server')
|
||||
uout.info(' -M, --make-policy=<policy.txt> creates a policy based on the target server\n (i.e.: the target server has the ideal\n configuration that other servers should\n adhere to)')
|
||||
uout.info(' -m, --manual print the man page (Windows only)')
|
||||
uout.info(' -n, --no-colors disable colors (automatic when the NO_COLOR')
|
||||
uout.info(' environment variable is set)')
|
||||
uout.info(' -p, --port=<port> port to connect')
|
||||
uout.info(' -P, --policy=<policy.txt> run a policy test using the specified policy')
|
||||
uout.info(' --skip-rate-test skip the connection rate test during standard audits\n (used to safely infer whether the DHEat attack\n is viable)')
|
||||
uout.info(' -t, --timeout=<secs> timeout (in seconds) for connection and reading\n (default: 5)')
|
||||
uout.info(' -T, --targets=<hosts.txt> a file containing a list of target hosts (one\n per line, format HOST[:PORT]). Use -p/--port\n to set the default port for all hosts. Use\n --threads to control concurrent scans.')
|
||||
uout.info(' --threads=<threads> number of threads to use when scanning multiple\n targets (-T/--targets) (default: 32)')
|
||||
uout.info(' -v, --verbose verbose output')
|
||||
uout.sep()
|
||||
uout.write()
|
||||
sys.exit(retval)
|
||||
|
||||
|
||||
def output_algorithms(out: OutputBuffer, title: str, alg_db: Dict[str, Dict[str, List[List[Optional[str]]]]], alg_type: str, algorithms: List[str], unknown_algs: List[str], is_json_output: bool, program_retval: int, maxlen: int = 0, host_keys: Optional[Dict[str, Dict[str, Union[bytes, str, int]]]] = None, dh_modulus_sizes: Optional[Dict[str, int]] = None) -> int: # pylint: disable=too-many-arguments
|
||||
with out:
|
||||
for algorithm in algorithms:
|
||||
@@ -371,7 +318,7 @@ def output_recommendations(out: OutputBuffer, algs: Algorithms, algorithm_recomm
|
||||
notes = " (%s)" % notes
|
||||
|
||||
fm = '(rec) {0}{1}{2}-- {3} algorithm to {4}{5} '
|
||||
fn(fm.format(sg, name, p, alg_type, an, notes))
|
||||
fn(fm.format(sg, name, p, alg_type, an, notes)) # type: ignore[operator]
|
||||
|
||||
if not out.is_section_empty() and not is_json_output:
|
||||
if software is not None:
|
||||
@@ -823,7 +770,7 @@ def make_policy(aconf: AuditConf, banner: Optional['Banner'], kex: Optional['SSH
|
||||
print(err)
|
||||
|
||||
|
||||
def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[..., None]) -> 'AuditConf': # pylint: disable=too-many-statements
|
||||
def process_commandline(out: OutputBuffer, args: List[str]) -> 'AuditConf': # pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
aconf = AuditConf()
|
||||
|
||||
@@ -836,82 +783,118 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
aconf.colors = enable_colors
|
||||
out.use_colors = enable_colors
|
||||
|
||||
try:
|
||||
sopts = 'h1246M:p:P:jbcnvl:t:T:Lmdg:'
|
||||
lopts = ['help', 'ssh1', 'ssh2', 'ipv4', 'ipv6', 'make-policy=', 'port=', 'policy=', 'json', 'batch', 'client-audit', 'no-colors', 'verbose', 'level=', 'timeout=', 'targets=', 'list-policies', 'lookup=', 'threads=', 'manual', 'debug', 'gex-test=', 'dheat=', 'skip-rate-test', 'conn-rate-test=']
|
||||
opts, args = getopt.gnu_getopt(args, sopts, lopts)
|
||||
except getopt.GetoptError as err:
|
||||
usage_cb(out, str(err))
|
||||
aconf.ssh1, aconf.ssh2 = False, False
|
||||
host: str = ''
|
||||
oport: Optional[str] = None
|
||||
port: int = 0
|
||||
for o, a in opts:
|
||||
if o in ('-h', '--help'):
|
||||
usage_cb(out)
|
||||
elif o in ('-1', '--ssh1'):
|
||||
aconf.ssh1 = True
|
||||
elif o in ('-2', '--ssh2'):
|
||||
aconf.ssh2 = True
|
||||
elif o in ('-4', '--ipv4'):
|
||||
aconf.ipv4 = True
|
||||
elif o in ('-6', '--ipv6'):
|
||||
aconf.ipv6 = True
|
||||
elif o in ('-p', '--port'):
|
||||
oport = a
|
||||
elif o in ('-b', '--batch'):
|
||||
port: int = 22
|
||||
|
||||
parser = argparse.ArgumentParser(description="# {} {}, https://github.com/jtesta/ssh-audit".format(os.path.basename(sys.argv[0]), VERSION), allow_abbrev=False)
|
||||
|
||||
# Add short options to the parser
|
||||
parser.add_argument("-1", "--ssh1", action="store_true", dest="ssh1", default=False, help="force ssh version 1 only")
|
||||
parser.add_argument("-2", "--ssh2", action="store_true", dest="ssh2", default=False, help="force ssh version 2 only")
|
||||
parser.add_argument("-4", "--ipv4", action="store_true", dest="ipv4", default=False, help="enable IPv4 (order of precedence)")
|
||||
parser.add_argument("-6", "--ipv6", action="store_true", dest="ipv6", default=False, help="enable IPv6 (order of precedence)")
|
||||
parser.add_argument("-b", "--batch", action="store_true", dest="batch", default=False, help="batch output")
|
||||
parser.add_argument("-c", "--client-audit", action="store_true", dest="client_audit", default=False, help="starts a server on port 2222 to audit client software config (use -p to change port; use -t to change timeout)")
|
||||
parser.add_argument("-d", "--debug", action="store_true", dest="debug", default=False, help="enable debugging output")
|
||||
parser.add_argument("-g", "--gex-test", action="store", dest="gex_test", metavar="<min1:pref1:max1[,min2:pref2:max2,...]> / <x-y[:step]>", type=str, default=None, help="conducts a very customized Diffie-Hellman GEX modulus size test. Tests an array of minimum, preferred, and maximum values, or a range of values with an optional incremental step amount")
|
||||
parser.add_argument("-j", "--json", action="count", dest="json", default=0, help="enable JSON output (use -jj to enable indentation for better readability)")
|
||||
parser.add_argument("-l", "--level", action="store", dest="level", type=str, choices=["info", "warn", "fail"], default="info", help="minimum output level (default: %(default)s)")
|
||||
parser.add_argument("-L", "--list-policies", action="store_true", dest="list_policies", default=False, help="list all the official, built-in policies. Combine with -v to view policy change logs")
|
||||
parser.add_argument("-M", "--make-policy", action="store", dest="make_policy", metavar="custom_policy.txt", type=str, default=None, help="creates a policy based on the target server (i.e.: the target server has the ideal configuration that other servers should adhere to), and stores it in the file path specified")
|
||||
parser.add_argument("-m", "--manual", action="store_true", dest="manual", default=False, help="print the man page (Docker, PyPI, Snap, and Windows builds only)")
|
||||
parser.add_argument("-n", "--no-colors", action="store_true", dest="no_colors", default=False, help="disable colors (automatic when the NO_COLOR environment variable is set)")
|
||||
parser.add_argument("-P", "--policy", action="store", dest="policy", metavar="\"Built-In Policy Name\" / custom_policy.txt", type=str, default=None, help="run a policy test using the specified policy (use -L to see built-in policies, or specify filesystem path to custom policy created by -M)")
|
||||
parser.add_argument("-p", "--port", action="store", dest="oport", metavar="N", type=int, default=None, help="the TCP port to connect to (or to listen on when -c is used)")
|
||||
parser.add_argument("-T", "--targets", action="store", dest="targets", metavar="targets.txt", type=str, default=None, help="a file containing a list of target hosts (one per line, format HOST[:PORT]). Use -p/--port to set the default port for all hosts. Use --threads to control concurrent scans")
|
||||
parser.add_argument("-t", "--timeout", action="store", dest="timeout", metavar="N", type=int, default=5, help="timeout (in seconds) for connection and reading (default: %(default)s)")
|
||||
parser.add_argument("-v", "--verbose", action="store_true", dest="verbose", default=False, help="enable verbose output")
|
||||
|
||||
# Add long options to the parser
|
||||
parser.add_argument("--conn-rate-test", action="store", dest="conn_rate_test", metavar="N[:max_rate]", type=str, default=None, help="perform a connection rate test (useful for collecting metrics related to susceptibility of the DHEat vuln). Testing is conducted with N concurrent sockets with an optional maximum rate of connections per second")
|
||||
parser.add_argument("--dheat", action="store", dest="dheat", metavar="N[:kex[:e_len]]", type=str, default=None, help="continuously perform the DHEat DoS attack (CVE-2002-20001) against the target using N concurrent sockets. Optionally, a specific key exchange algorithm can be specified instead of allowing it to be automatically chosen. Additionally, a small length of the fake e value sent to the server can be chosen for a more efficient attack (such as 4).")
|
||||
parser.add_argument("--lookup", action="store", dest="lookup", metavar="alg1[,alg2,...]", type=str, default=None, help="looks up an algorithm(s) without connecting to a server.")
|
||||
parser.add_argument("--skip-rate-test", action="store_true", dest="skip_rate_test", default=False, help="skip the connection rate test during standard audits (used to safely infer whether the DHEat attack is viable)")
|
||||
parser.add_argument("--threads", action="store", dest="threads", metavar="N", type=int, default=32, help="number of threads to use when scanning multiple targets (-T/--targets) (default: %(default)s)")
|
||||
|
||||
# Print Suggested Configurations from : https://www.ssh-audit.com/hardening_guides.html
|
||||
parser.add_argument("--get-hardening-guides", nargs="*", action="append", metavar="OS Ver Client/Server", dest="get_hardening_guides", type=str, default=None, help="Print suggested server or client configurations. Usage Example : Ubuntu 2404 Server")
|
||||
parser.add_argument("--list-hardening-guides", action="store_true", dest="list_hardening_guides", default=False, help="List supported server and client configurations.")
|
||||
|
||||
# The mandatory target option. Or rather, mandatory when -L, -T, --lookup or --print-config are not used.
|
||||
parser.add_argument("host", nargs="?", action="store", type=str, default="", help="target hostname or IPv4/IPv6 address")
|
||||
|
||||
# If no arguments were given, print the help and exit.
|
||||
if len(args) < 1:
|
||||
parser.print_help()
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
oport: Optional[int] = None
|
||||
try:
|
||||
argument = parser.parse_args(args=args)
|
||||
|
||||
if argument.list_hardening_guides is True:
|
||||
PrintHardeningGuides.supported_varient()
|
||||
|
||||
if argument.get_hardening_guides is not None:
|
||||
print_guides = (getattr(argument, 'get_hardening_guides'))[0]
|
||||
arg_len = len(print_guides)
|
||||
if arg_len <= 2:
|
||||
user_arg = ""
|
||||
for i in range(arg_len):
|
||||
user_arg = user_arg + " " + str(print_guides[i])
|
||||
print(f"\033[1mUnsupported configuration : {user_arg}\033[0m")
|
||||
PrintHardeningGuides.supported_varient()
|
||||
else:
|
||||
print_guides = (getattr(argument, 'get_hardening_guides'))[0]
|
||||
os_type = print_guides[0]
|
||||
os_ver = print_guides[1]
|
||||
clientserver = print_guides[2]
|
||||
|
||||
PrintHardeningGuides(os_type, os_ver, clientserver)
|
||||
|
||||
|
||||
# Set simple flags.
|
||||
aconf.client_audit = argument.client_audit
|
||||
aconf.ipv4 = argument.ipv4
|
||||
aconf.ipv6 = argument.ipv6
|
||||
aconf.level = argument.level
|
||||
aconf.list_policies = argument.list_policies
|
||||
aconf.manual = argument.manual
|
||||
aconf.skip_rate_test = argument.skip_rate_test
|
||||
aconf.ssh1 = argument.ssh1
|
||||
aconf.ssh2 = argument.ssh2
|
||||
oport = argument.oport
|
||||
|
||||
if argument.batch is True:
|
||||
aconf.batch = True
|
||||
aconf.verbose = True
|
||||
elif o in ('-c', '--client-audit'):
|
||||
aconf.client_audit = True
|
||||
elif o in ('-j', '--json'):
|
||||
if aconf.json: # If specified twice, enable indent printing.
|
||||
aconf.json_print_indent = True
|
||||
else:
|
||||
aconf.json = True
|
||||
elif o in ('-v', '--verbose'):
|
||||
aconf.verbose = True
|
||||
out.verbose = True
|
||||
elif o in ('-l', '--level'):
|
||||
if a not in ('info', 'warn', 'fail'):
|
||||
usage_cb(out, 'level {} is not valid'.format(a))
|
||||
aconf.level = a
|
||||
elif o in ('-t', '--timeout'):
|
||||
aconf.timeout = float(a)
|
||||
aconf.timeout_set = True
|
||||
elif o in ('-M', '--make-policy'):
|
||||
aconf.make_policy = True
|
||||
aconf.policy_file = a
|
||||
elif o in ('-P', '--policy'):
|
||||
aconf.policy_file = a
|
||||
elif o in ('-T', '--targets'):
|
||||
aconf.target_file = a
|
||||
|
||||
# If we're on Windows, and we can't use the idna workaround, force only one thread to be used (otherwise a crash would occur).
|
||||
# if no_idna_workaround:
|
||||
# print("\nWARNING: the idna module was not found on this system, thus only single-threaded scanning will be done (this is a workaround for this Windows-specific crash: https://github.com/python/cpython/issues/73474). Multi-threaded scanning can be enabled by installing the idna module (pip install idna).\n")
|
||||
# aconf.threads = 1
|
||||
elif o == '--threads':
|
||||
aconf.threads = int(a)
|
||||
# if no_idna_workaround:
|
||||
# aconf.threads = 1
|
||||
elif o in ('-L', '--list-policies'):
|
||||
aconf.list_policies = True
|
||||
elif o == '--lookup':
|
||||
aconf.lookup = a
|
||||
elif o in ('-m', '--manual'):
|
||||
aconf.manual = True
|
||||
elif o in ('-d', '--debug'):
|
||||
# If one -j was given, turn on JSON output. If -jj was given, enable indentation.
|
||||
aconf.json = argument.json > 0
|
||||
if argument.json > 1:
|
||||
aconf.json_print_indent = True
|
||||
|
||||
if argument.conn_rate_test is not None:
|
||||
aconf.conn_rate_test = argument.conn_rate_test
|
||||
|
||||
if argument.debug is True:
|
||||
aconf.debug = True
|
||||
out.debug = True
|
||||
elif o in ('-g', '--gex-test'):
|
||||
|
||||
if argument.dheat is not None:
|
||||
aconf.dheat = argument.dheat
|
||||
|
||||
if argument.gex_test is not None:
|
||||
dh_gex = argument.gex_test
|
||||
permitted_syntax = get_permitted_syntax_for_gex_test()
|
||||
|
||||
if not any(re.search(regex_str, a) for regex_str in permitted_syntax.values()):
|
||||
usage_cb(out, '{} {} is not valid'.format(o, a))
|
||||
if not any(re.search(regex_str, dh_gex) for regex_str in permitted_syntax.values()):
|
||||
out.fail('{} is not valid'.format(dh_gex), write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
if re.search(permitted_syntax['RANGE'], a):
|
||||
extracted_digits = re.findall(r'\d+', a)
|
||||
if re.search(permitted_syntax['RANGE'], dh_gex):
|
||||
extracted_digits = re.findall(r'\d+', dh_gex)
|
||||
bits_left_bound = int(extracted_digits[0])
|
||||
bits_right_bound = int(extracted_digits[1])
|
||||
|
||||
@@ -920,27 +903,52 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
bits_step = int(extracted_digits[2])
|
||||
|
||||
if bits_step <= 0:
|
||||
usage_cb(out, '{} {} is not valid'.format(o, bits_step))
|
||||
out.fail('the step field cannot be 0 or less: {}'.format(bits_step), write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
if all(x < 0 for x in (bits_left_bound, bits_right_bound)):
|
||||
usage_cb(out, '{} {} {} is not valid'.format(o, bits_left_bound, bits_right_bound))
|
||||
out.fail('{} {} {} is not valid'.format(dh_gex, bits_left_bound, bits_right_bound), write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
aconf.gex_test = a
|
||||
elif o == '--dheat':
|
||||
aconf.dheat = a
|
||||
elif o == '--skip-rate-test':
|
||||
aconf.skip_rate_test = True
|
||||
elif o == '--conn-rate-test':
|
||||
aconf.conn_rate_test = a
|
||||
aconf.gex_test = dh_gex
|
||||
|
||||
if argument.lookup is not None:
|
||||
aconf.lookup = argument.lookup
|
||||
|
||||
if len(args) == 0 and aconf.client_audit is False and aconf.target_file is None and aconf.list_policies is False and aconf.lookup == '' and aconf.manual is False:
|
||||
usage_cb(out)
|
||||
if argument.make_policy is not None:
|
||||
aconf.make_policy = True
|
||||
aconf.policy_file = argument.make_policy
|
||||
|
||||
if argument.policy is not None:
|
||||
aconf.policy_file = argument.policy
|
||||
|
||||
if argument.targets is not None:
|
||||
aconf.target_file = argument.targets
|
||||
|
||||
if argument.threads is not None:
|
||||
aconf.threads = argument.threads
|
||||
|
||||
if argument.timeout is not None:
|
||||
aconf.timeout = float(argument.timeout)
|
||||
aconf.timeout_set = True
|
||||
|
||||
if argument.verbose is True:
|
||||
aconf.verbose = True
|
||||
out.verbose = True
|
||||
|
||||
except argparse.ArgumentError as err:
|
||||
out.fail(str(err), write_now=True)
|
||||
parser.print_help()
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
if argument.host == "" and argument.client_audit is False and argument.targets is None and argument.list_policies is False and argument.lookup is None and argument.manual is False and argument.get_hardening_guides is None:
|
||||
out.fail("target host must be specified, unless -c, -m, -L, -T, --lookup or --print-configuration are used", write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
if aconf.manual:
|
||||
return aconf
|
||||
|
||||
if aconf.lookup != '':
|
||||
if aconf.lookup != "":
|
||||
return aconf
|
||||
|
||||
if aconf.list_policies:
|
||||
@@ -949,25 +957,26 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
|
||||
if aconf.client_audit is False and aconf.target_file is None:
|
||||
if oport is not None:
|
||||
host = args[0]
|
||||
host = argument.host
|
||||
else:
|
||||
host, port = Utils.parse_host_and_port(args[0])
|
||||
if not host and aconf.target_file is None:
|
||||
usage_cb(out, 'host is empty')
|
||||
host, port = Utils.parse_host_and_port(argument.host)
|
||||
|
||||
if port == 0 and oport is None:
|
||||
if aconf.client_audit: # The default port to listen on during a client audit is 2222.
|
||||
if not host and aconf.target_file is None:
|
||||
out.fail("target host is not specified", write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
if oport is None and aconf.client_audit: # The default port to listen on during a client audit is 2222.
|
||||
port = 2222
|
||||
else:
|
||||
port = 22
|
||||
|
||||
if oport is not None:
|
||||
port = Utils.parse_int(oport)
|
||||
if port <= 0 or port > 65535:
|
||||
usage_cb(out, 'port {} is not valid'.format(oport))
|
||||
if port < 1 or port > 65535:
|
||||
out.fail("port must be greater than 0 and less than 65535: {}".format(oport), write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
aconf.host = host
|
||||
aconf.port = port
|
||||
|
||||
if not (aconf.ssh1 or aconf.ssh2):
|
||||
aconf.ssh1, aconf.ssh2 = True, True
|
||||
|
||||
@@ -996,20 +1005,17 @@ def process_commandline(out: OutputBuffer, args: List[str], usage_cb: Callable[.
|
||||
try:
|
||||
aconf.policy = Policy(policy_file=aconf.policy_file, json_output=aconf.json)
|
||||
except Exception as e:
|
||||
out.fail("Error while loading policy file: %s: %s" % (str(e), traceback.format_exc()))
|
||||
out.write()
|
||||
out.fail("Error while loading policy file: %s: %s" % (str(e), traceback.format_exc()), write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
# If the user wants to do a client audit, but provided a server policy, terminate.
|
||||
if aconf.client_audit and aconf.policy.is_server_policy():
|
||||
out.fail("Error: client audit selected, but server policy provided.")
|
||||
out.write()
|
||||
out.fail("Error: client audit selected, but server policy provided.", write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
# If the user wants to do a server audit, but provided a client policy, terminate.
|
||||
if aconf.client_audit is False and aconf.policy.is_server_policy() is False:
|
||||
out.fail("Error: server audit selected, but client policy provided.")
|
||||
out.write()
|
||||
out.fail("Error: server audit selected, but client policy provided.", write_now=True)
|
||||
sys.exit(exitcodes.UNKNOWN_ERROR)
|
||||
|
||||
return aconf
|
||||
@@ -1499,7 +1505,7 @@ def run_gex_granular_modulus_size_test(out: OutputBuffer, s: 'SSH_Socket', kex:
|
||||
|
||||
def main() -> int:
|
||||
out = OutputBuffer()
|
||||
aconf = process_commandline(out, sys.argv[1:], usage)
|
||||
aconf = process_commandline(out, sys.argv[1:])
|
||||
|
||||
# If we're on Windows, but the colorama module could not be imported, print a warning if we're in verbose mode.
|
||||
if (sys.platform == 'win32') and ('colorama' not in sys.modules):
|
||||
|
||||
@@ -129,7 +129,7 @@ class Utils:
|
||||
return -1.0
|
||||
|
||||
@staticmethod
|
||||
def parse_host_and_port(host_and_port: str, default_port: int = 0) -> Tuple[str, int]:
|
||||
def parse_host_and_port(host_and_port: str, default_port: int = 22) -> Tuple[str, int]:
|
||||
'''Parses a string into a tuple of its host and port. The port is 0 if not specified.'''
|
||||
host = host_and_port
|
||||
port = default_port
|
||||
|
||||
@@ -8,7 +8,6 @@ class TestAuditConf:
|
||||
def init(self, ssh_audit):
|
||||
self.AuditConf = ssh_audit.AuditConf
|
||||
self.OutputBuffer = ssh_audit.OutputBuffer()
|
||||
self.usage = ssh_audit.usage
|
||||
self.process_commandline = process_commandline
|
||||
|
||||
@staticmethod
|
||||
@@ -107,7 +106,7 @@ class TestAuditConf:
|
||||
|
||||
def test_audit_conf_process_commandline(self):
|
||||
# pylint: disable=too-many-statements
|
||||
c = lambda x: self.process_commandline(self.OutputBuffer, x.split(), self.usage) # noqa
|
||||
c = lambda x: self.process_commandline(self.OutputBuffer, x.split()) # noqa
|
||||
with pytest.raises(SystemExit):
|
||||
conf = c('')
|
||||
with pytest.raises(SystemExit):
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
import pytest
|
||||
from ssh_audit.ssh_audit import process_commandline
|
||||
|
||||
|
||||
# pylint: disable=attribute-defined-outside-init
|
||||
class TestHardeningGuides:
|
||||
@pytest.fixture(autouse=True)
|
||||
def init(self, ssh_audit):
|
||||
self.OutputBuffer = ssh_audit.OutputBuffer()
|
||||
self.process_commandline = process_commandline
|
||||
|
||||
@staticmethod
|
||||
def _test_conf(conf, **kwargs):
|
||||
options = {
|
||||
'get_hardening_guides': '',
|
||||
}
|
||||
for k, v in kwargs.items():
|
||||
options[k] = v
|
||||
assert conf.get_hardening_guides == options['get_hardening_guides']
|
||||
|
||||
def test_printconfig_conf_process_commandline(self):
|
||||
# pylint: disable=too-many-statements
|
||||
c = lambda x: self.process_commandline(self.OutputBuffer, x.split()) # noqa
|
||||
with pytest.raises(SystemExit):
|
||||
conf = c('')
|
||||
with pytest.raises(SystemExit):
|
||||
conf = c('--get-hardening-guides')
|
||||
self._test_conf(conf)
|
||||
with pytest.raises(SystemExit):
|
||||
conf = c('--list-hardening-guides')
|
||||
self._test_conf(conf)
|
||||
|
||||
for vendor in ["Amazon", "Debian", "Rocky", "Mint", "Ubuntu", "NoOS", " "]:
|
||||
vendor = vendor
|
||||
for os_ver in ["2404", "2204", "2004", "1804", "2023", "22", "21", "20", "9", "Bookworm", "Bullseye", "NoVersion", ""]:
|
||||
os_ver = os_ver
|
||||
for cs_type in ["Client", "Server", "Mistake", ""]:
|
||||
cs_type = cs_type
|
||||
with pytest.raises(SystemExit):
|
||||
conf = c(f'--get-hardening-guides {vendor} {os_ver} {cs_type}')
|
||||
self._test_conf(conf)
|
||||
Reference in New Issue
Block a user