summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile3
-rw-r--r--interface-definitions/ssh.xml134
-rwxr-xr-xsrc/conf-mode/vyos-config-ssh.py293
3 files changed, 430 insertions, 0 deletions
diff --git a/Makefile b/Makefile
index ee89a5608..d194b44e4 100644
--- a/Makefile
+++ b/Makefile
@@ -20,6 +20,9 @@ interface_definitions:
sed -i '/^type: txt/d' $(TMPL_DIR)/system/ntp/server/node.tag/noselect/node.def
sed -i '/^type: txt/d' $(TMPL_DIR)/system/ntp/server/node.tag/preempt/node.def
sed -i '/^type: txt/d' $(TMPL_DIR)/system/ntp/server/node.tag/prefer/node.def
+ sed -i '/^type: txt/d' $(TMPL_DIR)/service/ssh/allow-root/node.def
+ sed -i '/^type: txt/d' $(TMPL_DIR)/service/ssh/disable-host-validation/node.def
+ sed -i '/^type: txt/d' $(TMPL_DIR)/service/ssh/disable-password-authentication/node.def
.PHONY: all
all: interface_definitions
diff --git a/interface-definitions/ssh.xml b/interface-definitions/ssh.xml
new file mode 100644
index 000000000..5ccd3006f
--- /dev/null
+++ b/interface-definitions/ssh.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+
+<!--SSH configuration -->
+
+<interfaceDefinition>
+ <node name="service">
+ <children>
+ <node name="ssh" owner="${vyos_sbindir}/vyos-config-ssh.py">
+ <properties>
+ <help>Secure SHell (SSH) protocol</help>
+ <priority>500</priority>
+ </properties>
+ <children>
+ <node name="access-control">
+ <properties>
+ <help>SSH user/group access controls. Directives are processed in this: deny-users, allow-users, deny-groups and allow-groups</help>
+ </properties>
+ <children>
+ <leafNode name="allow-groups">
+ <properties>
+ <help>Configure sshd_config access control for allowed groups</help>
+ </properties>
+ </leafNode>
+ <leafNode name="allow-users">
+ <properties>
+ <help>Configure sshd_config access control for allowed users</help>
+ </properties>
+ </leafNode>
+ <leafNode name="deny-groups">
+ <properties>
+ <help>Configure sshd_config access control for disallowed groups</help>
+ </properties>
+ </leafNode>
+ <leafNode name="deny-users">
+ <properties>
+ <help>Configure sshd_config access control for disallowed users</help>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ <leafNode name="allow-root">
+ <properties>
+ <help>Enable root login over ssh</help>
+ </properties>
+ </leafNode>
+ <leafNode name="ciphers">
+ <properties>
+ <help>Allowed ciphers</help>
+ <completionHelp>
+ <script>ssh -Q cipher | tr '\n' ' '</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-host-validation">
+ <properties>
+ <help>Don't validate the remote host name with DNS</help>
+ </properties>
+ </leafNode>
+ <leafNode name="disable-password-authentication">
+ <properties>
+ <help>Don't allow unknown user to login with password</help>
+ </properties>
+ </leafNode>
+ <leafNode name="key-exchange">
+ <properties>
+ <help>Key exchange algorithms</help>
+ <completionHelp>
+ <script>ssh -Q kex | tr '\n' ' '</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="listen-address">
+ <properties>
+ <help>Local addresses SSH service should listen on</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>IP address to listen for incoming connections</description>
+ </valueHelp>
+ <valueHelp>
+ <format>ipv6</format>
+ <description>IPv6 address to listen for incoming connections</description>
+ </valueHelp>
+ <type>ipv4,ipv6</type>
+ <multi/>
+ </properties>
+ </leafNode>
+ <leafNode name="loglevel">
+ <properties>
+ <help>Log level</help>
+ <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>
+ </properties>
+ </leafNode>
+ <leafNode name="mac">
+ <properties>
+ <help>Allowed message authentication algorithms</help>
+ <completionHelp>
+ <script>ssh -Q mac | tr '\n' ' '</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>Port for SSH service</help>
+ <valueHelp>
+ <format>u32:1-65535</format>
+ <description>Numeric IP port</description>
+ </valueHelp>
+ <type>u32</type>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/src/conf-mode/vyos-config-ssh.py b/src/conf-mode/vyos-config-ssh.py
new file mode 100755
index 000000000..1605dcd74
--- /dev/null
+++ b/src/conf-mode/vyos-config-ssh.py
@@ -0,0 +1,293 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2018 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 sys
+import os
+
+import jinja2
+
+from vyos.config import Config
+from vyos.util import ConfigError
+
+config_file = r'/etc/ssh/sshd_config'
+
+# Please be careful if you edit the template.
+config_tmpl = """
+
+### Autogenerated by vyos-config-ssh.py ###
+
+# 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
+UsePrivilegeSeparation yes
+KeyRegenerationInterval 3600
+ServerKeyBits 1024
+SyslogFacility AUTH
+LoginGraceTime 120
+StrictModes yes
+RSAAuthentication yes
+PubkeyAuthentication yes
+IgnoreRhosts yes
+RhostsRSAAuthentication no
+HostbasedAuthentication no
+PermitEmptyPasswords no
+ChallengeResponseAuthentication no
+X11Forwarding yes
+X11DisplayOffset 10
+PrintMotd no
+PrintLastLog yes
+TCPKeepAlive yes
+Banner /etc/issue.net
+Subsystem sftp /usr/lib/openssh/sftp-server
+UsePAM yes
+HostKey /etc/ssh/ssh_host_key
+
+# Specifies whether sshd should look up the remote host name,
+# and to check that the resolved host name for the remote IP
+# address maps back to the very same IP address.
+UseDNS {{ host_validation }}
+
+# Specifies the port number that sshd listens on. The default is 22.
+# Multiple options of this type are permitted.
+Port {{ port }}
+
+# Gives the verbosity level that is used when logging messages from sshd
+LogLevel {{ log_level }}
+
+# Specifies whether root can log in using ssh
+PermitRootLogin {{ allow_root }}
+
+# Specifies whether password authentication is allowed
+PasswordAuthentication {{ password_authentication }}
+
+{% if listen_on -%}
+# Specifies the local addresses sshd should listen on
+{% for a in listen_on -%}
+ListenAddress {{ a }}
+{% endfor -%}
+{% 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 }}
+{% 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 }}
+{% 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 }}
+{% 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(" ") }}
+{% 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(" ") }}
+{% 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(" ") }}
+{% endif %}
+
+{% if deny_groups -%}
+# This keyword can be followed by a list of group name patterns, separated by spaces.
+# 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(" ") }}
+{% endif %}
+"""
+
+default_config_data = {
+ 'port' : '22',
+ 'log_level': 'INFO',
+ 'allow_root': 'no',
+ 'password_authentication': 'yes',
+ 'host_validation': 'yes'
+}
+
+def get_config():
+ ssh = default_config_data
+ conf = Config()
+ if not conf.exists('service ssh'):
+ return None
+ else:
+ conf.set_level('service ssh')
+
+ if conf.exists('access-control allow-users'):
+ # Retrieve ',' separated list for allowed users and convert it to a list.
+ # The current VyOS CLI implementation should be improved to rather use multi nodes
+ # instead of a ',' separated input.
+ allow_user = conf.return_value('access-control allow-users')
+ tmp = allow_user.split(',')
+ users = []
+ for u in tmp:
+ users.append(u)
+
+ ssh.setdefault('allow_users', users)
+
+ if conf.exists('access-control allow-groups'):
+ # Retrieve ',' separated list for allowed groups and convert it to a list.
+ # The current VyOS CLI implementation should be improved to rather use multi nodes
+ # instead of a ',' separated input.
+ allow_group = conf.return_value('access-control allow-groups')
+ tmp = allow_group.split(',')
+ groups = []
+ for g in tmp:
+ groups.append(g)
+
+ ssh.setdefault('allow_groups', groups)
+
+ if conf.exists('access-control deny-users'):
+ # Retrieve ',' separated list for denied users and convert it to a list.
+ # The current VyOS CLI implementation should be improved to rather use multi nodes
+ # instead of a ',' separated input.
+ deny_user = conf.return_value('access-control deny-users')
+ tmp = deny_user.split(',')
+ users = []
+ for u in tmp:
+ users.append(u)
+
+ ssh.setdefault('deny_users', users)
+
+ if conf.exists('access-control deny-groups'):
+ # Retrieve ',' separated list for denied groups and convert it to a list.
+ # The current VyOS CLI implementation should be improved to rather use multi nodes
+ # instead of a ',' separated input.
+ deny_group = conf.return_value('access-control deny-groups')
+ tmp = deny_group.split(',')
+ groups = []
+ for g in tmp:
+ groups.append(g)
+
+ ssh.setdefault('deny_groups', groups)
+
+ if conf.exists('allow-root'):
+ ssh['allow-root'] = 'yes'
+
+ if conf.exists('ciphers'):
+ # TODO: OpenSSH supports having multiple Ciphers configured. VyOS CLI
+ # yet has no multi node for this. See T632 in phabricator.
+ ciphers = conf.return_value('ciphers')
+ ssh.setdefault('ciphers', ciphers)
+
+ if conf.exists('disable-host-validation'):
+ ssh['host_validation'] = 'no'
+
+ if conf.exists('disable-password-authentication'):
+ ssh['password_authentication'] = 'no'
+
+ if conf.exists('key-exchange'):
+ # TODO: OpenSSH supports having multiple KEYX methods configured. VyOS CLI
+ # yet has no multi node for this. See T632 in phabricator.
+ kex = conf.return_value('key-exchange')
+ ssh.setdefault('key_exchange', kex)
+
+ if conf.exists('listen-address'):
+ # 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?
+ addresses = conf.return_values('listen-address')
+ listen = []
+
+ for addr in addresses:
+ listen.append(addr)
+
+ ssh.setdefault('listen_on', listen)
+
+ if conf.exists('loglevel'):
+ ssh['log_level'] = conf.return_value('loglevel')
+
+ if conf.exists('mac'):
+ # TODO: OpenSSH supports having multiple MACs configured. VyOS CLI
+ # yet has no multi node for this. See T632 in phabricator.
+ mac = conf.return_value('mac')
+ ssh.setdefault('mac', mac)
+
+ if conf.exists('port'):
+ port = conf.return_value('port')
+ ssh.setdefault('port', port)
+
+ return ssh
+
+def verify(ssh):
+ if ssh is None:
+ 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))
+
+ return None
+
+def generate(ssh):
+ if ssh is None:
+ return None
+
+ tmpl = jinja2.Template(config_tmpl)
+ config_text = tmpl.render(ssh)
+ with open(config_file, 'w') as f:
+ f.write(config_text)
+ return None
+
+def apply(ssh):
+ if ssh is not None and 'port' in ssh.keys():
+ os.system("sudo systemctl restart ssh")
+ else:
+ # SSH access is removed in the commit
+ os.system("sudo systemctl stop ssh")
+ os.unlink(config_file)
+
+ return None
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)