summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/ssh/sshd_config.tmpl142
-rw-r--r--interface-definitions/include/interface-ipv4.xml.i11
-rw-r--r--interface-definitions/include/interface-ipv6.xml.i10
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in9
-rw-r--r--interface-definitions/ssh.xml.in32
-rw-r--r--python/vyos/ifconfig/vxlan.py28
-rw-r--r--python/vyos/ifconfig/wireguard.py58
-rwxr-xr-xsrc/conf_mode/interfaces-tunnel.py11
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py9
-rwxr-xr-xsrc/conf_mode/ssh.py91
10 files changed, 189 insertions, 212 deletions
diff --git a/data/templates/ssh/sshd_config.tmpl b/data/templates/ssh/sshd_config.tmpl
index 08fe56655..1c136bb23 100644
--- a/data/templates/ssh/sshd_config.tmpl
+++ b/data/templates/ssh/sshd_config.tmpl
@@ -1,6 +1,10 @@
### Autogenerated by 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
@@ -22,99 +26,89 @@ TCPKeepAlive yes
Banner /etc/issue.net
Subsystem sftp /usr/lib/openssh/sftp-server
UsePAM yes
+PermitRootLogin no
+
+#
+# User configurable section
+#
-# Specifies whether sshd should look up the remote host name,
-# and to check that the resolved host name for the remote IP
+# 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 {{ host_validation }}
+UseDNS {{ "no" if disable_host_validation is defined else "yes" }}
-# Specifies the port number that sshd listens on. The default is 22.
-# Multiple options of this type are permitted.
-{% for p in port %}
-Port {{ p }}
-{% endfor %}
+# Specifies the port number that sshd(8) listens on
+{% if port is string %}
+Port {{ port }}
+{% else %}
+{% for value in port %}
+Port {{ value }}
+{% endfor %}
+{% endif %}
# Gives the verbosity level that is used when logging messages from sshd
-LogLevel {{ log_level }}
-
-# Specifies whether root can log in using ssh
-PermitRootLogin no
+LogLevel {{ loglevel }}
# Specifies whether password authentication is allowed
-PasswordAuthentication {{ password_authentication }}
+PasswordAuthentication {{ "no" if disable_password_authentication is defined else "yes" }}
-{% if listen_on %}
+{% if listen_address %}
# Specifies the local addresses sshd should listen on
-{% for a in listen_on %}
-ListenAddress {{ a }}
-{% endfor %}
-{{ "\n" }}
-{% endif %}
-
-{%- if ciphers %}
-# Specifies the ciphers allowed. Multiple ciphers must be comma-separated.
-#
-# NOTE: As of now, there is no 'multi' node for 'ciphers', thus we have only one :/
-Ciphers {{ ciphers | join(",") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if mac %}
-# Specifies the available MAC (message authentication code) algorithms. The MAC
-# algorithm is used for data integrity protection. Multiple algorithms must be
-# comma-separated.
-#
-# NOTE: As of now, there is no 'multi' node for 'mac', thus we have only one :/
-MACs {{ mac | join(",") }}
-{{ "\n" }}
-{% endif %}
-
-{%- if key_exchange %}
-# Specifies the available KEX (Key Exchange) algorithms. Multiple algorithms must
-# be comma-separated.
-#
-# NOTE: As of now, there is no 'multi' node for 'key-exchange', thus we have only one :/
-KexAlgorithms {{ key_exchange | join(",") }}
-{{ "\n" }}
+{% if listen_address is string %}
+ListenAddress {{ listen_address }}
+{% else %}
+{% for address in listen_address %}
+ListenAddress {{ value }}
+{% endfor %}
+{% endif %}
{% endif %}
-{%- if allow_users %}
-# This keyword can be followed by a list of user name patterns, separated by spaces.
-# If specified, login is allowed only for user names that match one of the patterns.
-# Only user names are valid, a numerical user ID is not recognized.
-AllowUsers {{ allow_users | join(" ") }}
-{{ "\n" }}
+{% if ciphers %}
+# Specifies the ciphers allowed for protocol version 2
+{% set value = ciphers if ciphers is string else ciphers | join(',') %}
+Ciphers {{ value }}
{% endif %}
-{%- if allow_groups %}
-# This keyword can be followed by a list of group name patterns, separated by spaces.
-# If specified, login is allowed only for users whose primary group or supplementary
-# group list matches one of the patterns. Only group names are valid, a numerical group
-# ID is not recognized.
-AllowGroups {{ allow_groups | join(" ") }}
-{{ "\n" }}
+{% if mac %}
+# Specifies the available MAC (message authentication code) algorithms
+{% set value = mac if mac is string else mac | join(',') %}
+MACs {{ value }}
{% endif %}
-{%- if deny_users %}
-# This keyword can be followed by a list of user name patterns, separated by spaces.
-# Login is disallowed for user names that match one of the patterns. Only user names
-# are valid, a numerical user ID is not recognized.
-DenyUsers {{ deny_users | join(" ") }}
-{{ "\n" }}
+{% if key_exchange %}
+# Specifies the available Key Exchange algorithms
+{% set value = key_exchange if key_exchange is string else key_exchange | join(',') %}
+KexAlgorithms {{ value }}
{% endif %}
-{%- if deny_groups %}
-# This keyword can be followed by a list of group name patterns, separated by spaces.
+{% if access_control is defined %}
+{% if access_control.allow is defined %}
+{% if access_control.allow.user is defined %}
+# If specified, login is allowed only for user names that match
+{% set value = access_control.allow.user if access_control.allow.user is string else access_control.allow.user | join(' ') %}
+AllowUsers {{ value }}
+{% endif %}
+{% if access_control.allow.group is defined %}
+# If specified, login is allowed only for users whose primary group or supplementary group list matches
+{% set value = access_control.allow.group if access_control.allow.group is string else access_control.allow.group | join(' ') %}
+AllowGroups {{ value }}
+{% endif %}
+{% endif %}
+{% if access_control.deny is defined %}
+{% if access_control.deny.user is defined %}
+# Login is disallowed for user names that match
+{% set value = access_control.deny.user if access_control.deny.user is string else access_control.deny.user | join(' ') %}
+DenyUsers {{ value }}
+{% endif %}
+{% if access_control.deny.group is defined %}
# Login is disallowed for users whose primary group or supplementary group list matches
-# one of the patterns. Only group names are valid, a numerical group ID is not recognized.
-DenyGroups {{ deny_groups | join(" ") }}
-{{ "\n" }}
+{% set value = access_control.deny.group if access_control.deny.group is string else access_control.deny.group | join(' ') %}
+DenyGroups {{ value }}
+{% endif %}
+{% endif %}
{% endif %}
-{%- if client_keepalive %}
+{% if client_keepalive_interval %}
# Sets a timeout interval in seconds after which if no data has been received from the client,
-# sshd will send a message through the encrypted channel to request a response from the client.
-# The default is 0, indicating that these messages will not be sent to the client.
-# This option applies to protocol version 2 only.
-ClientAliveInterval {{ client_keepalive }}
+# sshd(8) will send a message through the encrypted channel to request a response from the client
+ClientAliveInterval {{ client_keepalive_interval }}
{% endif %}
diff --git a/interface-definitions/include/interface-ipv4.xml.i b/interface-definitions/include/interface-ipv4.xml.i
new file mode 100644
index 000000000..15932a9d3
--- /dev/null
+++ b/interface-definitions/include/interface-ipv4.xml.i
@@ -0,0 +1,11 @@
+<node name="ip">
+ <properties>
+ <help>IPv4 routing parameters</help>
+ </properties>
+ <children>
+ #include <include/interface-disable-arp-filter.xml.i>
+ #include <include/interface-enable-arp-accept.xml.i>
+ #include <include/interface-enable-arp-announce.xml.i>
+ #include <include/interface-enable-arp-ignore.xml.i>
+ </children>
+</node>
diff --git a/interface-definitions/include/interface-ipv6.xml.i b/interface-definitions/include/interface-ipv6.xml.i
new file mode 100644
index 000000000..23362f75a
--- /dev/null
+++ b/interface-definitions/include/interface-ipv6.xml.i
@@ -0,0 +1,10 @@
+<node name="ipv6">
+ <properties>
+ <help>IPv6 routing parameters</help>
+ </properties>
+ <children>
+ #include <include/ipv6-address.xml.i>
+ #include <include/ipv6-disable-forwarding.xml.i>
+ #include <include/ipv6-dup-addr-detect-transmits.xml.i>
+ </children>
+</node>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index a38a73e15..64520ce99 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -22,13 +22,8 @@
#include <include/interface-disable-link-detect.xml.i>
#include <include/interface-vrf.xml.i>
#include <include/interface-mtu-64-8024.xml.i>
- <node name="ipv6">
- <children>
- #include <include/ipv6-address.xml.i>
- #include <include/ipv6-disable-forwarding.xml.i>
- #include <include/ipv6-dup-addr-detect-transmits.xml.i>
- </children>
- </node>
+ #include <include/interface-ipv4.xml.i>
+ #include <include/interface-ipv6.xml.i>
<leafNode name="local-ip">
<properties>
<help>Local IP address for this tunnel</help>
diff --git a/interface-definitions/ssh.xml.in b/interface-definitions/ssh.xml.in
index 4e8cf28eb..1b20f5776 100644
--- a/interface-definitions/ssh.xml.in
+++ b/interface-definitions/ssh.xml.in
@@ -76,8 +76,12 @@
<properties>
<help>Allowed ciphers</help>
<completionHelp>
- <script>ssh -Q cipher | tr '\n' ' '</script>
+ <!-- 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>
@@ -97,9 +101,13 @@
<properties>
<help>Allowed key exchange (KEX) algorithms</help>
<completionHelp>
- <script>ssh -Q kex | tr '\n' ' '</script>
+ <!-- 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>
<leafNode name="listen-address">
@@ -123,6 +131,9 @@
<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>
@@ -143,14 +154,22 @@
<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>
- <script>ssh -Q mac | tr '\n' ' '</script>
+ <!-- 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>
@@ -166,10 +185,15 @@
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
</properties>
+ <defaultValue>22</defaultValue>
</leafNode>
<leafNode name="client-keepalive-interval">
<properties>
- <help>how often send keep alives in seconds</help>
+ <help>Enable transmission of keepalives from server to client</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Time interval in seconds for keepalive message</description>
+ </valueHelp>
<constraint>
<validator name="numeric" argument="--range 1-65535"/>
</constraint>
diff --git a/python/vyos/ifconfig/vxlan.py b/python/vyos/ifconfig/vxlan.py
index f9f2e38e9..cd9026bf8 100644
--- a/python/vyos/ifconfig/vxlan.py
+++ b/python/vyos/ifconfig/vxlan.py
@@ -66,24 +66,26 @@ class VXLANIf(Interface):
'ifname': 'add',
'vni': 'id',
'port': 'dstport',
- 'src_address': 'nolearning local',
+ 'src_address': 'local',
+ 'src_interface': 'dev',
}
def _create(self):
- cmdline = set()
- if self.config['remote']:
- cmdline = ('ifname', 'type', 'remote', 'src_interface', 'vni', 'port')
-
- elif self.config['src_address']:
- cmdline = ('ifname', 'type', 'src_address', 'vni', 'port')
+ cmdline = ['ifname', 'type', 'vni', 'port']
- elif self.config['group'] and self.config['src_interface']:
- cmdline = ('ifname', 'type', 'group', 'src_interface', 'vni', 'port')
+ if self.config['src_address']:
+ cmdline.append('src_address')
- else:
- ifname = self.config['ifname']
- raise ConfigError(
- f'VXLAN "{ifname}" is missing mandatory underlay interface for a multicast network.')
+ if self.config['remote']:
+ cmdline.append('remote')
+
+ if self.config['group'] or self.config['src_interface']:
+ if self.config['group'] and self.config['src_interface']:
+ cmdline.append('group', 'src_interface')
+ else:
+ ifname = self.config['ifname']
+ raise ConfigError(
+ f'VXLAN "{ifname}" is missing mandatory underlay multicast group or source interface for a multicast network.')
cmd = 'ip link'
for key in cmdline:
diff --git a/python/vyos/ifconfig/wireguard.py b/python/vyos/ifconfig/wireguard.py
index 027b5ea8c..a90a66ac3 100644
--- a/python/vyos/ifconfig/wireguard.py
+++ b/python/vyos/ifconfig/wireguard.py
@@ -149,10 +149,10 @@ class WireGuardIf(Interface):
default = {
'type': 'wireguard',
'port': 0,
- 'private-key': None,
+ 'private_key': None,
'pubkey': None,
- 'psk': '/dev/null',
- 'allowed-ips': [],
+ 'psk': '',
+ 'allowed_ips': [],
'fwmark': 0x00,
'endpoint': None,
'keepalive': 0
@@ -166,8 +166,8 @@ class WireGuardIf(Interface):
}
}
options = Interface.options + \
- ['port', 'private-key', 'pubkey', 'psk',
- 'allowed-ips', 'fwmark', 'endpoint', 'keepalive']
+ ['port', 'private_key', 'pubkey', 'psk',
+ 'allowed_ips', 'fwmark', 'endpoint', 'keepalive']
"""
Wireguard interface class, contains a comnfig dictionary since
@@ -180,44 +180,44 @@ class WireGuardIf(Interface):
>>> from vyos.ifconfig import WireGuardIf as wg_if
>>> wg_intfc = wg_if("wg01")
>>> print (wg_intfc.wg_config)
- {'private-key': None, 'keepalive': 0, 'endpoint': None, 'port': 0,
- 'allowed-ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
+ {'private_key': None, 'keepalive': 0, 'endpoint': None, 'port': 0,
+ 'allowed_ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
>>> wg_intfc.wg_config['keepalive'] = 100
>>> print (wg_intfc.wg_config)
- {'private-key': None, 'keepalive': 100, 'endpoint': None, 'port': 0,
- 'allowed-ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
+ {'private_key': None, 'keepalive': 100, 'endpoint': None, 'port': 0,
+ 'allowed_ips': [], 'pubkey': None, 'fwmark': 0, 'psk': '/dev/null'}
"""
def update(self):
- if not self.config['private-key']:
+ if not self.config['private_key']:
raise ValueError("private key required")
else:
# fmask permission check?
pass
- cmd = "wg set {} ".format(self.config['ifname'])
- cmd += "listen-port {} ".format(self.config['port'])
- cmd += "fwmark {} ".format(str(self.config['fwmark']))
- cmd += "private-key {} ".format(self.config['private-key'])
- cmd += "peer {} ".format(self.config['pubkey'])
- cmd += " preshared-key {} ".format(self.config['psk'])
- cmd += " allowed-ips "
- for aip in self.config['allowed-ips']:
- if aip != self.config['allowed-ips'][-1]:
- cmd += aip + ","
- else:
- cmd += aip
+ cmd = 'wg set {ifname}'.format(**self.config)
+ cmd += ' listen-port {port}'.format(**self.config)
+ cmd += ' fwmark "{fwmark}" '.format(**self.config)
+ cmd += ' private-key {private_key}'.format(**self.config)
+ cmd += ' peer {pubkey}'.format(**self.config)
+ cmd += ' persistent-keepalive {keepalive}'.format(**self.config)
+ cmd += ' allowed-ips {}'.format(', '.join(self.config['allowed-ips']))
+
if self.config['endpoint']:
- cmd += " endpoint '{}'".format(self.config['endpoint'])
- cmd += " persistent-keepalive {}".format(self.config['keepalive'])
+ cmd += ' endpoint "{endpoint}"'.format(**self.config)
+
+ psk_file = ''
+ if self.config['psk']:
+ psk_file = '/tmp/{ifname}.psk'.format(**self.config)
+ with open(psk_file, 'w') as f:
+ f.write(self.config['psk'])
+ cmd += f' preshared-key {psk_file}'
self._cmd(cmd)
- # remove psk since it isn't required anymore and is saved in the cli
- # config only !!
- if self.config['psk'] != '/dev/null':
- if os.path.exists(self.config['psk']):
- os.remove(self.config['psk'])
+ # PSK key file is not required to be stored persistently as its backed by CLI
+ if os.path.exists(psk_file):
+ os.remove(psk_file)
def remove_peer(self, peerkey):
"""
diff --git a/src/conf_mode/interfaces-tunnel.py b/src/conf_mode/interfaces-tunnel.py
index c9964d41c..c13f77d91 100755
--- a/src/conf_mode/interfaces-tunnel.py
+++ b/src/conf_mode/interfaces-tunnel.py
@@ -254,6 +254,10 @@ default_config_data = {
'ip': False,
'ipv6': False,
'nhrp': [],
+ 'arp_filter': 1,
+ 'arp_accept': 0,
+ 'arp_announce': 0,
+ 'arp_ignore': 0,
'ipv6_accept_ra': 1,
'ipv6_autoconf': 0,
'ipv6_forwarding': 1,
@@ -307,6 +311,10 @@ mapping = {
'link_detect': ('disable-link-detect', False, 2),
'vrf': ('vrf', False, None),
'addresses-add': ('address', True, None),
+ 'arp_filter': ('ip disable-arp-filter', False, 0),
+ 'arp_accept': ('ip enable-arp-accept', False, 1),
+ 'arp_announce': ('ip enable-arp-announce', False, 1),
+ 'arp_ignore': ('ip enable-arp-ignore', False, 1),
'ipv6_autoconf': ('ipv6 address autoconf', False, 1),
'ipv6_forwarding': ('ipv6 disable-forwarding', False, 0),
'ipv6_dad_transmits:': ('ipv6 dup-addr-detect-transmits', False, None)
@@ -474,6 +482,8 @@ def verify(conf):
kls = get_class(options)
valid = kls.updates + ['alias', 'addresses-add', 'addresses-del', 'vrf', 'state']
+ valid += ['arp_filter', 'arp_accept', 'arp_announce', 'arp_ignore']
+ valid += ['ipv6_accept_ra', 'ipv6_autoconf', 'ipv6_forwarding', 'ipv6_dad_transmits']
if changes['section'] == 'create':
valid.extend(['type',])
@@ -645,6 +655,7 @@ def apply(conf):
# set other interface properties
for option in ('alias', 'mtu', 'link_detect', 'multicast', 'allmulticast',
+ 'arp_accept', 'arp_filter', 'arp_announce', 'arp_ignore',
'ipv6_accept_ra', 'ipv6_autoconf', 'ipv6_forwarding', 'ipv6_dad_transmits'):
if not options[option]:
# should never happen but better safe
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index ab3e073ae..c24c9a7ce 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -275,7 +275,7 @@ def apply(wg):
# peer pubkey
# setting up the wg interface
- w.config['private-key'] = c['pk']
+ w.config['private_key'] = c['pk']
for peer in wg['peer']:
# peer pubkey
@@ -300,13 +300,8 @@ def apply(wg):
if peer['persistent_keepalive']:
w.config['keepalive'] = peer['persistent_keepalive']
- # maybe move it into ifconfig.py
- # preshared-key - needs to be read from a file
if peer['psk']:
- psk_file = '/config/auth/wireguard/psk'
- with open(psk_file, 'w') as f:
- f.write(peer['psk'])
- w.config['psk'] = psk_file
+ w.config['psk'] = peer['psk']
w.update()
diff --git a/src/conf_mode/ssh.py b/src/conf_mode/ssh.py
index 43fa2ff39..1ca2c8b4c 100755
--- a/src/conf_mode/ssh.py
+++ b/src/conf_mode/ssh.py
@@ -20,89 +20,28 @@ from netifaces import interfaces
from sys import exit
from vyos.config import Config
+from vyos.configdict import dict_merge
from vyos import ConfigError
from vyos.util import call
from vyos.template import render
-
+from vyos.xml import defaults
from vyos import airbag
airbag.enable()
config_file = r'/etc/ssh/sshd_config'
systemd_override = r'/etc/systemd/system/ssh.service.d/override.conf'
-default_config_data = {
- 'port' : ['22'],
- 'log_level': 'INFO',
- 'password_authentication': 'yes',
- 'host_validation': 'yes',
- 'vrf': ''
-}
-
def get_config():
- ssh = default_config_data
conf = Config()
base = ['service', 'ssh']
if not conf.exists(base):
return None
- else:
- conf.set_level(base)
-
- tmp = ['access-control', 'allow', 'user']
- if conf.exists(tmp):
- ssh['allow_users'] = conf.return_values(tmp)
-
- tmp = ['access-control', 'allow', 'group']
- if conf.exists(tmp):
- ssh['allow_groups'] = conf.return_values(tmp)
-
- tmp = ['access-control', 'deny' 'user']
- if conf.exists(tmp):
- ssh['deny_users'] = conf.return_values(tmp)
-
- tmp = ['access-control', 'deny', 'group']
- if conf.exists(tmp):
- ssh['deny_groups'] = conf.return_values(tmp)
-
- tmp = ['ciphers']
- if conf.exists(tmp):
- ssh['ciphers'] = conf.return_values(tmp)
-
- tmp = ['key-exchange']
- if conf.exists(tmp):
- ssh['key_exchange'] = conf.return_values(tmp)
-
- if conf.exists(['disable-host-validation']):
- ssh['host_validation'] = 'no'
-
- if conf.exists(['disable-password-authentication']):
- ssh['password_authentication'] = 'no'
-
- tmp = ['listen-address']
- if conf.exists(tmp):
- # We can listen on both IPv4 and IPv6 addresses
- # Maybe there could be a check in the future if the configured IP address
- # is configured on this system at all?
- ssh['listen_on'] = conf.return_values(tmp)
-
- tmp = ['loglevel']
- if conf.exists(tmp):
- ssh['log_level'] = conf.return_value(tmp)
-
- tmp = ['mac']
- if conf.exists(tmp):
- ssh['mac'] = conf.return_values(tmp)
- tmp = ['port']
- if conf.exists(tmp):
- ssh['port'] = conf.return_values(tmp)
-
- tmp = ['client-keepalive-interval']
- if conf.exists(tmp):
- ssh['client_keepalive'] = conf.return_value(tmp)
-
- tmp = ['vrf']
- if conf.exists(tmp):
- ssh['vrf'] = conf.return_value(tmp)
+ ssh = conf.get_config_dict(base, key_mangling=('-', '_'))
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(base)
+ ssh = dict_merge(default_values, ssh)
return ssh
@@ -110,18 +49,18 @@ def verify(ssh):
if not ssh:
return None
- if 'loglevel' in ssh.keys():
- allowed_loglevel = 'QUIET, FATAL, ERROR, INFO, VERBOSE'
- if not ssh['loglevel'] in allowed_loglevel:
- raise ConfigError('loglevel must be one of "{0}"\n'.format(allowed_loglevel))
-
- if ssh['vrf'] and ssh['vrf'] not in interfaces():
+ if 'vrf' in ssh.keys() and ssh['vrf'] not in interfaces():
raise ConfigError('VRF "{vrf}" does not exist'.format(**ssh))
return None
def generate(ssh):
if not ssh:
+ if os.path.isfile(config_file):
+ os.unlink(config_file)
+ if os.path.isfile(systemd_override):
+ os.unlink(systemd_override)
+
return None
render(config_file, 'ssh/sshd_config.tmpl', ssh, trim_blocks=True)
@@ -133,10 +72,6 @@ def apply(ssh):
if not ssh:
# SSH access is removed in the commit
call('systemctl stop ssh.service')
- if os.path.isfile(config_file):
- os.unlink(config_file)
- if os.path.isfile(systemd_override):
- os.unlink(systemd_override)
# Reload systemd manager configuration
call('systemctl daemon-reload')