summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--debian/control1
-rw-r--r--interface-definitions/system-login.xml.in176
-rw-r--r--src/conf_mode/system-login.py145
3 files changed, 322 insertions, 0 deletions
diff --git a/debian/control b/debian/control
index 9df421977..6e59ea2fb 100644
--- a/debian/control
+++ b/debian/control
@@ -63,6 +63,7 @@ Depends: python3,
openvpn,
openvpn-auth-ldap,
openvpn-auth-radius,
+ libpam-radius-auth,
mtr-tiny,
telnet,
traceroute,
diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in
new file mode 100644
index 000000000..33197d191
--- /dev/null
+++ b/interface-definitions/system-login.xml.in
@@ -0,0 +1,176 @@
+<?xml version="1.0"?>
+<interfaceDefinition>
+ <node name="system">
+ <children>
+ <node name="login" owner="${vyos_conf_scripts_dir}/system_login.py">
+ <properties>
+ <help>User Login</help>
+ <priority>400</priority>
+ </properties>
+ <children>
+ <node name="radius">
+ <properties>
+ <help>RADIUS based user authentication</help>
+ </properties>
+ <children>
+ <leafNode name="source-address">
+ <properties>
+ <help>RADIUS client source address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>TFTP IPv4 listen address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <tagNode name="server">
+ <properties>
+ <help>RADIUS server configuration</help>
+ </properties>
+ <children>
+ <leafNode name="key">
+ <properties>
+ <help>RADIUS shared secret key</help>
+ </properties>
+ </leafNode>
+ <leafNode name="port">
+ <properties>
+ <help>RADIUS authentication port</help>
+ <valueHelp>
+ <format>1-65535</format>
+ <description>Numeric IP port (default: 1812)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-65535"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ <leafNode name="timeout">
+ <properties>
+ <help>Timeout for RADIUS session</help>
+ <valueHelp>
+ <format>1-30</format>
+ <description>Session timeout in seconds (default: 2)</description>
+ </valueHelp>
+ <constraint>
+ <validator name="numeric" argument="--range 1-30"/>
+ </constraint>
+ <constraintErrorMessage>Timeout must be between 1 and 30 seconds</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <tagNode name="user">
+ <properties>
+ <help>User account information</help>
+ <constraint>
+ <regex>[a-zA-Z0-9\-_\.]{1,100}</regex>
+ </constraint>
+ <constraintErrorMessage>Username contains illegal characters or\nexceeds 100 character limitation.</constraintErrorMessage>
+ </properties>
+ <children>
+ <node name="authentication">
+ <properties>
+ <help>Password authentication</help>
+ </properties>
+ <children>
+ <leafNode name="encrypted-password">
+ <properties>
+ <help>Encrypted password</help>
+ <constraint>
+ <regex>(\*|\!)</regex>
+ <regex>[a-zA-Z0-9\.\/]{13}$</regex>
+ <regex>\$1\$[a-zA-Z0-9\./]*\$[a-zA-Z0-9\./]{22}$</regex>
+ <regex>\$5\$[a-zA-Z0-9\./]*\$[a-zA-Z0-9\./]{43}$</regex>
+ <regex>\$6\$[a-zA-Z0-9\./]*\$[a-zA-Z0-9\./]{86}$</regex>
+ </constraint>
+ <constraintErrorMessage>Invalid encrypted password for $VAR(../../@).</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="plaintext-password">
+ <properties>
+ <help>Plaintext password used for encryption</help>
+ </properties>
+ </leafNode>
+ <tagNode name="public-keys">
+ <properties>
+ <help>Remote access public keys</help>
+ <valueHelp>
+ <format>&gt;identifier&lt;</format>
+ <description>Key identifier used by ssh-keygen (usually of form user@host)</description>
+ </valueHelp>
+ </properties>
+ <children>
+ <leafNode name="key">
+ <properties>
+ <help>Public key value (base64-encoded)</help>
+ <completionHelp>
+ <script>echo 'The key is usually several hundred bytes long (because of the size of the public key encoding). Use the loadkey tool to input key from a URL or file.'</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="options">
+ <properties>
+ <help>Optional public key options</help>
+ </properties>
+ </leafNode>
+ <leafNode name="type">
+ <properties>
+ <help></help>
+ <completionHelp>
+ <list>ssh-dss ssh-rsa ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519</list>
+ </completionHelp>
+ <valueHelp>
+ <format>ssh-dss</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>ssh-rsa</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>ecdsa-sha2-nistp256</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>ecdsa-sha2-nistp384</format>
+ <description/>
+ </valueHelp>
+ <valueHelp>
+ <format>ssh-ed25519</format>
+ <description/>
+ </valueHelp>
+ <constraint>
+ <regex>(ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519s)</regex>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ <leafNode name="full-name">
+ <properties>
+ <help>Full name of the user (use quotes for names with spaces)</help>
+ <constraint>
+ <regex>[^:]*$</regex>
+ </constraint>
+ <constraintErrorMessage>Cannot use ':' in full name</constraintErrorMessage>
+ </properties>
+ </leafNode>
+ <leafNode name="home-directory">
+ <properties>
+ <help>Home directory</help>
+ </properties>
+ </leafNode>
+ </children>
+ </tagNode>
+ </children>
+ </node>
+ </children>
+ </node>
+</interfaceDefinition>
diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py
new file mode 100644
index 000000000..2c1e4dc3e
--- /dev/null
+++ b/src/conf_mode/system-login.py
@@ -0,0 +1,145 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 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
+
+from vyos.config import Config
+from vyos import ConfigError
+
+default_config_data = {
+ 'deleted': False,
+ 'radius_server': [],
+ 'radius_source': '',
+ 'user': []
+}
+
+def get_config():
+ login = default_config_data
+ conf = Config()
+ base_level = ['system', 'login']
+
+ if not conf.exists(base_level):
+ login['deleted'] = True
+ return login
+
+ if conf.exists(base_level + ['radius', 'source-address']):
+ login['radius_source'] = conf.return_value(['radius', 'source-address'])
+
+ # Read in all RADIUS servers and store to list
+ for server in conf.list_nodes(base_level + ['radius', 'server']):
+ radius = {
+ 'address': server,
+ 'key': '',
+ 'port': '1812',
+ 'timeout': '2'
+ }
+ conf.set_level(base_level + ['radius', 'server', server])
+
+ # RADIUS shared secret
+ if conf.exists(['key']):
+ radius['key'] = conf.return_value(['key'])
+
+ # RADIUS authentication port
+ if conf.exists(['port']):
+ radius['port'] = conf.return_value(['port'])
+
+ # RADIUS session timeout
+ if conf.exists(['timeout']):
+ radius['timeout'] = conf.return_value(['timeout'])
+
+ # Append individual RADIUS server configuration to global server list
+ login['radius_server'].append(radius)
+
+ # Read in all local users and store to list
+ for username in conf.list_nodes(base_level + ['user']):
+ user = {
+ 'name': username,
+ 'password_plaintext': '',
+ 'password_encrypted': '',
+ 'public_keys': [],
+ 'full_name': '',
+ 'home_dir': '/home/' + username,
+ }
+ conf.set_level(base_level + ['user', username])
+
+ # Plaintext password
+ if conf.exists(['authentication', 'plaintext-password']):
+ user['password_plaintext'] = conf.return_value(['authentication', 'plaintext-password'])
+
+ # Encrypted password
+ if conf.exists(['authentication', 'encrypted-password']):
+ user['password_encrypted'] = conf.return_value(['authentication', 'encrypted-password'])
+
+ # Read in public keys
+ for id in conf.list_nodes(['authentication', 'public-keys']):
+ key = {
+ 'name': id,
+ 'key': '',
+ 'options': '',
+ 'type': ''
+ }
+ conf.set_level(base_level + ['user', username, 'authentication', 'public-keys', id])
+
+ # Public Key portion
+ if conf.exists(['key']):
+ user['key'] = conf.return_value(['key'])
+
+ # Options for individual public key
+ if conf.exists(['options']):
+ user['options'] = conf.return_value(['options'])
+
+ # Type of public key
+ if conf.exists(['type']):
+ user['type'] = conf.return_value(['type'])
+
+ # Append individual public key to list of user keys
+ user['public_keys'].append(key)
+
+ # set proper config level
+ conf.set_level(base_level + ['user', username])
+
+ # User real name
+ if conf.exists(['full-name']):
+ user['full_name'] = conf.return_value(['full-name'])
+
+ # User home-directory
+ if conf.exists(['home-directory']):
+ user['home_dir'] = conf.return_value(['home-directory'])
+
+ return login
+
+def verify(login):
+ pass
+
+def generate(login):
+ import pprint
+ pprint.pprint(login)
+
+ pass
+
+def apply(login):
+ pass
+
+if __name__ == '__main__':
+ try:
+ c = get_config()
+ verify(c)
+ generate(c)
+ apply(c)
+ except ConfigError as e:
+ print(e)
+ sys.exit(1)