From e426b103b803b61e6593e1662c470d0f23a9a36f Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 7 Jan 2021 17:12:21 +0100 Subject: xml: radius: T3192: split individual nodes to discrete includes Provide more re-usable nodes for future implementations. --- .../include/generic-disable-node.xml.in | 8 ++++ .../include/radius-server-ipv4.xml.i | 27 ++++++++++++ .../include/radius-server-key.xml.in | 7 ++++ .../include/radius-server-port.xml.in | 15 +++++++ interface-definitions/include/radius-server.xml.i | 48 ---------------------- 5 files changed, 57 insertions(+), 48 deletions(-) create mode 100644 interface-definitions/include/generic-disable-node.xml.in create mode 100644 interface-definitions/include/radius-server-ipv4.xml.i create mode 100644 interface-definitions/include/radius-server-key.xml.in create mode 100644 interface-definitions/include/radius-server-port.xml.in delete mode 100644 interface-definitions/include/radius-server.xml.i (limited to 'interface-definitions/include') diff --git a/interface-definitions/include/generic-disable-node.xml.in b/interface-definitions/include/generic-disable-node.xml.in new file mode 100644 index 000000000..3e41070bc --- /dev/null +++ b/interface-definitions/include/generic-disable-node.xml.in @@ -0,0 +1,8 @@ + + + + Temporary disable + + + + diff --git a/interface-definitions/include/radius-server-ipv4.xml.i b/interface-definitions/include/radius-server-ipv4.xml.i new file mode 100644 index 000000000..7c5e4eb7d --- /dev/null +++ b/interface-definitions/include/radius-server-ipv4.xml.i @@ -0,0 +1,27 @@ + + + + RADIUS based user authentication + + + #include + + + RADIUS server configuration + + ipv4 + RADIUS server IPv4 address + + + + + + + #include + #include + #include + + + + + diff --git a/interface-definitions/include/radius-server-key.xml.in b/interface-definitions/include/radius-server-key.xml.in new file mode 100644 index 000000000..1f487d3d0 --- /dev/null +++ b/interface-definitions/include/radius-server-key.xml.in @@ -0,0 +1,7 @@ + + + + Shared secret key + + + diff --git a/interface-definitions/include/radius-server-port.xml.in b/interface-definitions/include/radius-server-port.xml.in new file mode 100644 index 000000000..71b6bddb7 --- /dev/null +++ b/interface-definitions/include/radius-server-port.xml.in @@ -0,0 +1,15 @@ + + + + Authentication port + + u32:1-65535 + Numeric IP port (default: 1812) + + + + + + 1812 + + diff --git a/interface-definitions/include/radius-server.xml.i b/interface-definitions/include/radius-server.xml.i deleted file mode 100644 index c1dadd2a2..000000000 --- a/interface-definitions/include/radius-server.xml.i +++ /dev/null @@ -1,48 +0,0 @@ - - - - RADIUS based user authentication - - - #include - - - RADIUS server configuration - - ipv4 - RADIUS server IPv4 address - - - - - - - - - Temporary disable this server - - - - - - Shared secret key - - - - - Authentication port - - u32:1-65535 - Numeric IP port (default: 1812) - - - - - - 1812 - - - - - - -- cgit v1.2.3 From 582f52764afce78b9be0d95b88f6dc8d0bff9690 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 7 Jan 2021 17:25:30 +0100 Subject: xml: include: provide generic include for disable node --- interface-definitions/bcast-relay.xml.in | 14 ++----------- interface-definitions/dhcp-server.xml.in | 21 +++---------------- interface-definitions/dhcpv6-server.xml.in | 21 +++---------------- interface-definitions/firewall-options.xml.in | 7 +------ interface-definitions/igmp-proxy.xml.in | 7 +------ .../include/accel-auth-local-users.xml.i | 7 +------ .../include/generic-disable-node.xml.i | 8 ++++++++ .../include/generic-disable-node.xml.in | 8 -------- .../include/interface-eapol.xml.i | 24 +++++++++++----------- interface-definitions/include/nat-rule.xml.i | 7 +------ .../include/radius-server-ipv4.xml.i | 2 +- .../include/radius-server-key.xml.in | 14 ++++++------- interface-definitions/interfaces-openvpn.xml.in | 21 +++---------------- interface-definitions/interfaces-wireguard.xml.in | 7 +------ interface-definitions/lldp.xml.in | 7 +------ interface-definitions/nat.xml.in | 7 +------ interface-definitions/service_mdns-repeater.xml.in | 7 +------ interface-definitions/service_webproxy.xml.in | 7 +------ interface-definitions/vpn_ipsec.xml.in | 7 +------ interface-definitions/vpn_openconnect.xml.in | 7 +------ interface-definitions/vpn_pptp.xml.in | 6 +----- interface-definitions/vrrp.xml.in | 7 +------ 22 files changed, 52 insertions(+), 171 deletions(-) create mode 100644 interface-definitions/include/generic-disable-node.xml.i delete mode 100644 interface-definitions/include/generic-disable-node.xml.in (limited to 'interface-definitions/include') diff --git a/interface-definitions/bcast-relay.xml.in b/interface-definitions/bcast-relay.xml.in index b691f79fa..1b354d885 100644 --- a/interface-definitions/bcast-relay.xml.in +++ b/interface-definitions/bcast-relay.xml.in @@ -9,12 +9,7 @@ 990 - - - Globally disable broadcast relay service - - - + #include Unique ID for each UDP port to forward @@ -27,12 +22,7 @@ - - - Disable broadcast relay service instance - - - + #include Set source IP of forwarded packets, otherwise original senders address is used diff --git a/interface-definitions/dhcp-server.xml.in b/interface-definitions/dhcp-server.xml.in index 2c1609d94..912e4eaf7 100644 --- a/interface-definitions/dhcp-server.xml.in +++ b/interface-definitions/dhcp-server.xml.in @@ -9,12 +9,7 @@ 911 - - - Disable DHCP server - - - + #include Dynamically update Domain Name System (RFC4702) @@ -63,12 +58,7 @@ Shared-network-name description - - - Option to disable DHCP configuration for shared-network - - - + #include Additional shared-network parameters for DHCP server. @@ -330,12 +320,7 @@ Invalid static mapping name. May only contain letters, numbers and .-_ - - - Option to disable static mapping - - - + #include Fixed IP address of static mapping diff --git a/interface-definitions/dhcpv6-server.xml.in b/interface-definitions/dhcpv6-server.xml.in index 37bc7e03e..fb0e79c47 100644 --- a/interface-definitions/dhcpv6-server.xml.in +++ b/interface-definitions/dhcpv6-server.xml.in @@ -9,12 +9,7 @@ 900 - - - Option to disable DHCPv6 server - - - + #include Preference of this DHCPv6 server compared with others @@ -37,12 +32,7 @@ Invalid DHCPv6 shared network name. May only contain letters, numbers and .-_ - - - Option to disable DHCPv6 configuration for shared-network - - - + #include Common options to distribute to all clients, including stateless clients @@ -324,12 +314,7 @@ Invalid static mapping name. May only contain letters, numbers and .-_ - - - Option to disable static mapping - - - + #include Client identifier (DUID) for this static mapping diff --git a/interface-definitions/firewall-options.xml.in b/interface-definitions/firewall-options.xml.in index defd44f06..8d9225a9a 100644 --- a/interface-definitions/firewall-options.xml.in +++ b/interface-definitions/firewall-options.xml.in @@ -16,12 +16,7 @@ - - - Disable this rule - - - + #include Adjust MSS for IPv4 transit packets diff --git a/interface-definitions/igmp-proxy.xml.in b/interface-definitions/igmp-proxy.xml.in index b9c52794f..d0f44eada 100644 --- a/interface-definitions/igmp-proxy.xml.in +++ b/interface-definitions/igmp-proxy.xml.in @@ -9,12 +9,7 @@ 740 - - - Option to disable IGMP proxy - - - + #include Option to disable "quickleave" diff --git a/interface-definitions/include/accel-auth-local-users.xml.i b/interface-definitions/include/accel-auth-local-users.xml.i index 0d66b8135..35c7a2a06 100644 --- a/interface-definitions/include/accel-auth-local-users.xml.i +++ b/interface-definitions/include/accel-auth-local-users.xml.i @@ -9,12 +9,7 @@ User name for authentication - - - Option to disable a PPPoE Server user - - - + #include Password for authentication diff --git a/interface-definitions/include/generic-disable-node.xml.i b/interface-definitions/include/generic-disable-node.xml.i new file mode 100644 index 000000000..520383afb --- /dev/null +++ b/interface-definitions/include/generic-disable-node.xml.i @@ -0,0 +1,8 @@ + + + + Temporary disable + + + + diff --git a/interface-definitions/include/generic-disable-node.xml.in b/interface-definitions/include/generic-disable-node.xml.in deleted file mode 100644 index 3e41070bc..000000000 --- a/interface-definitions/include/generic-disable-node.xml.in +++ /dev/null @@ -1,8 +0,0 @@ - - - - Temporary disable - - - - diff --git a/interface-definitions/include/interface-eapol.xml.i b/interface-definitions/include/interface-eapol.xml.i index 94476f0f1..8b33b4acf 100644 --- a/interface-definitions/include/interface-eapol.xml.i +++ b/interface-definitions/include/interface-eapol.xml.i @@ -1,12 +1,12 @@ - - - - Extensible Authentication Protocol over Local Area Network - - - #include - #include - #include - - - + + + + Extensible Authentication Protocol over Local Area Network + + + #include + #include + #include + + + diff --git a/interface-definitions/include/nat-rule.xml.i b/interface-definitions/include/nat-rule.xml.i index e034ef4dd..7ef90f07e 100644 --- a/interface-definitions/include/nat-rule.xml.i +++ b/interface-definitions/include/nat-rule.xml.i @@ -26,12 +26,7 @@ #include - - - Disable NAT rule - - - + #include Exclude packets matching this rule from NAT diff --git a/interface-definitions/include/radius-server-ipv4.xml.i b/interface-definitions/include/radius-server-ipv4.xml.i index 7c5e4eb7d..9c73c4c49 100644 --- a/interface-definitions/include/radius-server-ipv4.xml.i +++ b/interface-definitions/include/radius-server-ipv4.xml.i @@ -17,7 +17,7 @@ - #include + #include #include #include diff --git a/interface-definitions/include/radius-server-key.xml.in b/interface-definitions/include/radius-server-key.xml.in index 1f487d3d0..32a01b402 100644 --- a/interface-definitions/include/radius-server-key.xml.in +++ b/interface-definitions/include/radius-server-key.xml.in @@ -1,7 +1,7 @@ - - - - Shared secret key - - - + + + + Shared secret key + + + diff --git a/interface-definitions/interfaces-openvpn.xml.in b/interface-definitions/interfaces-openvpn.xml.in index 34040bf72..527f7fd54 100644 --- a/interface-definitions/interfaces-openvpn.xml.in +++ b/interface-definitions/interfaces-openvpn.xml.in @@ -418,12 +418,7 @@ - - - Option to disable client connection - - - + #include IP address of the client @@ -482,12 +477,7 @@ Pool of client IPv4 addresses - - - Disable client IP pool - - - + #include First IP address in the pool @@ -546,12 +536,7 @@ - - - Disable client IPv6 pool - - - + #include diff --git a/interface-definitions/interfaces-wireguard.xml.in b/interface-definitions/interfaces-wireguard.xml.in index 92c9f510c..acf5082d6 100644 --- a/interface-definitions/interfaces-wireguard.xml.in +++ b/interface-definitions/interfaces-wireguard.xml.in @@ -55,12 +55,7 @@ peer alias too long (limit 100 characters) - - - disables peer - - - + #include base64 encoded public key diff --git a/interface-definitions/lldp.xml.in b/interface-definitions/lldp.xml.in index 950b267ef..9fdffcea1 100644 --- a/interface-definitions/lldp.xml.in +++ b/interface-definitions/lldp.xml.in @@ -25,12 +25,7 @@ - - - Disable lldp on this interface - - - + #include LLDP-MED location data [REQUIRED] diff --git a/interface-definitions/nat.xml.in b/interface-definitions/nat.xml.in index 00aaddb17..d6bed5b27 100644 --- a/interface-definitions/nat.xml.in +++ b/interface-definitions/nat.xml.in @@ -79,12 +79,7 @@ Rule description - - - Disable NAT rule - - - + #include #include diff --git a/interface-definitions/service_mdns-repeater.xml.in b/interface-definitions/service_mdns-repeater.xml.in index e21b1b27c..33ef9a434 100644 --- a/interface-definitions/service_mdns-repeater.xml.in +++ b/interface-definitions/service_mdns-repeater.xml.in @@ -13,12 +13,7 @@ 990 - - - Disable mDNS repeater service - - - + #include Interface to repeat mDNS advertisements [REQUIRED] diff --git a/interface-definitions/service_webproxy.xml.in b/interface-definitions/service_webproxy.xml.in index 4cd8138ec..7cb0f7ece 100644 --- a/interface-definitions/service_webproxy.xml.in +++ b/interface-definitions/service_webproxy.xml.in @@ -394,12 +394,7 @@ URL filtering settings - - - Disable URL filtering - - - + #include URL filtering via squidGuard redirector diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in index daf98a833..426d7e71c 100644 --- a/interface-definitions/vpn_ipsec.xml.in +++ b/interface-definitions/vpn_ipsec.xml.in @@ -1045,12 +1045,7 @@ - - - Option to disable vpn tunnel - - - + #include ESP group name diff --git a/interface-definitions/vpn_openconnect.xml.in b/interface-definitions/vpn_openconnect.xml.in index 386d06509..054e027fc 100644 --- a/interface-definitions/vpn_openconnect.xml.in +++ b/interface-definitions/vpn_openconnect.xml.in @@ -42,12 +42,7 @@ User name for authentication - - - Option to disable a SSL VPN Server user - - - + #include Password for authentication diff --git a/interface-definitions/vpn_pptp.xml.in b/interface-definitions/vpn_pptp.xml.in index fe1bde27e..72eda8752 100644 --- a/interface-definitions/vpn_pptp.xml.in +++ b/interface-definitions/vpn_pptp.xml.in @@ -104,11 +104,7 @@ User name for authentication - - - Option to disable a PPTP Server user - - + #include Password for authentication diff --git a/interface-definitions/vrrp.xml.in b/interface-definitions/vrrp.xml.in index c6a32930f..caa9f4a33 100644 --- a/interface-definitions/vrrp.xml.in +++ b/interface-definitions/vrrp.xml.in @@ -73,12 +73,7 @@ Group description - - - - Disable VRRP group - - + #include Health check script -- cgit v1.2.3 From b9feaf0d6be3adf179df6f35fcd8416d128750f6 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 7 Jan 2021 18:33:23 +0100 Subject: login: radius: T3192: support IPv6 server(s) and source-address --- data/templates/login/pam_radius_auth.conf.tmpl | 33 +++++++++++ .../system-login/pam_radius_auth.conf.tmpl | 16 ------ .../include/radius-server-ipv4-ipv6.xml.i | 32 +++++++++++ .../include/source-address-ipv4-ipv6.xml.i | 1 + interface-definitions/system-login.xml.in | 2 +- smoketest/scripts/cli/test_system_login.py | 66 +++++++++++++++++++++- src/conf_mode/system-login.py | 47 ++++++++------- 7 files changed, 157 insertions(+), 40 deletions(-) create mode 100644 data/templates/login/pam_radius_auth.conf.tmpl delete mode 100644 data/templates/system-login/pam_radius_auth.conf.tmpl create mode 100644 interface-definitions/include/radius-server-ipv4-ipv6.xml.i (limited to 'interface-definitions/include') diff --git a/data/templates/login/pam_radius_auth.conf.tmpl b/data/templates/login/pam_radius_auth.conf.tmpl new file mode 100644 index 000000000..56a5e10ee --- /dev/null +++ b/data/templates/login/pam_radius_auth.conf.tmpl @@ -0,0 +1,33 @@ +# Automatically generated by system-login.py +# RADIUS configuration file + +{# RADIUS IPv6 source address must be specified in [] notation #} +{% set source_address = namespace() %} +{% if radius_source_address is defined and radius_source_address is not none %} +{% for address in radius_source_address %} +{% if address | is_ipv4 %} +{% set source_address.ipv4 = address %} +{% elif address | is_ipv6 %} +{% set source_address.ipv6 = "[" + address + "]" %} +{% endif %} +{% endfor %} +{% endif %} +{% if radius_server is defined and radius_server is not none %} +# server[:port] shared_secret timeout source_ip +{% for server in radius_server | sort(attribute='priority') if not server.disabled %} +{# RADIUS IPv6 servers must be specified in [] notation #} +{% if server.address | is_ipv4 %} +{{ server.address }}:{{ server.port }} {{ "%-25s" | format(server.key) }} {{ "%-10s" | format(server.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is defined }} +{% else %} +[{{ server.address }}]:{{ server.port }} {{ "%-25s" | format(server.key) }} {{ "%-10s" | format(server.timeout) }} {{ source_address.ipv6 if source_address.ipv6 is defined }} +{% endif %} +{% endfor %} + +priv-lvl 15 +mapped_priv_user radius_priv_user + +{% if radius_vrf %} +vrf-name {{ radius_vrf }} +{% endif %} +{% endif %} + diff --git a/data/templates/system-login/pam_radius_auth.conf.tmpl b/data/templates/system-login/pam_radius_auth.conf.tmpl deleted file mode 100644 index ec2d6df95..000000000 --- a/data/templates/system-login/pam_radius_auth.conf.tmpl +++ /dev/null @@ -1,16 +0,0 @@ -# Automatically generated by system-login.py -# RADIUS configuration file -{% if radius_server %} -# server[:port] shared_secret timeout source_ip -{% for s in radius_server|sort(attribute='priority') if not s.disabled %} -{% set addr_port = s.address + ":" + s.port %} -{{ "%-22s" | format(addr_port) }} {{ "%-25s" | format(s.key) }} {{ "%-10s" | format(s.timeout) }} {{ radius_source_address if radius_source_address }} -{% endfor %} - -priv-lvl 15 -mapped_priv_user radius_priv_user - -{% if radius_vrf %} -vrf-name {{ radius_vrf }} -{% endif %} -{% endif %} diff --git a/interface-definitions/include/radius-server-ipv4-ipv6.xml.i b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i new file mode 100644 index 000000000..e947c09e2 --- /dev/null +++ b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i @@ -0,0 +1,32 @@ + + + + RADIUS based user authentication + + + #include + + + RADIUS server configuration + + ipv4 + RADIUS server IPv4 address + + + ipv6 + RADIUS server IPv6 address + + + + + + + + #include + #include + #include + + + + + diff --git a/interface-definitions/include/source-address-ipv4-ipv6.xml.i b/interface-definitions/include/source-address-ipv4-ipv6.xml.i index 004e04f7b..4da4698c2 100644 --- a/interface-definitions/include/source-address-ipv4-ipv6.xml.i +++ b/interface-definitions/include/source-address-ipv4-ipv6.xml.i @@ -17,6 +17,7 @@ + diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 0bea6a22d..6c573bf96 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -110,7 +110,7 @@ - #include + #include diff --git a/smoketest/scripts/cli/test_system_login.py b/smoketest/scripts/cli/test_system_login.py index 6188cf38b..bb6f57fc2 100755 --- a/smoketest/scripts/cli/test_system_login.py +++ b/smoketest/scripts/cli/test_system_login.py @@ -24,8 +24,10 @@ from platform import release as kernel_version from subprocess import Popen, PIPE from vyos.configsession import ConfigSession +from vyos.configsession import ConfigSessionError from vyos.util import cmd from vyos.util import read_file +from vyos.template import inc_ip base_path = ['system', 'login'] users = ['vyos1', 'vyos2'] @@ -42,7 +44,7 @@ class TestSystemLogin(unittest.TestCase): self.session.commit() del self.session - def test_local_user(self): + def test_system_login_user(self): # Check if user can be created and we can SSH to localhost self.session.set(['service', 'ssh', 'port', '22']) @@ -82,7 +84,7 @@ class TestSystemLogin(unittest.TestCase): for option in options: self.assertIn(f'{option}=y', kernel_config) - def test_radius_config(self): + def test_system_login_radius_ipv4(self): # Verify generated RADIUS configuration files radius_key = 'VyOSsecretVyOS' @@ -95,6 +97,12 @@ class TestSystemLogin(unittest.TestCase): self.session.set(base_path + ['radius', 'server', radius_server, 'port', radius_port]) self.session.set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout]) self.session.set(base_path + ['radius', 'source-address', radius_source]) + self.session.set(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) + + # check validate() - Only one IPv4 source-address supported + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) self.session.commit() @@ -130,5 +138,59 @@ class TestSystemLogin(unittest.TestCase): tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf) self.assertTrue(tmp) + def test_system_login_radius_ipv6(self): + # Verify generated RADIUS configuration files + + radius_key = 'VyOS-VyOS' + radius_server = '2001:db8::1' + radius_source = '::1' + radius_port = '4000' + radius_timeout = '4' + + self.session.set(base_path + ['radius', 'server', radius_server, 'key', radius_key]) + self.session.set(base_path + ['radius', 'server', radius_server, 'port', radius_port]) + self.session.set(base_path + ['radius', 'server', radius_server, 'timeout', radius_timeout]) + self.session.set(base_path + ['radius', 'source-address', radius_source]) + self.session.set(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) + + # check validate() - Only one IPv4 source-address supported + with self.assertRaises(ConfigSessionError): + self.session.commit() + self.session.delete(base_path + ['radius', 'source-address', inc_ip(radius_source, 1)]) + + self.session.commit() + + # this file must be read with higher permissions + pam_radius_auth_conf = cmd('sudo cat /etc/pam_radius_auth.conf') + tmp = re.findall(r'\n?\[{}\]:{}\s+{}\s+{}\s+\[{}\]'.format(radius_server, + radius_port, radius_key, radius_timeout, + radius_source), pam_radius_auth_conf) + self.assertTrue(tmp) + + # required, static options + self.assertIn('priv-lvl 15', pam_radius_auth_conf) + self.assertIn('mapped_priv_user radius_priv_user', pam_radius_auth_conf) + + # PAM + pam_common_account = read_file('/etc/pam.d/common-account') + self.assertIn('pam_radius_auth.so', pam_common_account) + + pam_common_auth = read_file('/etc/pam.d/common-auth') + self.assertIn('pam_radius_auth.so', pam_common_auth) + + pam_common_session = read_file('/etc/pam.d/common-session') + self.assertIn('pam_radius_auth.so', pam_common_session) + + pam_common_session_noninteractive = read_file('/etc/pam.d/common-session-noninteractive') + self.assertIn('pam_radius_auth.so', pam_common_session_noninteractive) + + # NSS + nsswitch_conf = read_file('/etc/nsswitch.conf') + tmp = re.findall(r'passwd:\s+mapuid\s+files\s+mapname', nsswitch_conf) + self.assertTrue(tmp) + + tmp = re.findall(r'group:\s+mapname\s+files', nsswitch_conf) + self.assertTrue(tmp) + if __name__ == '__main__': unittest.main(verbosity=2) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 39bad717d..92f717df8 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Copyright (C) 2020 VyOS maintainers and contributors +# Copyright (C) 2020-2021 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 @@ -25,6 +25,7 @@ from sys import exit from vyos.config import Config from vyos.template import render +from vyos.template import is_ipv4 from vyos.util import cmd, call, DEVNULL, chmod_600, chmod_755 from vyos import ConfigError @@ -38,7 +39,7 @@ default_config_data = { 'add_users': [], 'del_users': [], 'radius_server': [], - 'radius_source_address': '', + 'radius_source_address': [], 'radius_vrf': '' } @@ -134,7 +135,7 @@ def get_config(config=None): conf.set_level(base_level + ['radius']) if conf.exists(['source-address']): - login['radius_source_address'] = conf.return_value(['source-address']) + login['radius_source_address'] = conf.return_values(['source-address']) # retrieve VRF instance if conf.exists(['vrf']): @@ -214,6 +215,17 @@ def verify(login): if fail: raise ConfigError('At least one RADIUS server must be active.') + ipv4_count = 0 + ipv6_count = 0 + for address in login['radius_source_address']: + if is_ipv4(address): ipv4_count += 1 + else: ipv6_count += 1 + + if ipv4_count > 1: + raise ConfigError('Only one IPv4 source-address can be set!') + if ipv6_count > 1: + raise ConfigError('Only one IPv6 source-address can be set!') + vrf_name = login['radius_vrf'] if vrf_name and vrf_name not in interfaces(): raise ConfigError(f'VRF "{vrf_name}" does not exist') @@ -255,13 +267,8 @@ def generate(login): pass if len(login['radius_server']) > 0: - render(radius_config_file, 'system-login/pam_radius_auth.conf.tmpl', - login) - - uid = getpwnam('root').pw_uid - gid = getpwnam('root').pw_gid - os.chown(radius_config_file, uid, gid) - chmod_600(radius_config_file) + render(radius_config_file, 'login/pam_radius_auth.conf.tmpl', login, + permission=0o600, user='root', group='root') else: if os.path.isfile(radius_config_file): os.unlink(radius_config_file) @@ -284,16 +291,15 @@ def apply(login): # we need to use '' quotes when passing formatted data to the shell # else it will not work as some data parts are lost in translation if user['password_encrypted']: - command += " -p '{}'".format(user['password_encrypted']) + command += " -p '{password_encrypted}'".format(**user) if user['full_name']: - command += " -c '{}'".format(user['full_name']) + command += " -c '{full_name}'".format(**user) if user['home_dir']: - command += " -d '{}'".format(user['home_dir']) + command += " -d '{home_dir}'".format(**user) - command += " -G frrvty,vyattacfg,sudo,adm,dip,disk" - command += " {}".format(user['name']) + command += " -G frrvty,vyattacfg,sudo,adm,dip,disk {name}".format(**user) try: cmd(command) @@ -321,10 +327,9 @@ def apply(login): for id in user['public_keys']: line = '' if id['options']: - line = '{} '.format(id['options']) + line = '{options} '.format(**id) - line += '{} {} {}\n'.format(id['type'], - id['key'], id['name']) + line += '{type} {key} {name}\n'.format(**id) f.write(line) os.chown(ssh_key_file, uid, gid) @@ -339,8 +344,8 @@ def apply(login): try: # Logout user if he is logged in if user in list(set([tmp[0] for tmp in users()])): - print('{} is logged in, forcing logout'.format(user)) - call('pkill -HUP -u {}'.format(user)) + print(f'{user} is logged in, forcing logout') + call(f'pkill -HUP -u {user}') # Remove user account but leave home directory to be safe call(f'userdel -r {user}', stderr=DEVNULL) @@ -356,7 +361,7 @@ def apply(login): env = os.environ.copy() env['DEBIAN_FRONTEND'] = 'noninteractive' # Enable RADIUS in PAM - cmd("pam-auth-update --package --enable radius", env=env) + cmd('pam-auth-update --package --enable radius', env=env) # Make NSS system aware of RADIUS, too command = "sed -i -e \'/\smapname/b\' \ -- cgit v1.2.3 From e8a1c291b1d4b90709a68038e16522b4cee82904 Mon Sep 17 00:00:00 2001 From: Christian Poessinger Date: Thu, 7 Jan 2021 21:28:04 +0100 Subject: login: radius: T3192: migrate to get_config_dict() --- data/templates/login/authorized_keys.tmpl | 9 + data/templates/login/pam_radius_auth.conf.tmpl | 29 +- .../include/radius-server-ipv4-ipv6.xml.i | 2 +- interface-definitions/system-login.xml.in | 13 +- python/vyos/util.py | 2 +- src/conf_mode/system-login.py | 473 ++++++++------------- 6 files changed, 211 insertions(+), 317 deletions(-) create mode 100644 data/templates/login/authorized_keys.tmpl (limited to 'interface-definitions/include') diff --git a/data/templates/login/authorized_keys.tmpl b/data/templates/login/authorized_keys.tmpl new file mode 100644 index 000000000..639a80e1d --- /dev/null +++ b/data/templates/login/authorized_keys.tmpl @@ -0,0 +1,9 @@ +### Automatically generated by system-login.py ### + +{% if authentication is defined and authentication.public_keys is defined and authentication.public_keys is not none %} +{% for key, key_options in authentication.public_keys.items() %} +{# The whitespace after options is wisely chosen #} +{{ key_options.options + ' ' if key_options.options is defined }}{{ key_options.type }} {{ key_options.key }} {{ key }} +{% endfor %} +{% endif %} + diff --git a/data/templates/login/pam_radius_auth.conf.tmpl b/data/templates/login/pam_radius_auth.conf.tmpl index 56a5e10ee..fad8e7dcb 100644 --- a/data/templates/login/pam_radius_auth.conf.tmpl +++ b/data/templates/login/pam_radius_auth.conf.tmpl @@ -1,10 +1,11 @@ # Automatically generated by system-login.py # RADIUS configuration file +{% if radius is defined and radius is not none %} {# RADIUS IPv6 source address must be specified in [] notation #} {% set source_address = namespace() %} -{% if radius_source_address is defined and radius_source_address is not none %} -{% for address in radius_source_address %} +{% if radius.source_address is defined and radius.source_address is not none %} +{% for address in radius.source_address %} {% if address | is_ipv4 %} {% set source_address.ipv4 = address %} {% elif address | is_ipv6 %} @@ -12,22 +13,24 @@ {% endif %} {% endfor %} {% endif %} -{% if radius_server is defined and radius_server is not none %} +{% if radius.server is defined and radius.server is not none %} # server[:port] shared_secret timeout source_ip -{% for server in radius_server | sort(attribute='priority') if not server.disabled %} +{# .items() returns a tuple of two elements: key and value. 1 relates to the 2nd element i.e. the value and .priority relates to the key from the internal dict #} +{% for server, options in radius.server.items() | sort(attribute='1.priority') if not options.disabled %} {# RADIUS IPv6 servers must be specified in [] notation #} -{% if server.address | is_ipv4 %} -{{ server.address }}:{{ server.port }} {{ "%-25s" | format(server.key) }} {{ "%-10s" | format(server.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is defined }} -{% else %} -[{{ server.address }}]:{{ server.port }} {{ "%-25s" | format(server.key) }} {{ "%-10s" | format(server.timeout) }} {{ source_address.ipv6 if source_address.ipv6 is defined }} -{% endif %} -{% endfor %} +{% if server | is_ipv4 %} +{{ server }}:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv4 if source_address.ipv4 is defined }} +{% else %} +[{{ server }}]:{{ options.port }} {{ "%-25s" | format(options.key) }} {{ "%-10s" | format(options.timeout) }} {{ source_address.ipv6 if source_address.ipv6 is defined }} +{% endif %} +{% endfor %} +{% endif %} priv-lvl 15 mapped_priv_user radius_priv_user -{% if radius_vrf %} -vrf-name {{ radius_vrf }} -{% endif %} +{% if radius.vrf is defined and radius.vrf is not none %} +vrf-name {{ radius.vrf }} +{% endif %} {% endif %} diff --git a/interface-definitions/include/radius-server-ipv4-ipv6.xml.i b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i index e947c09e2..e4919d86a 100644 --- a/interface-definitions/include/radius-server-ipv4-ipv6.xml.i +++ b/interface-definitions/include/radius-server-ipv4-ipv6.xml.i @@ -4,7 +4,6 @@ RADIUS based user authentication - #include RADIUS server configuration @@ -27,6 +26,7 @@ #include + #include diff --git a/interface-definitions/system-login.xml.in b/interface-definitions/system-login.xml.in index 6c573bf96..34e14d8e7 100644 --- a/interface-definitions/system-login.xml.in +++ b/interface-definitions/system-login.xml.in @@ -34,6 +34,7 @@ Invalid encrypted password for $VAR(../../@). + ! @@ -44,7 +45,7 @@ Remote access public keys - >identifier< + txt Key identifier used by ssh-keygen (usually of form user@host) @@ -61,7 +62,7 @@ - + Public key type ssh-dss ssh-rsa ecdsa-sha2-nistp256 ecdsa-sha2-nistp384 ecdsa-sha2-nistp521 ssh-ed25519 @@ -86,7 +87,7 @@ - (ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519) + ^(ssh-dss|ssh-rsa|ecdsa-sha2-nistp256|ecdsa-sha2-nistp384|ecdsa-sha2-nistp521|ssh-ed25519)$ @@ -119,7 +120,7 @@ Session timeout - 1-30 + u32:1-30 Session timeout in seconds (default: 2) @@ -127,18 +128,20 @@ Timeout must be between 1 and 30 seconds + 2 Server priority - 1-255 + u32:1-255 Server priority (default: 255) + 255 diff --git a/python/vyos/util.py b/python/vyos/util.py index 494c8155e..699f05892 100644 --- a/python/vyos/util.py +++ b/python/vyos/util.py @@ -311,7 +311,7 @@ def chmod_755(path): def makedir(path, user=None, group=None): if os.path.exists(path): return - os.mkdir(path) + os.makedirs(path, mode=0o755) chown(path, user, group) diff --git a/src/conf_mode/system-login.py b/src/conf_mode/system-login.py index 92f717df8..82accd404 100755 --- a/src/conf_mode/system-login.py +++ b/src/conf_mode/system-login.py @@ -16,34 +16,30 @@ import os -from crypt import crypt, METHOD_SHA512 -from netifaces import interfaces +from crypt import crypt +from crypt import METHOD_SHA512 from psutil import users -from pwd import getpwall, getpwnam +from pwd import getpwall +from pwd import getpwnam from spwd import getspnam from sys import exit from vyos.config import Config +from vyos.configdict import dict_merge +from vyos.configverify import verify_vrf from vyos.template import render from vyos.template import is_ipv4 -from vyos.util import cmd, call, DEVNULL, chmod_600, chmod_755 +from vyos.util import cmd +from vyos.util import call +from vyos.util import DEVNULL +from vyos.util import dict_search +from vyos.xml import defaults from vyos import ConfigError - from vyos import airbag airbag.enable() radius_config_file = "/etc/pam_radius_auth.conf" -default_config_data = { - 'deleted': False, - 'add_users': [], - 'del_users': [], - 'radius_server': [], - 'radius_source_address': [], - 'radius_vrf': '' -} - - def get_local_users(): """Return list of dynamically allocated users (see Debian Policy Manual)""" local_users = [] @@ -58,215 +54,130 @@ def get_local_users(): def get_config(config=None): - login = default_config_data if config: conf = config else: conf = Config() - base_level = ['system', 'login'] - - # We do not need to check if the nodes exist or not and bail out early - # ... this would interrupt the following logic on determine which users - # should be deleted and which users should stay. - # - # All fine so far! - - # 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']) - - # 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']) - - # 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']): - key['key'] = conf.return_value(['key']) - - # Options for individual public key - if conf.exists(['options']): - key['options'] = conf.return_value(['options']) - - # Type of public key - if conf.exists(['type']): - key['type'] = conf.return_value(['type']) - - # Append individual public key to list of user keys - user['public_keys'].append(key) - - login['add_users'].append(user) - - # - # RADIUS configuration - # - conf.set_level(base_level + ['radius']) - - if conf.exists(['source-address']): - login['radius_source_address'] = conf.return_values(['source-address']) - - # retrieve VRF instance - if conf.exists(['vrf']): - login['radius_vrf'] = conf.return_value(['vrf']) - - # Read in all RADIUS servers and store to list - for server in conf.list_nodes(['server']): - server_cfg = { - 'address': server, - 'disabled': False, - 'key': '', - 'port': '1812', - 'timeout': '2', - 'priority': 255 - } - conf.set_level(base_level + ['radius', 'server', server]) - - # Check if RADIUS server was temporary disabled - if conf.exists(['disable']): - server_cfg['disabled'] = True - - # RADIUS shared secret - if conf.exists(['key']): - server_cfg['key'] = conf.return_value(['key']) - - # RADIUS authentication port - if conf.exists(['port']): - server_cfg['port'] = conf.return_value(['port']) - - # RADIUS session timeout - if conf.exists(['timeout']): - server_cfg['timeout'] = conf.return_value(['timeout']) - - # Check if RADIUS server has priority - if conf.exists(['priority']): - server_cfg['priority'] = int(conf.return_value(['priority'])) - - # Append individual RADIUS server configuration to global server list - login['radius_server'].append(server_cfg) + base = ['system', 'login'] + login = conf.get_config_dict(base, key_mangling=('-', '_'), + get_first_key=True) # users no longer existing in the running configuration need to be deleted local_users = get_local_users() - cli_users = [tmp['name'] for tmp in login['add_users']] - # create a list of all users, cli and users - all_users = list(set(local_users+cli_users)) + cli_users = [] + if 'user' in login: + cli_users = list(login['user']) + + # XXX: T2665: we can not safely rely on the defaults() when there are + # tagNodes in place, it is better to blend in the defaults manually. + default_values = defaults(base + ['user']) + for user in login['user']: + login['user'][user] = dict_merge(default_values, login['user'][user]) + + # XXX: T2665: we can not safely rely on the defaults() when there are + # tagNodes in place, it is better to blend in the defaults manually. + default_values = defaults(base + ['radius', 'server']) + for server in dict_search('radius.server', login) or []: + login['radius']['server'][server] = dict_merge(default_values, + login['radius']['server'][server]) + + # XXX: for a yet unknown reason when we only have one source-address + # get_config_dict() will show a string over a string + if 'radius' in login and 'source_address' in login['radius']: + print(type(login['radius']['source_address'])) + if isinstance(login['radius']['source_address'], str): + login['radius']['source_address'] = [login['radius']['source_address']] - # Remove any normal users that dos not exist in the current configuration. - # This can happen if user is added but configuration was not saved and - # system is rebooted. - login['del_users'] = [tmp for tmp in all_users if tmp not in cli_users] + # create a list of all users, cli and users + all_users = list(set(local_users + cli_users)) + # We will remove any normal users that dos not exist in the current + # configuration. This can happen if user is added but configuration was not + # saved and system is rebooted. + rm_users = [tmp for tmp in all_users if tmp not in cli_users] + if rm_users: login.update({'rm_users' : rm_users}) return login - def verify(login): - cur_user = os.environ['SUDO_USER'] - if cur_user in login['del_users']: - raise ConfigError( - 'Attempting to delete current user: {}'.format(cur_user)) - - for user in login['add_users']: - for key in user['public_keys']: - if not key['type']: - raise ConfigError( - 'SSH public key type missing for "{name}"!'.format(**key)) - - if not key['key']: - raise ConfigError( - 'SSH public key for id "{name}" missing!'.format(**key)) + if 'rm_users' in login: + cur_user = os.environ['SUDO_USER'] + if cur_user in login['rm_users']: + raise ConfigError(f'Attempting to delete current user: {cur_user}') + + if 'user' in login: + for user, user_config in login['user'].items(): + for pubkey, pubkey_options in (dict_search('authentication.public_keys', user_config) or {}).items(): + if 'type' not in pubkey_options: + raise ConfigError(f'Missing type for public-key "{pubkey}"!') + if 'key' not in pubkey_options: + raise ConfigError(f'Missing key for public-key "{pubkey}"!') # At lease one RADIUS server must not be disabled - if len(login['radius_server']) > 0: + if 'radius' in login: + if 'server' not in login['radius']: + raise ConfigError('No RADIUS server defined!') + fail = True - for server in login['radius_server']: - if not server['disabled']: + for server, server_config in dict_search('radius.server', login).items(): + if 'key' not in server_config: + raise ConfigError(f'RADIUS server "{server}" requires key!') + + if 'disabled' not in server_config: fail = False + continue if fail: - raise ConfigError('At least one RADIUS server must be active.') + raise ConfigError('All RADIUS servers are disabled') - ipv4_count = 0 - ipv6_count = 0 - for address in login['radius_source_address']: - if is_ipv4(address): ipv4_count += 1 - else: ipv6_count += 1 + verify_vrf(login['radius']) - if ipv4_count > 1: - raise ConfigError('Only one IPv4 source-address can be set!') - if ipv6_count > 1: - raise ConfigError('Only one IPv6 source-address can be set!') + if 'source_address' in login['radius']: + ipv4_count = 0 + ipv6_count = 0 + for address in login['radius']['source_address']: + if is_ipv4(address): ipv4_count += 1 + else: ipv6_count += 1 - vrf_name = login['radius_vrf'] - if vrf_name and vrf_name not in interfaces(): - raise ConfigError(f'VRF "{vrf_name}" does not exist') + if ipv4_count > 1: + raise ConfigError('Only one IPv4 source-address can be set!') + if ipv6_count > 1: + raise ConfigError('Only one IPv6 source-address can be set!') return None def generate(login): # calculate users encrypted password - for user in login['add_users']: - if user['password_plaintext']: - user['password_encrypted'] = crypt( - user['password_plaintext'], METHOD_SHA512) - user['password_plaintext'] = '' - - # remove old plaintext password and set new encrypted password - env = os.environ.copy() - env['vyos_libexec_dir'] = '/usr/libexec/vyos' - - call("/opt/vyatta/sbin/my_delete system login user '{name}' " - "authentication plaintext-password" - .format(**user), env=env) - - call("/opt/vyatta/sbin/my_set system login user '{name}' " - "authentication encrypted-password '{password_encrypted}'" - .format(**user), env=env) - - else: - try: - if getspnam(user['name']).sp_pwdp == user['password_encrypted']: - # If the current encrypted bassword matches the encrypted password - # from the config - do not update it. This will remove the encrypted - # value from the system logs. - # - # The encrypted password will be set only once during the first boot - # after an image upgrade. - user['password_encrypted'] = '' - except: - pass - - if len(login['radius_server']) > 0: + if 'user' in login: + for user, user_config in login['user'].items(): + tmp = dict_search('authentication.plaintext_password', user_config) + if tmp: + encrypted_password = crypt(tmp, METHOD_SHA512) + login['user'][user]['authentication']['encrypted_password'] = encrypted_password + del login['user'][user]['authentication']['plaintext_password'] + + # remove old plaintext password and set new encrypted password + env = os.environ.copy() + env['vyos_libexec_dir'] = '/usr/libexec/vyos' + + call(f"/opt/vyatta/sbin/my_delete system login user '{user}' " + "authentication plaintext-password", env=env) + + call(f"/opt/vyatta/sbin/my_set system login user '{user}' " + "authentication encrypted-password '{encrypted_password}'", env=env) + else: + try: + if getspnam(user).sp_pwdp == dict_search('authentication.encrypted_password', user_config): + # If the current encrypted bassword matches the encrypted password + # from the config - do not update it. This will remove the encrypted + # value from the system logs. + # + # The encrypted password will be set only once during the first boot + # after an image upgrade. + del login['user'][user]['authentication']['encrypted_password'] + except: + pass + + if 'radius' in login: render(radius_config_file, 'login/pam_radius_auth.conf.tmpl', login, permission=0o600, user='root', group='root') else: @@ -277,93 +188,72 @@ def generate(login): def apply(login): - for user in login['add_users']: - # make new user using vyatta shell and make home directory (-m), - # default group of 100 (users) - command = "useradd -m -N" - # check if user already exists: - if user['name'] in get_local_users(): - # update existing account - command = "usermod" - - # all accounts use /bin/vbash - command += " -s /bin/vbash" - # we need to use '' quotes when passing formatted data to the shell - # else it will not work as some data parts are lost in translation - if user['password_encrypted']: - command += " -p '{password_encrypted}'".format(**user) - - if user['full_name']: - command += " -c '{full_name}'".format(**user) - - if user['home_dir']: - command += " -d '{home_dir}'".format(**user) - - command += " -G frrvty,vyattacfg,sudo,adm,dip,disk {name}".format(**user) - - try: - cmd(command) - - uid = getpwnam(user['name']).pw_uid - gid = getpwnam(user['name']).pw_gid - - # we should not rely on the value stored in user['home_dir'], as a - # crazy user will choose username root or any other system user - # which will fail. Should we deny using root at all? - home_dir = getpwnam(user['name']).pw_dir - - # install ssh keys - ssh_key_dir = home_dir + '/.ssh' - if not os.path.isdir(ssh_key_dir): - os.mkdir(ssh_key_dir) - os.chown(ssh_key_dir, uid, gid) - chmod_755(ssh_key_dir) - - ssh_key_file = ssh_key_dir + '/authorized_keys' - with open(ssh_key_file, 'w') as f: - f.write("# Automatically generated by VyOS\n") - f.write("# Do not edit, all changes will be lost\n") - - for id in user['public_keys']: - line = '' - if id['options']: - line = '{options} '.format(**id) - - line += '{type} {key} {name}\n'.format(**id) - f.write(line) - - os.chown(ssh_key_file, uid, gid) - chmod_600(ssh_key_file) - - except Exception as e: - print(e) - raise ConfigError('Adding user "{name}" raised exception' - .format(**user)) - - for user in login['del_users']: - try: - # Logout user if he is logged in - if user in list(set([tmp[0] for tmp in users()])): - print(f'{user} is logged in, forcing logout') - call(f'pkill -HUP -u {user}') - - # Remove user account but leave home directory to be safe - call(f'userdel -r {user}', stderr=DEVNULL) - - except Exception as e: - raise ConfigError(f'Deleting user "{user}" raised exception: {e}') + if 'user' in login: + for user, user_config in login['user'].items(): + # make new user using vyatta shell and make home directory (-m), + # default group of 100 (users) + command = 'useradd -m -N' + # check if user already exists: + if user in get_local_users(): + # update existing account + command = 'usermod' + + # all accounts use /bin/vbash + command += ' -s /bin/vbash' + # we need to use '' quotes when passing formatted data to the shell + # else it will not work as some data parts are lost in translation + tmp = dict_search('authentication.encrypted_password', user_config) + if tmp: command += f" -p '{tmp}'" + + tmp = dict_search('full_name', user_config) + if tmp: command += f" -c '{tmp}'" + + tmp = dict_search('home_directory', user_config) + if tmp: command += f" -d '{tmp}'" + else: command += f" -d '/home/{user}'" + + command += f' -G frrvty,vyattacfg,sudo,adm,dip,disk {user}' + + try: + cmd(command) + + # we should not rely on the value stored in + # user_config['home_directory'], as a crazy user will choose + # username root or any other system user which will fail. + # + # XXX: Should we deny using root at all? + home_dir = getpwnam(user).pw_dir + render(f'{home_dir}/.ssh/authorized_keys', 'login/authorized_keys.tmpl', + user_config, permission=0o600, user=user, group='users') + + except Exception as e: + raise ConfigError(f'Adding user "{user}" raised exception: "{e}"') + + if 'rm_users' in login: + for user in login['rm_users']: + try: + # Logout user if he is still logged in + if user in list(set([tmp[0] for tmp in users()])): + print(f'{user} is logged in, forcing logout!') + call(f'pkill -HUP -u {user}') + + # Remove user account but leave home directory to be safe + call(f'userdel -r {user}', stderr=DEVNULL) + + except Exception as e: + raise ConfigError(f'Deleting user "{user}" raised exception: {e}') # # RADIUS configuration # - if len(login['radius_server']) > 0: - try: - env = os.environ.copy() - env['DEBIAN_FRONTEND'] = 'noninteractive' + env = os.environ.copy() + env['DEBIAN_FRONTEND'] = 'noninteractive' + try: + if 'radius' in login: # Enable RADIUS in PAM cmd('pam-auth-update --package --enable radius', env=env) - - # Make NSS system aware of RADIUS, too + # Make NSS system aware of RADIUS + # This fancy snipped was copied from old Vyatta code command = "sed -i -e \'/\smapname/b\' \ -e \'/^passwd:/s/\s\s*/&mapuid /\' \ -e \'/^passwd:.*#/s/#.*/mapname &/\' \ @@ -371,31 +261,20 @@ def apply(login): -e \'/^group:.*#/s/#.*/ mapname &/\' \ -e \'/^group:[^#]*$/s/: */&mapname /\' \ /etc/nsswitch.conf" - - cmd(command) - - except Exception as e: - raise ConfigError('RADIUS configuration failed: {}'.format(e)) - - else: - try: - env = os.environ.copy() - env['DEBIAN_FRONTEND'] = 'noninteractive' - + else: # Disable RADIUS in PAM - cmd("pam-auth-update --package --remove radius", env=env) - + cmd('pam-auth-update --package --remove radius', env=env) + # Drop RADIUS from NSS NSS system + # This fancy snipped was copied from old Vyatta code command = "sed -i -e \'/^passwd:.*mapuid[ \t]/s/mapuid[ \t]//\' \ -e \'/^passwd:.*[ \t]mapname/s/[ \t]mapname//\' \ -e \'/^group:.*[ \t]mapname/s/[ \t]mapname//\' \ -e \'s/[ \t]*$//\' \ /etc/nsswitch.conf" - cmd(command) - - except Exception as e: - raise ConfigError( - 'Removing RADIUS configuration failed.\n{}'.format(e)) + cmd(command) + except Exception as e: + raise ConfigError(f'RADIUS configuration failed: {e}') return None -- cgit v1.2.3