Page Menu
Home
VyOS Platform
Search
Configure Global Search
Log In
Files
F119064220
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
30 KB
Referenced Files
None
Subscribers
None
View Options
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
Details
Attached
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)
Attached To
Mode
rVYOSONEX vyos-1x
Attached
Detach File
Event Timeline
Log In to Comment