Page MenuHomeVyOS Platform

No OneTemporary

Size
30 KB
Referenced Files
None
Subscribers
None
diff --git a/data/templates/ssh/sshd_config.j2 b/data/templates/ssh/sshd_config.j2
index 650fd25e6..2cf0494c4 100644
--- a/data/templates/ssh/sshd_config.j2
+++ b/data/templates/ssh/sshd_config.j2
@@ -1,107 +1,112 @@
### Autogenerated by service_ssh.py ###
# https://linux.die.net/man/5/sshd_config
#
# Non-configurable defaults
#
Protocol 2
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
SyslogFacility AUTH
LoginGraceTime 120
StrictModes yes
PubkeyAuthentication yes
IgnoreRhosts yes
HostbasedAuthentication no
PermitEmptyPasswords no
X11Forwarding yes
X11DisplayOffset 10
PrintMotd no
PrintLastLog yes
TCPKeepAlive yes
Banner /etc/issue.net
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes
PermitRootLogin no
PidFile /run/sshd/sshd.pid
AddressFamily any
DebianBanner no
KbdInteractiveAuthentication no
#
# User configurable section
#
# Look up remote host name and check that the resolved host name for the remote IP
# address maps back to the very same IP address.
UseDNS {{ "no" if disable_host_validation is vyos_defined else "yes" }}
# Specifies the port number that sshd(8) listens on
{% for value in port %}
Port {{ value }}
{% endfor %}
# Gives the verbosity level that is used when logging messages from sshd
LogLevel {{ loglevel | upper }}
# Specifies whether password authentication is allowed
PasswordAuthentication {{ "no" if disable_password_authentication is vyos_defined else "yes" }}
{% if listen_address is vyos_defined %}
# Specifies the local addresses sshd should listen on
{% for address in listen_address %}
ListenAddress {{ address }}
{% endfor %}
{% endif %}
{% if ciphers is vyos_defined %}
# Specifies the ciphers allowed for protocol version 2
Ciphers {{ ciphers | join(',') }}
{% endif %}
{% if hostkey_algorithm is vyos_defined %}
# Specifies the available Host Key signature algorithms
HostKeyAlgorithms {{ hostkey_algorithm | join(',') }}
{% endif %}
+{% if pubkey_accepted_algorithm is vyos_defined %}
+# Specifies the available PubKey signature algorithms
+PubkeyAcceptedAlgorithms {{ pubkey_accepted_algorithm | join(',') }}
+{% endif %}
+
{% if mac is vyos_defined %}
# Specifies the available MAC (message authentication code) algorithms
MACs {{ mac | join(',') }}
{% endif %}
{% if key_exchange is vyos_defined %}
# Specifies the available Key Exchange algorithms
KexAlgorithms {{ key_exchange | join(',') }}
{% endif %}
{% if access_control is vyos_defined %}
{% if access_control.allow.user is vyos_defined %}
# If specified, login is allowed only for user names that match
AllowUsers {{ access_control.allow.user | join(' ') }}
{% endif %}
{% if access_control.allow.group is vyos_defined %}
# If specified, login is allowed only for users whose primary group or supplementary group list matches
AllowGroups {{ access_control.allow.group | join(' ') }}
{% endif %}
{% if access_control.deny.user is vyos_defined %}
# Login is disallowed for user names that match
DenyUsers {{ access_control.deny.user | join(' ') }}
{% endif %}
{% if access_control.deny.group is vyos_defined %}
# Login is disallowed for users whose primary group or supplementary group list matches
DenyGroups {{ access_control.deny.group | join(' ') }}
{% endif %}
{% endif %}
{% if client_keepalive_interval is vyos_defined %}
# Sets a timeout interval in seconds after which if no data has been received from the client,
# sshd(8) will send a message through the encrypted channel to request a response from the client
ClientAliveInterval {{ client_keepalive_interval }}
{% endif %}
{% if rekey.data is vyos_defined %}
RekeyLimit {{ rekey.data }}M {{ rekey.time + 'M' if rekey.time is vyos_defined }}
{% endif %}
diff --git a/interface-definitions/service_ssh.xml.in b/interface-definitions/service_ssh.xml.in
index d9eee1ab8..221e451d1 100644
--- a/interface-definitions/service_ssh.xml.in
+++ b/interface-definitions/service_ssh.xml.in
@@ -1,270 +1,283 @@
<?xml version="1.0"?>
<interfaceDefinition>
<node name="service">
<properties>
<help>System services</help>
</properties>
<children>
<node name="ssh" owner="${vyos_conf_scripts_dir}/service_ssh.py">
<properties>
<help>Secure Shell (SSH)</help>
<priority>1000</priority>
</properties>
<children>
<node name="access-control">
<properties>
<help>SSH user/group access controls</help>
</properties>
<children>
<node name="allow">
<properties>
<help>Allow user/group SSH access</help>
</properties>
<children>
#include <include/ssh-group.xml.i>
#include <include/ssh-user.xml.i>
</children>
</node>
<node name="deny">
<properties>
<help>Deny user/group SSH access</help>
</properties>
<children>
#include <include/ssh-group.xml.i>
#include <include/ssh-user.xml.i>
</children>
</node>
</children>
</node>
<leafNode name="ciphers">
<properties>
<help>Allowed ciphers</help>
<completionHelp>
<!-- generated by ssh -Q cipher | tr '\n' ' ' as this will not change dynamically -->
<list>3des-cbc aes128-cbc aes192-cbc aes256-cbc rijndael-cbc@lysator.liu.se aes128-ctr aes192-ctr aes256-ctr aes128-gcm@openssh.com aes256-gcm@openssh.com chacha20-poly1305@openssh.com</list>
</completionHelp>
<constraint>
<regex>(3des-cbc|aes128-cbc|aes192-cbc|aes256-cbc|rijndael-cbc@lysator.liu.se|aes128-ctr|aes192-ctr|aes256-ctr|aes128-gcm@openssh.com|aes256-gcm@openssh.com|chacha20-poly1305@openssh.com)</regex>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="disable-host-validation">
<properties>
<help>Disable IP Address to Hostname lookup</help>
<valueless/>
</properties>
</leafNode>
<leafNode name="disable-password-authentication">
<properties>
<help>Disable password-based authentication</help>
<valueless/>
</properties>
</leafNode>
<node name="dynamic-protection">
<properties>
<help>Allow dynamic protection</help>
</properties>
<children>
<leafNode name="block-time">
<properties>
<help>Block source IP in seconds. Subsequent blocks increase by a factor of 1.5</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Time interval in seconds for blocking</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
<defaultValue>120</defaultValue>
</leafNode>
<leafNode name="detect-time">
<properties>
<help>Remember source IP in seconds before reset their score</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Time interval in seconds</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
<defaultValue>1800</defaultValue>
</leafNode>
<leafNode name="threshold">
<properties>
<help>Block source IP when their cumulative attack score exceeds threshold</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Threshold score</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
<defaultValue>30</defaultValue>
</leafNode>
<leafNode name="allow-from">
<properties>
<help>Always allow inbound connections from these systems</help>
<valueHelp>
<format>ipv4</format>
<description>Address to match against</description>
</valueHelp>
<valueHelp>
<format>ipv4net</format>
<description>IPv4 address and prefix length</description>
</valueHelp>
<valueHelp>
<format>ipv6</format>
<description>IPv6 address to match against</description>
</valueHelp>
<valueHelp>
<format>ipv6net</format>
<description>IPv6 address and prefix length</description>
</valueHelp>
<constraint>
<validator name="ip-address"/>
<validator name="ip-prefix"/>
</constraint>
<multi/>
</properties>
</leafNode>
</children>
</node>
<leafNode name="hostkey-algorithm">
<properties>
<help>Allowed host key signature algorithms</help>
<completionHelp>
<!-- generated by ssh -Q HostKeyAlgorithms | tr '\n' ' ' as this will not change dynamically -->
<list>ssh-ed25519 ssh-ed25519-cert-v01@openssh.com sk-ssh-ed25519@openssh.com sk-ssh-ed25519-cert-v01@openssh.com ssh-rsa rsa-sha2-256 rsa-sha2-512 ssh-dss ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 sk-ecdsa-sha2-nistp256@openssh.com webauthn-sk-ecdsa-sha2-nistp256@openssh.com ssh-rsa-cert-v01@openssh.com rsa-sha2-256-cert-v01@openssh.com rsa-sha2-512-cert-v01@openssh.com ssh-dss-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-ecdsa-sha2-nistp256-cert-v01@openssh.com</list>
</completionHelp>
<multi/>
<constraint>
<regex>(ssh-ed25519|ssh-ed25519-cert-v01@openssh.com|sk-ssh-ed25519@openssh.com|sk-ssh-ed25519-cert-v01@openssh.com|ssh-rsa|rsa-sha2-256|rsa-sha2-512|ssh-dss|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|sk-ecdsa-sha2-nistp256@openssh.com|webauthn-sk-ecdsa-sha2-nistp256@openssh.com|ssh-rsa-cert-v01@openssh.com|rsa-sha2-256-cert-v01@openssh.com|rsa-sha2-512-cert-v01@openssh.com|ssh-dss-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-ecdsa-sha2-nistp256-cert-v01@openssh.com)</regex>
</constraint>
</properties>
</leafNode>
+ <leafNode name="pubkey-accepted-algorithm">
+ <properties>
+ <help>Allowed pubkey signature algorithms</help>
+ <completionHelp>
+ <!-- generated by ssh -Q PubkeyAcceptedAlgorithms | tr '\n' ' ' as this will not change dynamically -->
+ <list>ssh-ed25519 ssh-ed25519-cert-v01@openssh.com sk-ssh-ed25519@openssh.com sk-ssh-ed25519-cert-v01@openssh.com ecdsa-sha2-nistp256 ecdsa-sha2-nistp256-cert-v01@openssh.com ecdsa-sha2-nistp384 ecdsa-sha2-nistp384-cert-v01@openssh.com ecdsa-sha2-nistp521 ecdsa-sha2-nistp521-cert-v01@openssh.com sk-ecdsa-sha2-nistp256@openssh.com sk-ecdsa-sha2-nistp256-cert-v01@openssh.com webauthn-sk-ecdsa-sha2-nistp256@openssh.com ssh-dss ssh-dss-cert-v01@openssh.com ssh-rsa ssh-rsa-cert-v01@openssh.com rsa-sha2-256 rsa-sha2-256-cert-v01@openssh.com rsa-sha2-512 rsa-sha2-512-cert-v01@openssh.com</list>
+ </completionHelp>
+ <multi/>
+ <constraint>
+ <regex>(ssh-ed25519|ssh-ed25519-cert-v01@openssh.com|sk-ssh-ed25519@openssh.com|sk-ssh-ed25519-cert-v01@openssh.com|ecdsa-sha2-nistp256|ecdsa-sha2-nistp256-cert-v01@openssh.com|ecdsa-sha2-nistp384|ecdsa-sha2-nistp384-cert-v01@openssh.com|ecdsa-sha2-nistp521|ecdsa-sha2-nistp521-cert-v01@openssh.com|sk-ecdsa-sha2-nistp256@openssh.com|sk-ecdsa-sha2-nistp256-cert-v01@openssh.com|webauthn-sk-ecdsa-sha2-nistp256@openssh.com|ssh-dss|ssh-dss-cert-v01@openssh.com|ssh-rsa|ssh-rsa-cert-v01@openssh.com|rsa-sha2-256|rsa-sha2-256-cert-v01@openssh.com|rsa-sha2-512|rsa-sha2-512-cert-v01@openssh.com)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
<leafNode name="key-exchange">
<properties>
<help>Allowed key exchange (KEX) algorithms</help>
<completionHelp>
<!-- generated by ssh -Q kex | tr '\n' ' ' as this will not change dynamically -->
<list>diffie-hellman-group1-sha1 diffie-hellman-group14-sha1 diffie-hellman-group14-sha256 diffie-hellman-group16-sha512 diffie-hellman-group18-sha512 diffie-hellman-group-exchange-sha1 diffie-hellman-group-exchange-sha256 ecdh-sha2-nistp256 ecdh-sha2-nistp384 ecdh-sha2-nistp521 curve25519-sha256 curve25519-sha256@libssh.org</list>
</completionHelp>
<multi/>
<constraint>
<regex>(diffie-hellman-group1-sha1|diffie-hellman-group14-sha1|diffie-hellman-group14-sha256|diffie-hellman-group16-sha512|diffie-hellman-group18-sha512|diffie-hellman-group-exchange-sha1|diffie-hellman-group-exchange-sha256|ecdh-sha2-nistp256|ecdh-sha2-nistp384|ecdh-sha2-nistp521|curve25519-sha256|curve25519-sha256@libssh.org)</regex>
</constraint>
</properties>
</leafNode>
#include <include/listen-address.xml.i>
<leafNode name="loglevel">
<properties>
<help>Log level</help>
<completionHelp>
<list>quiet fatal error info verbose</list>
</completionHelp>
<valueHelp>
<format>quiet</format>
<description>stay silent</description>
</valueHelp>
<valueHelp>
<format>fatal</format>
<description>log fatals only</description>
</valueHelp>
<valueHelp>
<format>error</format>
<description>log errors and fatals only</description>
</valueHelp>
<valueHelp>
<format>info</format>
<description>default log level</description>
</valueHelp>
<valueHelp>
<format>verbose</format>
<description>enable logging of failed login attempts</description>
</valueHelp>
<constraint>
<regex>(quiet|fatal|error|info|verbose)</regex>
</constraint>
</properties>
<defaultValue>info</defaultValue>
</leafNode>
<leafNode name="mac">
<properties>
<help>Allowed message authentication code (MAC) algorithms</help>
<completionHelp>
<!-- generated by ssh -Q mac | tr '\n' ' ' as this will not change dynamically -->
<list>hmac-sha1 hmac-sha1-96 hmac-sha2-256 hmac-sha2-512 hmac-md5 hmac-md5-96 umac-64@openssh.com umac-128@openssh.com hmac-sha1-etm@openssh.com hmac-sha1-96-etm@openssh.com hmac-sha2-256-etm@openssh.com hmac-sha2-512-etm@openssh.com hmac-md5-etm@openssh.com hmac-md5-96-etm@openssh.com umac-64-etm@openssh.com umac-128-etm@openssh.com</list>
</completionHelp>
<constraint>
<regex>(hmac-sha1|hmac-sha1-96|hmac-sha2-256|hmac-sha2-512|hmac-md5|hmac-md5-96|umac-64@openssh.com|umac-128@openssh.com|hmac-sha1-etm@openssh.com|hmac-sha1-96-etm@openssh.com|hmac-sha2-256-etm@openssh.com|hmac-sha2-512-etm@openssh.com|hmac-md5-etm@openssh.com|hmac-md5-96-etm@openssh.com|umac-64-etm@openssh.com|umac-128-etm@openssh.com)</regex>
</constraint>
<multi/>
</properties>
</leafNode>
<leafNode name="port">
<properties>
<help>Port for SSH service</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Numeric IP port</description>
</valueHelp>
<multi/>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
<defaultValue>22</defaultValue>
</leafNode>
<node name="rekey">
<properties>
<help>SSH session rekey limit</help>
</properties>
<children>
<leafNode name="data">
<properties>
<help>Threshold data in megabytes</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Megabytes</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
</leafNode>
<leafNode name="time">
<properties>
<help>Threshold time in minutes</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Minutes</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
</leafNode>
</children>
</node>
<leafNode name="client-keepalive-interval">
<properties>
<help>Enable transmission of keepalives from server to client</help>
<valueHelp>
<format>u32:1-65535</format>
<description>Time interval in seconds for keepalive message</description>
</valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
</leafNode>
#include <include/vrf-multi.xml.i>
</children>
</node>
</children>
</node>
</interfaceDefinition>
diff --git a/smoketest/scripts/cli/test_service_ssh.py b/smoketest/scripts/cli/test_service_ssh.py
index b09990c92..d8e325eee 100755
--- a/smoketest/scripts/cli/test_service_ssh.py
+++ b/smoketest/scripts/cli/test_service_ssh.py
@@ -1,309 +1,325 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019-2024 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import paramiko
import re
import unittest
from pwd import getpwall
from base_vyostest_shim import VyOSUnitTestSHIM
from vyos.configsession import ConfigSessionError
from vyos.utils.process import cmd
from vyos.utils.process import is_systemd_service_running
from vyos.utils.process import process_named_running
from vyos.utils.file import read_file
from vyos.xml_ref import default_value
PROCESS_NAME = 'sshd'
SSHD_CONF = '/run/sshd/sshd_config'
base_path = ['service', 'ssh']
key_rsa = '/etc/ssh/ssh_host_rsa_key'
key_dsa = '/etc/ssh/ssh_host_dsa_key'
key_ed25519 = '/etc/ssh/ssh_host_ed25519_key'
def get_config_value(key):
tmp = read_file(SSHD_CONF)
tmp = re.findall(f'\n?{key}\s+(.*)', tmp)
return tmp
class TestServiceSSH(VyOSUnitTestSHIM.TestCase):
@classmethod
def setUpClass(cls):
super(TestServiceSSH, cls).setUpClass()
# ensure we can also run this test on a live system - so lets clean
# out the current configuration :)
cls.cli_delete(cls, base_path)
cls.cli_delete(cls, ['vrf'])
def tearDown(self):
# Check for running process
self.assertTrue(process_named_running(PROCESS_NAME))
# delete testing SSH config
self.cli_delete(base_path)
self.cli_delete(['vrf'])
self.cli_commit()
self.assertTrue(os.path.isfile(key_rsa))
self.assertTrue(os.path.isfile(key_dsa))
self.assertTrue(os.path.isfile(key_ed25519))
# Established SSH connections remains running after service is stopped.
# We can not use process_named_running here - we rather need to check
# that the systemd service is no longer running
self.assertFalse(is_systemd_service_running(PROCESS_NAME))
def test_ssh_default(self):
# Check if SSH service runs with default settings - used for checking
# behavior of <defaultValue> in XML definition
self.cli_set(base_path)
# commit changes
self.cli_commit()
# Check configured port agains CLI default value
port = get_config_value('Port')
cli_default = default_value(base_path + ['port'])
self.assertEqual(port, cli_default)
def test_ssh_single_listen_address(self):
# Check if SSH service can be configured and runs
self.cli_set(base_path + ['port', '1234'])
self.cli_set(base_path + ['disable-host-validation'])
self.cli_set(base_path + ['disable-password-authentication'])
self.cli_set(base_path + ['loglevel', 'verbose'])
self.cli_set(base_path + ['client-keepalive-interval', '100'])
self.cli_set(base_path + ['listen-address', '127.0.0.1'])
# commit changes
self.cli_commit()
# Check configured port
port = get_config_value('Port')[0]
self.assertTrue("1234" in port)
# Check DNS usage
dns = get_config_value('UseDNS')[0]
self.assertTrue("no" in dns)
# Check PasswordAuthentication
pwd = get_config_value('PasswordAuthentication')[0]
self.assertTrue("no" in pwd)
# Check loglevel
loglevel = get_config_value('LogLevel')[0]
self.assertTrue("VERBOSE" in loglevel)
# Check listen address
address = get_config_value('ListenAddress')[0]
self.assertTrue("127.0.0.1" in address)
# Check keepalive
keepalive = get_config_value('ClientAliveInterval')[0]
self.assertTrue("100" in keepalive)
def test_ssh_multiple_listen_addresses(self):
# Check if SSH service can be configured and runs with multiple
# listen ports and listen-addresses
ports = ['22', '2222', '2223', '2224']
for port in ports:
self.cli_set(base_path + ['port', port])
addresses = ['127.0.0.1', '::1']
for address in addresses:
self.cli_set(base_path + ['listen-address', address])
# commit changes
self.cli_commit()
# Check configured port
tmp = get_config_value('Port')
for port in ports:
self.assertIn(port, tmp)
# Check listen address
tmp = get_config_value('ListenAddress')
for address in addresses:
self.assertIn(address, tmp)
def test_ssh_vrf_single(self):
vrf = 'mgmt'
# Check if SSH service can be bound to given VRF
self.cli_set(base_path + ['vrf', vrf])
# VRF does yet not exist - an error must be thrown
with self.assertRaises(ConfigSessionError):
self.cli_commit()
self.cli_set(['vrf', 'name', vrf, 'table', '1338'])
# commit changes
self.cli_commit()
# Check for process in VRF
tmp = cmd(f'ip vrf pids {vrf}')
self.assertIn(PROCESS_NAME, tmp)
def test_ssh_vrf_multi(self):
# Check if SSH service can be bound to multiple VRFs
vrfs = ['red', 'blue', 'green']
for vrf in vrfs:
self.cli_set(base_path + ['vrf', vrf])
# VRF does yet not exist - an error must be thrown
with self.assertRaises(ConfigSessionError):
self.cli_commit()
table = 12345
for vrf in vrfs:
self.cli_set(['vrf', 'name', vrf, 'table', str(table)])
table += 1
# commit changes
self.cli_commit()
# Check for process in VRF
for vrf in vrfs:
tmp = cmd(f'ip vrf pids {vrf}')
self.assertIn(PROCESS_NAME, tmp)
def test_ssh_login(self):
# Perform SSH login and command execution with a predefined user. The
# result (output of uname -a) must match the output if the command is
# run natively.
#
# We also try to login as an invalid user - this is not allowed to work.
test_user = 'ssh_test'
test_pass = 'v2i57DZs8idUwMN3VC92'
test_command = 'uname -a'
self.cli_set(base_path)
self.cli_set(['system', 'login', 'user', test_user, 'authentication', 'plaintext-password', test_pass])
# commit changes
self.cli_commit()
# Login with proper credentials
output, error = self.ssh_send_cmd(test_command, test_user, test_pass)
# verify login
self.assertFalse(error)
self.assertEqual(output, cmd(test_command))
# Login with invalid credentials
with self.assertRaises(paramiko.ssh_exception.AuthenticationException):
output, error = self.ssh_send_cmd(test_command, 'invalid_user', 'invalid_password')
self.cli_delete(['system', 'login', 'user', test_user])
self.cli_commit()
# After deletion the test user is not allowed to remain in /etc/passwd
usernames = [x[0] for x in getpwall()]
self.assertNotIn(test_user, usernames)
def test_ssh_dynamic_protection(self):
# check sshguard service
SSHGUARD_CONFIG = '/etc/sshguard/sshguard.conf'
SSHGUARD_WHITELIST = '/etc/sshguard/whitelist'
SSHGUARD_PROCESS = 'sshguard'
block_time = '123'
detect_time = '1804'
port = '22'
threshold = '10'
allow_list = ['192.0.2.0/24', '2001:db8::/48']
self.cli_set(base_path + ['dynamic-protection', 'block-time', block_time])
self.cli_set(base_path + ['dynamic-protection', 'detect-time', detect_time])
self.cli_set(base_path + ['dynamic-protection', 'threshold', threshold])
for allow in allow_list:
self.cli_set(base_path + ['dynamic-protection', 'allow-from', allow])
# commit changes
self.cli_commit()
# Check configured port
tmp = get_config_value('Port')
self.assertIn(port, tmp)
# Check sshgurad service
self.assertTrue(process_named_running(SSHGUARD_PROCESS))
sshguard_lines = [
f'THRESHOLD={threshold}',
f'BLOCK_TIME={block_time}',
f'DETECTION_TIME={detect_time}'
]
tmp_sshguard_conf = read_file(SSHGUARD_CONFIG)
for line in sshguard_lines:
self.assertIn(line, tmp_sshguard_conf)
tmp_whitelist_conf = read_file(SSHGUARD_WHITELIST)
for allow in allow_list:
self.assertIn(allow, tmp_whitelist_conf)
# Delete service ssh dynamic-protection
# but not service ssh itself
self.cli_delete(base_path + ['dynamic-protection'])
self.cli_commit()
self.assertFalse(process_named_running(SSHGUARD_PROCESS))
# Network Device Collaborative Protection Profile
def test_ssh_ndcpp(self):
ciphers = ['aes128-cbc', 'aes128-ctr', 'aes256-cbc', 'aes256-ctr']
host_key_algs = ['sk-ssh-ed25519@openssh.com', 'ssh-rsa', 'ssh-ed25519']
kexes = ['diffie-hellman-group14-sha1', 'ecdh-sha2-nistp256', 'ecdh-sha2-nistp384', 'ecdh-sha2-nistp521']
macs = ['hmac-sha1', 'hmac-sha2-256', 'hmac-sha2-512']
rekey_time = '60'
rekey_data = '1024'
for cipher in ciphers:
self.cli_set(base_path + ['ciphers', cipher])
for host_key in host_key_algs:
self.cli_set(base_path + ['hostkey-algorithm', host_key])
for kex in kexes:
self.cli_set(base_path + ['key-exchange', kex])
for mac in macs:
self.cli_set(base_path + ['mac', mac])
# Optional rekey parameters
self.cli_set(base_path + ['rekey', 'data', rekey_data])
self.cli_set(base_path + ['rekey', 'time', rekey_time])
# commit changes
self.cli_commit()
ssh_lines = ['Ciphers aes128-cbc,aes128-ctr,aes256-cbc,aes256-ctr',
'HostKeyAlgorithms sk-ssh-ed25519@openssh.com,ssh-rsa,ssh-ed25519',
'MACs hmac-sha1,hmac-sha2-256,hmac-sha2-512',
'KexAlgorithms diffie-hellman-group14-sha1,ecdh-sha2-nistp256,ecdh-sha2-nistp384,ecdh-sha2-nistp521',
'RekeyLimit 1024M 60M'
]
tmp_sshd_conf = read_file(SSHD_CONF)
for line in ssh_lines:
self.assertIn(line, tmp_sshd_conf)
+ def test_ssh_pubkey_accepted_algorithm(self):
+ algs = ['ssh-ed25519', 'ecdsa-sha2-nistp256', 'ecdsa-sha2-nistp384',
+ 'ecdsa-sha2-nistp521', 'ssh-dss', 'ssh-rsa', 'rsa-sha2-256',
+ 'rsa-sha2-512'
+ ]
+
+ expected = 'PubkeyAcceptedAlgorithms '
+ for alg in algs:
+ self.cli_set(base_path + ['pubkey-accepted-algorithm', alg])
+ expected = f'{expected}{alg},'
+ expected = expected[:-1]
+
+ self.cli_commit()
+ tmp_sshd_conf = read_file(SSHD_CONF)
+ self.assertIn(expected, tmp_sshd_conf)
+
if __name__ == '__main__':
unittest.main(verbosity=2)

File Metadata

Mime Type
text/x-diff
Expires
Fri, Nov 6, 9:40 AM (10 h, 24 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4653922
Default Alt Text
(30 KB)

Event Timeline