summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rw-r--r--data/templates/frr/bgpd.frr.tmpl3
-rw-r--r--data/templates/ipsec/charon/dhcp.conf.tmpl10
-rw-r--r--data/templates/ipsec/charon/eap-radius.conf.tmpl115
-rw-r--r--data/templates/ipsec/ios_profile.tmpl20
-rw-r--r--data/templates/ipsec/swanctl/peer.tmpl2
-rw-r--r--data/templates/ipsec/windows_profile.tmpl4
-rw-r--r--data/templates/router-advert/radvd.conf.tmpl80
-rw-r--r--interface-definitions/include/accel-ppp/radius-additions.xml.i6
-rw-r--r--interface-definitions/include/bgp/protocol-common-config.xml.i6
-rw-r--r--interface-definitions/include/radius-nas-identifier.xml.i7
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in9
-rw-r--r--interface-definitions/vpn_ipsec.xml.in84
-rw-r--r--interface-definitions/vpn_l2tp.xml.in6
-rw-r--r--op-mode-definitions/generate-ipsec-profile.xml.in111
-rw-r--r--python/vyos/configsession.py7
-rw-r--r--python/vyos/util.py6
-rw-r--r--smoketest/configs/bgp-azure-ipsec-gateway16
-rwxr-xr-xsmoketest/scripts/cli/test_protocols_bgp.py16
-rwxr-xr-xsmoketest/scripts/cli/test_service_router-advert.py26
-rwxr-xr-xsrc/conf_mode/interfaces-loopback.py4
-rwxr-xr-xsrc/conf_mode/le_cert.py4
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py10
-rwxr-xr-xsrc/conf_mode/system-option.py3
-rwxr-xr-xsrc/conf_mode/vpn_ipsec.py42
-rw-r--r--src/etc/sysctl.d/30-vyos-router.conf7
-rwxr-xr-xsrc/migration-scripts/interfaces/19-to-20130
-rwxr-xr-xsrc/migration-scripts/interfaces/20-to-21130
-rwxr-xr-xsrc/op_mode/ikev2_profile_generator.py171
-rwxr-xr-xsrc/op_mode/show_dhcp.py7
-rwxr-xr-xsrc/op_mode/show_dhcpv6.py6
-rwxr-xr-xsrc/services/vyos-configd7
-rw-r--r--src/systemd/isc-dhcp-server.service6
33 files changed, 750 insertions, 316 deletions
diff --git a/Makefile b/Makefile
index 32bc58be0..c8f6ac7c9 100644
--- a/Makefile
+++ b/Makefile
@@ -27,7 +27,10 @@ interface_definitions: $(config_xml_obj)
find $(BUILD_DIR)/interface-definitions -type f -name "*.xml" | xargs -I {} $(CURDIR)/scripts/build-command-templates {} $(CURDIR)/schema/interface_definition.rng $(TMPL_DIR) || exit 1
# XXX: delete top level node.def's that now live in other packages
- rm -f $(TMPL_DIR)/firewall/node.def
+ # IPSec VPN EAP-RADIUS does not support source-address
+ rm -rf $(TMPL_DIR)/vpn/ipsec/remote-access/radius/source-address
+ # T3568: firewall is yet not migrated to XML and Python - this is only a dummy
+ rm -rf $(TMPL_DIR)/firewall/node.def
rm -rf $(TMPL_DIR)/nfirewall
# XXX: test if there are empty node.def files - this is not allowed as these
# could mask help strings or mandatory priority statements
diff --git a/data/templates/frr/bgpd.frr.tmpl b/data/templates/frr/bgpd.frr.tmpl
index c21e7f234..aa297876b 100644
--- a/data/templates/frr/bgpd.frr.tmpl
+++ b/data/templates/frr/bgpd.frr.tmpl
@@ -65,6 +65,9 @@
{% if config.shutdown is defined %}
neighbor {{ neighbor }} shutdown
{% endif %}
+{% if config.solo is defined %}
+ neighbor {{ neighbor }} solo
+{% endif %}
{% if config.strict_capability_match is defined %}
neighbor {{ neighbor }} strict-capability-match
{% endif %}
diff --git a/data/templates/ipsec/charon/dhcp.conf.tmpl b/data/templates/ipsec/charon/dhcp.conf.tmpl
index 96dfd7633..92774b275 100644
--- a/data/templates/ipsec/charon/dhcp.conf.tmpl
+++ b/data/templates/ipsec/charon/dhcp.conf.tmpl
@@ -1,11 +1,11 @@
dhcp {
load = yes
-{% if options is defined and options.remote_access is defined and options.remote_access.dhcp is defined %}
-{% if options.remote_access.dhcp.interface is defined %}
- interface = {{ options.remote_access.dhcp.interface }}
+{% if remote_access is defined and remote_access.dhcp is defined %}
+{% if remote_access.dhcp.interface is defined %}
+ interface = {{ remote_access.dhcp.interface }}
{% endif %}
-{% if options.remote_access.dhcp.server is defined %}
- server = {{ options.remote_access.dhcp.server }}
+{% if remote_access.dhcp.server is defined %}
+ server = {{ remote_access.dhcp.server }}
{% endif %}
{% endif %}
diff --git a/data/templates/ipsec/charon/eap-radius.conf.tmpl b/data/templates/ipsec/charon/eap-radius.conf.tmpl
new file mode 100644
index 000000000..5ec35c988
--- /dev/null
+++ b/data/templates/ipsec/charon/eap-radius.conf.tmpl
@@ -0,0 +1,115 @@
+eap-radius {
+ # Send RADIUS accounting information to RADIUS servers.
+ # accounting = no
+
+ # Close the IKE_SA if there is a timeout during interim RADIUS accounting
+ # updates.
+ # accounting_close_on_timeout = yes
+
+ # Interval in seconds for interim RADIUS accounting updates, if not
+ # specified by the RADIUS server in the Access-Accept message.
+ # accounting_interval = 0
+
+ # If enabled, accounting is disabled unless an IKE_SA has at least one
+ # virtual IP. Only for IKEv2, for IKEv1 a virtual IP is strictly necessary.
+ # accounting_requires_vip = no
+
+ # If enabled, adds the Class attributes received in Access-Accept message to
+ # the RADIUS accounting messages.
+ # accounting_send_class = no
+
+ # Use class attributes in Access-Accept messages as group membership
+ # information.
+ # class_group = no
+
+ # Closes all IKE_SAs if communication with the RADIUS server times out. If
+ # it is not set only the current IKE_SA is closed.
+ # close_all_on_timeout = no
+
+ # Send EAP-Start instead of EAP-Identity to start RADIUS conversation.
+ # eap_start = no
+
+ # Use filter_id attribute as group membership information.
+ # filter_id = no
+
+ # Prefix to EAP-Identity, some AAA servers use a IMSI prefix to select the
+ # EAP method.
+ # id_prefix =
+
+ # Whether to load the plugin. Can also be an integer to increase the
+ # priority of this plugin.
+ load = yes
+
+ # NAS-Identifier to include in RADIUS messages.
+ nas_identifier = {{ remote_access.radius.nas_identifier if remote_access is defined and remote_access.radius is defined and remote_access.radius.nas_identifier is defined else 'strongSwan' }}
+
+ # Port of RADIUS server (authentication).
+ # port = 1812
+
+ # Base to use for calculating exponential back off.
+ # retransmit_base = 1.4
+
+ # Timeout in seconds before sending first retransmit.
+ # retransmit_timeout = 2.0
+
+ # Number of times to retransmit a packet before giving up.
+ # retransmit_tries = 4
+
+ # Shared secret between RADIUS and NAS. If set, make sure to adjust the
+ # permissions of the config file accordingly.
+ # secret =
+
+ # IP/Hostname of RADIUS server.
+ # server =
+
+ # Number of sockets (ports) to use, increase for high load.
+ # sockets = 1
+
+ # Whether to include the UDP port in the Called- and Calling-Station-Id
+ # RADIUS attributes.
+ # station_id_with_port = yes
+
+ dae {
+ # Enables support for the Dynamic Authorization Extension (RFC 5176).
+ # enable = no
+
+ # Address to listen for DAE messages from the RADIUS server.
+ # listen = 0.0.0.0
+
+ # Port to listen for DAE requests.
+ # port = 3799
+
+ # Shared secret used to verify/sign DAE messages. If set, make sure to
+ # adjust the permissions of the config file accordingly.
+ # secret =
+ }
+
+ forward {
+ # RADIUS attributes to be forwarded from IKEv2 to RADIUS.
+ # ike_to_radius =
+
+ # Same as ike_to_radius but from RADIUS to IKEv2.
+ # radius_to_ike =
+ }
+
+ # Section to specify multiple RADIUS servers.
+ servers {
+{% if remote_access is defined and remote_access.radius is defined and remote_access.radius.server is defined %}
+{% for server, server_options in remote_access.radius.server.items() if server_options.disable is not defined %}
+ {{ server | replace('.', '-') }} {
+ address = {{ server }}
+ secret = {{ server_options.key }}
+ auth_port = {{ server_options.port }}
+{% if server_options.disable_accounting is not defined %}
+ acct_port = {{ server_options.port | int +1 }}
+{% endif %}
+ sockets = 20
+ }
+{% endfor %}
+{% endif %}
+ }
+
+ # Section to configure multiple XAuth authentication rounds via RADIUS.
+ xauth {
+ }
+}
diff --git a/data/templates/ipsec/ios_profile.tmpl b/data/templates/ipsec/ios_profile.tmpl
index 49e8b0992..af6c79d6e 100644
--- a/data/templates/ipsec/ios_profile.tmpl
+++ b/data/templates/ipsec/ios_profile.tmpl
@@ -58,35 +58,29 @@
<!-- The client uses EAP to authenticate -->
<key>ExtendedAuthEnabled</key>
<integer>1</integer>
-{% if ike_proposal is defined and ike_proposal is not none %}
<!-- The next two dictionaries are optional (as are the keys in them), but it is recommended to specify them as the default is to use 3DES.
IMPORTANT: Because only one proposal is sent (even if nothing is configured here) it must match the server configuration -->
<key>IKESecurityAssociationParameters</key>
-{% for ike, ike_config in ike_proposal.items() %}
<dict>
<!-- @see https://developer.apple.com/documentation/networkextension/nevpnikev2encryptionalgorithm -->
<key>EncryptionAlgorithm</key>
- <string>{{ ike_config.encryption | upper }}</string>
+ <string>{{ ike_encryption.encryption }}</string>
<!-- @see https://developer.apple.com/documentation/networkextension/nevpnikev2integrityalgorithm -->
<key>IntegrityAlgorithm</key>
- <string>{{ ike_config.hash | upper }}</string>
+ <string>{{ ike_encryption.hash }}</string>
<!-- @see https://developer.apple.com/documentation/networkextension/nevpnikev2diffiehellmangroup -->
<key>DiffieHellmanGroup</key>
- <integer>{{ ike_config.dh_group | upper }}
+ <integer>{{ ike_encryption.dh_group }}</integer>
</dict>
-{% endfor %}
-{% endif %}
-{% if esp_proposal is defined and esp_proposal is not none %}
<key>ChildSecurityAssociationParameters</key>
-{% for esp, esp_config in esp_proposal.items() %}
<dict>
<key>EncryptionAlgorithm</key>
- <string>{{ esp_config.encryption | upper }}</string>
+ <string>{{ esp_encryption.encryption }}</string>
<key>IntegrityAlgorithm</key>
- <string>{{ esp_config.hash | upper }}</string>
+ <string>{{ esp_encryption.hash }}</string>
+ <key>DiffieHellmanGroup</key>
+ <integer>{{ ike_encryption.dh_group }}</integer>
</dict>
-{% endfor %}
-{% endif %}
</dict>
</dict>
<!-- This payload is optional but it provides an easy way to install the CA certificate together with the configuration -->
diff --git a/data/templates/ipsec/swanctl/peer.tmpl b/data/templates/ipsec/swanctl/peer.tmpl
index 8e46e8892..32ead9e60 100644
--- a/data/templates/ipsec/swanctl/peer.tmpl
+++ b/data/templates/ipsec/swanctl/peer.tmpl
@@ -54,7 +54,7 @@
}
children {
{% if peer_conf.vti is defined and peer_conf.vti.bind is defined and peer_conf.tunnel is not defined %}
-{% set vti_esp = esp_group[peer_conf.vti.esp_group] if peer_conf.vti.esp_group is defined else None %}
+{% set vti_esp = esp_group[ peer_conf.vti.esp_group ] if peer_conf.vti.esp_group is defined else esp_group[ peer_conf.default_esp_group ] %}
peer_{{ name }}_vti {
esp_proposals = {{ vti_esp | get_esp_ike_cipher | join(',') }}
local_ts = 0.0.0.0/0,::/0
diff --git a/data/templates/ipsec/windows_profile.tmpl b/data/templates/ipsec/windows_profile.tmpl
new file mode 100644
index 000000000..8c26944be
--- /dev/null
+++ b/data/templates/ipsec/windows_profile.tmpl
@@ -0,0 +1,4 @@
+Remove-VpnConnection -Name "{{ vpn_name }}" -Force -PassThru
+
+Add-VpnConnection -Name "{{ vpn_name }}" -ServerAddress "{{ remote }}" -TunnelType "Ikev2"
+Set-VpnConnectionIPsecConfiguration -ConnectionName "{{ vpn_name }}" -AuthenticationTransformConstants {{ ike_encryption.encryption }} -CipherTransformConstants {{ ike_encryption.encryption }} -EncryptionMethod {{ esp_encryption.encryption }} -IntegrityCheckMethod {{ esp_encryption.hash }} -PfsGroup None -DHGroup "Group{{ ike_encryption.dh_group }}" -PassThru -Force
diff --git a/data/templates/router-advert/radvd.conf.tmpl b/data/templates/router-advert/radvd.conf.tmpl
index 9cc237512..88d066491 100644
--- a/data/templates/router-advert/radvd.conf.tmpl
+++ b/data/templates/router-advert/radvd.conf.tmpl
@@ -1,58 +1,64 @@
### Autogenerated by service_router-advert.py ###
{% if interface is defined and interface is not none %}
-{% for iface in interface %}
+{% for iface, iface_config in interface.items() %}
interface {{ iface }} {
IgnoreIfMissing on;
-{% if interface[iface].default_preference is defined and interface[iface].default_preference is not none %}
- AdvDefaultPreference {{ interface[iface].default_preference }};
+{% if iface_config.default_preference is defined and iface_config.default_preference is not none %}
+ AdvDefaultPreference {{ iface_config.default_preference }};
{% endif %}
-{% if interface[iface].managed_flag is defined and interface[iface].managed_flag is not none %}
- AdvManagedFlag {{ 'on' if interface[iface].managed_flag is defined else 'off' }};
+{% if iface_config.managed_flag is defined and iface_config.managed_flag is not none %}
+ AdvManagedFlag {{ 'on' if iface_config.managed_flag is defined else 'off' }};
{% endif %}
-{% if interface[iface].interval.max is defined and interface[iface].interval.max is not none %}
- MaxRtrAdvInterval {{ interface[iface].interval.max }};
+{% if iface_config.interval.max is defined and iface_config.interval.max is not none %}
+ MaxRtrAdvInterval {{ iface_config.interval.max }};
{% endif %}
-{% if interface[iface].interval.min is defined and interface[iface].interval.min is not none %}
- MinRtrAdvInterval {{ interface[iface].interval.min }};
+{% if iface_config.interval.min is defined and iface_config.interval.min is not none %}
+ MinRtrAdvInterval {{ iface_config.interval.min }};
{% endif %}
-{% if interface[iface].reachable_time is defined and interface[iface].reachable_time is not none %}
- AdvReachableTime {{ interface[iface].reachable_time }};
+{% if iface_config.reachable_time is defined and iface_config.reachable_time is not none %}
+ AdvReachableTime {{ iface_config.reachable_time }};
{% endif %}
- AdvIntervalOpt {{ 'off' if interface[iface].no_send_advert is defined else 'on' }};
- AdvSendAdvert {{ 'off' if interface[iface].no_send_advert is defined else 'on' }};
-{% if interface[iface].default_lifetime is defined %}
- AdvDefaultLifetime {{ interface[iface].default_lifetime }};
-{% endif %}
-{% if interface[iface].link_mtu is defined %}
- AdvLinkMTU {{ interface[iface].link_mtu }};
-{% endif %}
- AdvOtherConfigFlag {{ 'on' if interface[iface].other_config_flag is defined else 'off' }};
- AdvRetransTimer {{ interface[iface].retrans_timer }};
- AdvCurHopLimit {{ interface[iface].hop_limit }};
-{% if interface[iface].route is defined %}
-{% for route in interface[iface].route %}
+ AdvIntervalOpt {{ 'off' if iface_config.no_send_advert is defined else 'on' }};
+ AdvSendAdvert {{ 'off' if iface_config.no_send_advert is defined else 'on' }};
+{% if iface_config.default_lifetime is defined %}
+ AdvDefaultLifetime {{ iface_config.default_lifetime }};
+{% endif %}
+{% if iface_config.link_mtu is defined %}
+ AdvLinkMTU {{ iface_config.link_mtu }};
+{% endif %}
+ AdvOtherConfigFlag {{ 'on' if iface_config.other_config_flag is defined else 'off' }};
+ AdvRetransTimer {{ iface_config.retrans_timer }};
+ AdvCurHopLimit {{ iface_config.hop_limit }};
+{% if iface_config.route is defined %}
+{% for route, route_options in iface_config.route.items() %}
route {{ route }} {
-{% if interface[iface].route[route].valid_lifetime is defined %}
- AdvRouteLifetime {{ interface[iface].route[route].valid_lifetime }};
+{% if route_options.valid_lifetime is defined %}
+ AdvRouteLifetime {{ route_options.valid_lifetime }};
{% endif %}
-{% if interface[iface].route[route].route_preference is defined %}
- AdvRoutePreference {{ interface[iface].route[route].route_preference }};
+{% if route_options.route_preference is defined %}
+ AdvRoutePreference {{ route_options.route_preference }};
{% endif %}
- RemoveRoute {{ 'off' if interface[iface].route[route].no_remove_route is defined else 'on' }};
+ RemoveRoute {{ 'off' if route_options.no_remove_route is defined else 'on' }};
};
{% endfor %}
{% endif %}
-{% for prefix in interface[iface].prefix %}
+{% if iface_config.prefix is defined and iface_config.prefix is not none %}
+{% for prefix, prefix_options in iface_config.prefix.items() %}
prefix {{ prefix }} {
- AdvAutonomous {{ 'off' if interface[iface].prefix[prefix].no_autonomous_flag is defined else 'on' }};
- AdvValidLifetime {{ interface[iface].prefix[prefix].valid_lifetime }};
- AdvOnLink {{ 'off' if interface[iface].prefix[prefix].no_on_link_flag is defined else 'on' }};
- AdvPreferredLifetime {{ interface[iface].prefix[prefix].preferred_lifetime }};
+ AdvAutonomous {{ 'off' if prefix_options.no_autonomous_flag is defined else 'on' }};
+ AdvValidLifetime {{ prefix_options.valid_lifetime }};
+ AdvOnLink {{ 'off' if prefix_options.no_on_link_flag is defined else 'on' }};
+ AdvPreferredLifetime {{ prefix_options.preferred_lifetime }};
+ };
+{% endfor %}
+{% endif %}
+{% if iface_config.name_server is defined %}
+ RDNSS {{ iface_config.name_server | join(" ") }} {
};
-{% endfor %}
-{% if interface[iface].name_server is defined %}
- RDNSS {{ interface[iface].name_server | join(" ") }} {
+{% endif %}
+{% if iface_config.dnssl is defined %}
+ DNSSL {{ iface_config.dnssl | join(" ") }} {
};
{% endif %}
};
diff --git a/interface-definitions/include/accel-ppp/radius-additions.xml.i b/interface-definitions/include/accel-ppp/radius-additions.xml.i
index e65088c43..fdcff36bf 100644
--- a/interface-definitions/include/accel-ppp/radius-additions.xml.i
+++ b/interface-definitions/include/accel-ppp/radius-additions.xml.i
@@ -88,11 +88,7 @@
</properties>
<defaultValue>3</defaultValue>
</leafNode>
- <leafNode name="nas-identifier">
- <properties>
- <help>NAS-Identifier attribute sent to RADIUS</help>
- </properties>
- </leafNode>
+ #include <include/radius-nas-identifier.xml.i>
<leafNode name="nas-ip-address">
<properties>
<help>NAS-IP-Address attribute sent to RADIUS</help>
diff --git a/interface-definitions/include/bgp/protocol-common-config.xml.i b/interface-definitions/include/bgp/protocol-common-config.xml.i
index 37fc7259f..5080ce588 100644
--- a/interface-definitions/include/bgp/protocol-common-config.xml.i
+++ b/interface-definitions/include/bgp/protocol-common-config.xml.i
@@ -1038,6 +1038,12 @@
</leafNode>
#include <include/bgp/remote-as.xml.i>
#include <include/bgp/neighbor-shutdown.xml.i>
+ <leafNode name="solo">
+ <properties>
+ <help>Do not send back prefixes learned from the neighbor</help>
+ <valueless/>
+ </properties>
+ </leafNode>
<leafNode name="strict-capability-match">
<properties>
<help>Enable strict capability negotiation</help>
diff --git a/interface-definitions/include/radius-nas-identifier.xml.i b/interface-definitions/include/radius-nas-identifier.xml.i
new file mode 100644
index 000000000..8e6933cc0
--- /dev/null
+++ b/interface-definitions/include/radius-nas-identifier.xml.i
@@ -0,0 +1,7 @@
+<!-- include start from radius-nas-identifier.xml.i -->
+<leafNode name="nas-identifier">
+ <properties>
+ <help>NAS-Identifier attribute sent to RADIUS</help>
+ </properties>
+</leafNode>
+<!-- include end -->
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index 6851c0354..b994bdafc 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -29,14 +29,7 @@
#include <include/interface/interface-ipv6-options.xml.i>
#include <include/source-address-ipv4-ipv6.xml.i>
#include <include/interface/tunnel-remote.xml.i>
- <leafNode name="source-interface">
- <properties>
- <help>Physical Interface used for underlaying traffic</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- </properties>
- </leafNode>
+ #include <include/source-interface.xml.i>
<leafNode name="6rd-prefix">
<properties>
<help>6rd network prefix</help>
diff --git a/interface-definitions/vpn_ipsec.xml.in b/interface-definitions/vpn_ipsec.xml.in
index 4cd1936a2..165fdfdf3 100644
--- a/interface-definitions/vpn_ipsec.xml.in
+++ b/interface-definitions/vpn_ipsec.xml.in
@@ -630,40 +630,6 @@
<valueless/>
</properties>
</leafNode>
- <node name="remote-access">
- <properties>
- <help>remote-access global options</help>
- </properties>
- <children>
- <node name="dhcp">
- <properties>
- <help>DHCP pool options for remote-access</help>
- </properties>
- <children>
- <leafNode name="interface">
- <properties>
- <help>Interface with DHCP server to use</help>
- <completionHelp>
- <script>${vyos_completion_dir}/list_interfaces.py</script>
- </completionHelp>
- </properties>
- </leafNode>
- <leafNode name="server">
- <properties>
- <help>DHCP server address</help>
- <valueHelp>
- <format>ipv4</format>
- <description>DHCP server IPv4 address</description>
- </valueHelp>
- <constraint>
- <validator name="ipv4-address"/>
- </constraint>
- </properties>
- </leafNode>
- </children>
- </node>
- </children>
- </node>
</children>
</node>
<tagNode name="profile">
@@ -737,18 +703,22 @@
<properties>
<help>Client authentication mode</help>
<completionHelp>
- <list>eap-tls eap-mschapv2</list>
+ <list>eap-tls eap-mschapv2 eap-radius</list>
</completionHelp>
<valueHelp>
<format>eap-tls</format>
- <description>EAP-TLS</description>
+ <description>Client uses EAP-TLS authentication</description>
</valueHelp>
<valueHelp>
<format>eap-mschapv2</format>
- <description>EAP-MSCHAPv2</description>
+ <description>Client uses EAP-MSCHAPv2 authentication</description>
+ </valueHelp>
+ <valueHelp>
+ <format>eap-radius</format>
+ <description>Client uses EAP-RADIUS authentication</description>
</valueHelp>
<constraint>
- <regex>^(eap-tls|eap-mschapv2)$</regex>
+ <regex>^(eap-tls|eap-mschapv2|eap-radius)$</regex>
</constraint>
</properties>
<defaultValue>eap-mschapv2</defaultValue>
@@ -835,6 +805,33 @@
</leafNode>
</children>
</tagNode>
+ <node name="dhcp">
+ <properties>
+ <help>DHCP pool options for remote-access</help>
+ </properties>
+ <children>
+ <leafNode name="interface">
+ <properties>
+ <help>Interface with DHCP server to use</help>
+ <completionHelp>
+ <script>${vyos_completion_dir}/list_interfaces.py</script>
+ </completionHelp>
+ </properties>
+ </leafNode>
+ <leafNode name="server">
+ <properties>
+ <help>DHCP server address</help>
+ <valueHelp>
+ <format>ipv4</format>
+ <description>DHCP server IPv4 address</description>
+ </valueHelp>
+ <constraint>
+ <validator name="ipv4-address"/>
+ </constraint>
+ </properties>
+ </leafNode>
+ </children>
+ </node>
<tagNode name="pool">
<properties>
<help>IP address pool for remote-access users</help>
@@ -879,6 +876,17 @@
#include <include/accel-ppp/name-server.xml.i>
</children>
</tagNode>
+ #include <include/radius-server-ipv4.xml.i>
+ <node name="radius">
+ <children>
+ #include <include/radius-nas-identifier.xml.i>
+ <tagNode name="server">
+ <children>
+ #include <include/accel-ppp/radius-additions-disable-accounting.xml.i>
+ </children>
+ </tagNode>
+ </children>
+ </node>
</children>
</node>
<node name="site-to-site">
diff --git a/interface-definitions/vpn_l2tp.xml.in b/interface-definitions/vpn_l2tp.xml.in
index 6cf5218ff..cf31af70f 100644
--- a/interface-definitions/vpn_l2tp.xml.in
+++ b/interface-definitions/vpn_l2tp.xml.in
@@ -205,11 +205,7 @@
<help>Maximum number of tries to send Access-Request/Accounting-Request queries</help>
</properties>
</leafNode>
- <leafNode name="nas-identifier">
- <properties>
- <help>Value to send to RADIUS server in NAS-Identifier attribute and to be matched in DM/CoA requests.</help>
- </properties>
- </leafNode>
+ #include <include/radius-nas-identifier.xml.i>
<node name="dae-server">
<properties>
<help>IPv4 address and port to bind Dynamic Authorization Extension server (DM/CoA)</help>
diff --git a/op-mode-definitions/generate-ipsec-profile.xml.in b/op-mode-definitions/generate-ipsec-profile.xml.in
index d1e5efd20..be9227971 100644
--- a/op-mode-definitions/generate-ipsec-profile.xml.in
+++ b/op-mode-definitions/generate-ipsec-profile.xml.in
@@ -7,33 +7,49 @@
<help>Generate IPsec related configurations</help>
</properties>
<children>
- <tagNode name="mac-ios-profile">
+ <node name="profile">
<properties>
- <help>Generate Apple iOS profile from IPsec connection profile</help>
- <completionHelp>
- <path>vpn ipsec remote-access connection</path>
- </completionHelp>
+ <help>Generate IKEv2 IPSec remote-access VPN profiles</help>
</properties>
<children>
- <tagNode name="remote">
+ <tagNode name="ios-remote-access">
<properties>
- <help>Remote address where the client will connect to</help>
+ <help>Generate iOS profile for specified remote-access connection name</help>
<completionHelp>
- <list>&lt;fqdn&gt;</list>
- <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
+ <path>vpn ipsec remote-access connection</path>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6"</command>
<children>
- <tagNode name="name">
+ <tagNode name="remote">
<properties>
- <help>Connection name as seen in the VPN application</help>
+ <help>Remote address where the client will connect to</help>
<completionHelp>
- <list>&lt;name&gt;</list>
+ <list>&lt;fqdn&gt;</list>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --name "$8"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7"</command>
<children>
+ <tagNode name="name">
+ <properties>
+ <help>Connection name as seen in the VPN application</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9"</command>
+ <children>
+ <tagNode name="profile">
+ <properties>
+ <help>Profile name as seen under system profiles</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command>
+ </tagNode>
+ </children>
+ </tagNode>
<tagNode name="profile">
<properties>
<help>Profile name as seen under system profiles</help>
@@ -41,18 +57,40 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --name "$8" --profile "${10}"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9"</command>
+ <children>
+ <tagNode name="name">
+ <properties>
+ <help>Connection name as seen in the VPN application</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os ios --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command>
+ </tagNode>
+ </children>
</tagNode>
</children>
</tagNode>
- <tagNode name="profile">
+ </children>
+ </tagNode>
+ <tagNode name="windows-remote-access">
+ <properties>
+ <help>Generate iOS profile for specified remote-access connection name</help>
+ <completionHelp>
+ <path>vpn ipsec remote-access connection</path>
+ </completionHelp>
+ </properties>
+ <children>
+ <tagNode name="remote">
<properties>
- <help>Profile name as seen under system profiles</help>
+ <help>Remote address where the client will connect to</help>
<completionHelp>
- <list>&lt;name&gt;</list>
+ <list>&lt;fqdn&gt;</list>
+ <script>${vyos_completion_dir}/list_local_ips.sh --both</script>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --profile "$8"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7"</command>
<children>
<tagNode name="name">
<properties>
@@ -61,14 +99,45 @@
<list>&lt;name&gt;</list>
</completionHelp>
</properties>
- <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --connection "$4" --remote "$6" --profile "$8" --name "${10}"</command>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9"</command>
+ <children>
+ <tagNode name="profile">
+ <properties>
+ <help>Profile name as seen under system profiles</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --name "$9" --profile "${11}"</command>
+ </tagNode>
+ </children>
+ </tagNode>
+ <tagNode name="profile">
+ <properties>
+ <help>Profile name as seen under system profiles</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9"</command>
+ <children>
+ <tagNode name="name">
+ <properties>
+ <help>Connection name as seen in the VPN application</help>
+ <completionHelp>
+ <list>&lt;name&gt;</list>
+ </completionHelp>
+ </properties>
+ <command>${vyos_op_scripts_dir}/ikev2_profile_generator.py --os windows --connection "$5" --remote "$7" --profile "$9" --name "${11}"</command>
+ </tagNode>
+ </children>
</tagNode>
</children>
</tagNode>
</children>
</tagNode>
</children>
- </tagNode>
+ </node>
</children>
</node>
</children>
diff --git a/python/vyos/configsession.py b/python/vyos/configsession.py
index 670e6c7fc..f28ad09c5 100644
--- a/python/vyos/configsession.py
+++ b/python/vyos/configsession.py
@@ -10,14 +10,14 @@
# See the GNU Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License along with this library;
-# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+# if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
import os
import re
import sys
import subprocess
-from vyos.util import call
+from vyos.util import is_systemd_service_running
CLI_SHELL_API = '/bin/cli-shell-api'
SET = '/opt/vyatta/sbin/my_set'
@@ -73,8 +73,7 @@ def inject_vyos_env(env):
env['vyos_validators_dir'] = '/usr/libexec/vyos/validators'
# if running the vyos-configd daemon, inject the vyshim env var
- ret = call('systemctl is-active --quiet vyos-configd.service')
- if not ret:
+ if is_systemd_service_running('vyos-configd.service'):
env['vyshim'] = '/usr/sbin/vyshim'
return env
diff --git a/python/vyos/util.py b/python/vyos/util.py
index d5cd46a6c..59f9f1c44 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -804,3 +804,9 @@ def make_incremental_progressbar(increment: float):
# Ignore further calls.
while True:
yield
+
+def is_systemd_service_running(service):
+ """ Test is a specified systemd service is actually running.
+ Returns True if service is running, false otherwise. """
+ tmp = run(f'systemctl is-active --quiet {service}')
+ return bool((tmp == 0))
diff --git a/smoketest/configs/bgp-azure-ipsec-gateway b/smoketest/configs/bgp-azure-ipsec-gateway
index 0580f4ddc..ddcd459ae 100644
--- a/smoketest/configs/bgp-azure-ipsec-gateway
+++ b/smoketest/configs/bgp-azure-ipsec-gateway
@@ -342,35 +342,35 @@ vpn {
log-modes ike
}
site-to-site {
- peer 51.105.0.2 {
+ peer 51.105.0.1 {
authentication {
mode pre-shared-secret
pre-shared-secret averysecretpsktowardsazure
}
connection-type respond
+ default-esp-group ESP-AZURE
ike-group IKE-AZURE
ikev2-reauth inherit
local-address 192.0.2.189
vti {
bind vti51
- esp-group ESP-AZURE
}
}
- peer 51.105.0.3 {
+ peer 51.105.0.2 {
authentication {
mode pre-shared-secret
pre-shared-secret averysecretpsktowardsazure
}
connection-type respond
+ default-esp-group ESP-AZURE
ike-group IKE-AZURE
ikev2-reauth inherit
local-address 192.0.2.189
vti {
bind vti52
- esp-group ESP-AZURE
}
}
- peer 51.105.0.246 {
+ peer 51.105.0.3 {
authentication {
mode pre-shared-secret
pre-shared-secret averysecretpsktowardsazure
@@ -384,7 +384,7 @@ vpn {
esp-group ESP-AZURE
}
}
- peer 51.105.0.247 {
+ peer 51.105.0.4 {
authentication {
mode pre-shared-secret
pre-shared-secret averysecretpsktowardsazure
@@ -398,7 +398,7 @@ vpn {
esp-group ESP-AZURE
}
}
- peer 51.105.0.18 {
+ peer 51.105.0.5 {
authentication {
mode pre-shared-secret
pre-shared-secret averysecretpsktowardsazure
@@ -412,7 +412,7 @@ vpn {
esp-group ESP-AZURE
}
}
- peer 51.105.0.19 {
+ peer 51.105.0.6 {
authentication {
mode pre-shared-secret
pre-shared-secret averysecretpsktowardsazure
diff --git a/smoketest/scripts/cli/test_protocols_bgp.py b/smoketest/scripts/cli/test_protocols_bgp.py
index a1b3356ce..c3a2ffbf9 100755
--- a/smoketest/scripts/cli/test_protocols_bgp.py
+++ b/smoketest/scripts/cli/test_protocols_bgp.py
@@ -694,5 +694,21 @@ class TestProtocolsBGP(VyOSUnitTestSHIM.TestCase):
self.assertIn(f' neighbor {interface} activate', frrconfig)
self.assertIn(f' exit-address-family', frrconfig)
+ def test_bgp_13_solo(self):
+ remote_asn = str(int(ASN) + 150)
+ neighbor = '192.0.2.55'
+
+ self.cli_set(base_path + ['local-as', ASN])
+ self.cli_set(base_path + ['neighbor', neighbor, 'remote-as', remote_asn])
+ self.cli_set(base_path + ['neighbor', neighbor, 'solo'])
+
+ # commit changes
+ self.cli_commit()
+
+ # Verify FRR bgpd configuration
+ frrconfig = self.getFRRconfig(f'router bgp {ASN}')
+ self.assertIn(f'router bgp {ASN}', frrconfig)
+ self.assertIn(f' neighbor {neighbor} solo', frrconfig)
+
if __name__ == '__main__':
unittest.main(verbosity=2) \ No newline at end of file
diff --git a/smoketest/scripts/cli/test_service_router-advert.py b/smoketest/scripts/cli/test_service_router-advert.py
index b19c49c6e..26b4626c2 100755
--- a/smoketest/scripts/cli/test_service_router-advert.py
+++ b/smoketest/scripts/cli/test_service_router-advert.py
@@ -43,11 +43,10 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
self.cli_delete(base_path)
self.cli_commit()
- def test_single(self):
+ def test_common(self):
self.cli_set(base_path + ['prefix', '::/64', 'no-on-link-flag'])
self.cli_set(base_path + ['prefix', '::/64', 'no-autonomous-flag'])
self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity'])
- self.cli_set(base_path + ['dnssl', '2001:db8::1234'])
self.cli_set(base_path + ['other-config-flag'])
# commit changes
@@ -92,5 +91,28 @@ class TestServiceRADVD(VyOSUnitTestSHIM.TestCase):
# Check for running process
self.assertTrue(process_named_running('radvd'))
+ def test_dns(self):
+ nameserver = ['2001:db8::1', '2001:db8::2']
+ dnssl = ['vyos.net', 'vyos.io']
+
+ self.cli_set(base_path + ['prefix', '::/64', 'valid-lifetime', 'infinity'])
+ self.cli_set(base_path + ['other-config-flag'])
+
+ for ns in nameserver:
+ self.cli_set(base_path + ['name-server', ns])
+ for sl in dnssl:
+ self.cli_set(base_path + ['dnssl', sl])
+
+ # commit changes
+ self.cli_commit()
+
+ config = read_file(RADVD_CONF)
+
+ tmp = 'RDNSS ' + ' '.join(nameserver) + ' {'
+ self.assertIn(tmp, config)
+
+ tmp = 'DNSSL ' + ' '.join(dnssl) + ' {'
+ self.assertIn(tmp, config)
+
if __name__ == '__main__':
unittest.main(verbosity=2)
diff --git a/src/conf_mode/interfaces-loopback.py b/src/conf_mode/interfaces-loopback.py
index 30a27abb4..193334443 100755
--- a/src/conf_mode/interfaces-loopback.py
+++ b/src/conf_mode/interfaces-loopback.py
@@ -45,8 +45,8 @@ def generate(loopback):
return None
def apply(loopback):
- l = LoopbackIf(loopback['ifname'])
- if 'deleted' in loopback.keys():
+ l = LoopbackIf(**loopback)
+ if 'deleted' in loopback:
l.remove()
else:
l.update(loopback)
diff --git a/src/conf_mode/le_cert.py b/src/conf_mode/le_cert.py
index 755c89966..6e169a3d5 100755
--- a/src/conf_mode/le_cert.py
+++ b/src/conf_mode/le_cert.py
@@ -22,6 +22,7 @@ from vyos.config import Config
from vyos import ConfigError
from vyos.util import cmd
from vyos.util import call
+from vyos.util import is_systemd_service_running
from vyos import airbag
airbag.enable()
@@ -87,8 +88,7 @@ def generate(cert):
# certbot will attempt to reload nginx, even with 'certonly';
# start nginx if not active
- ret = call('systemctl is-active --quiet nginx.service')
- if ret:
+ if not is_systemd_service_running('nginx.service'):
call('systemctl start nginx.service')
request_certbot(cert)
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 1a2fabded..9ecfd07fe 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -57,6 +57,11 @@ def get_config(config=None):
if not conf.exists(base):
bgp.update({'deleted' : ''})
+ if not vrf:
+ # We are running in the default VRF context, thus we can not delete
+ # our main BGP instance if there are dependent BGP VRF instances.
+ bgp['dependent_vrfs'] = conf.get_config_dict(['vrf', 'name'],
+ key_mangling=('-', '_'), get_first_key=True, no_tag_node_value_mangle=True)
return bgp
# We also need some additional information from the config, prefix-lists
@@ -96,6 +101,11 @@ def verify_remote_as(peer_config, bgp_config):
def verify(bgp):
if not bgp or 'deleted' in bgp:
+ if 'dependent_vrfs' in bgp:
+ for vrf, vrf_options in bgp['dependent_vrfs'].items():
+ if dict_search('protocols.bgp', vrf_options) != None:
+ raise ConfigError('Cannot delete default BGP instance, ' \
+ 'dependent VRF instance(s) exist!')
return None
if 'local_as' not in bgp:
diff --git a/src/conf_mode/system-option.py b/src/conf_mode/system-option.py
index 454611c55..55cf6b142 100755
--- a/src/conf_mode/system-option.py
+++ b/src/conf_mode/system-option.py
@@ -24,6 +24,7 @@ from vyos.config import Config
from vyos.configdict import dict_merge
from vyos.template import render
from vyos.util import cmd
+from vyos.util import is_systemd_service_running
from vyos.validate import is_addr_assigned
from vyos.xml import defaults
from vyos import ConfigError
@@ -114,7 +115,7 @@ def apply(options):
if 'performance' in options:
cmd('systemctl restart tuned.service')
# wait until daemon has started before sending configuration
- while (int(os.system('systemctl is-active --quiet tuned.service')) != 0):
+ while (not is_systemd_service_running('tuned.service')):
sleep(0.250)
cmd('tuned-adm profile network-{performance}'.format(**options))
else:
diff --git a/src/conf_mode/vpn_ipsec.py b/src/conf_mode/vpn_ipsec.py
index f1c6b216b..11ff12e94 100755
--- a/src/conf_mode/vpn_ipsec.py
+++ b/src/conf_mode/vpn_ipsec.py
@@ -49,13 +49,14 @@ airbag.enable()
dhcp_wait_attempts = 2
dhcp_wait_sleep = 1
-swanctl_dir = '/etc/swanctl'
-ipsec_conf = '/etc/ipsec.conf'
-ipsec_secrets = '/etc/ipsec.secrets'
-charon_conf = '/etc/strongswan.d/charon.conf'
-charon_dhcp_conf = '/etc/strongswan.d/charon/dhcp.conf'
-interface_conf = '/etc/strongswan.d/interfaces_use.conf'
-swanctl_conf = f'{swanctl_dir}/swanctl.conf'
+swanctl_dir = '/etc/swanctl'
+ipsec_conf = '/etc/ipsec.conf'
+ipsec_secrets = '/etc/ipsec.secrets'
+charon_conf = '/etc/strongswan.d/charon.conf'
+charon_dhcp_conf = '/etc/strongswan.d/charon/dhcp.conf'
+charon_radius_conf = '/etc/strongswan.d/charon/eap-radius.conf'
+interface_conf = '/etc/strongswan.d/interfaces_use.conf'
+swanctl_conf = f'{swanctl_dir}/swanctl.conf'
default_install_routes = 'yes'
@@ -110,6 +111,12 @@ def get_config(config=None):
ipsec['remote_access']['connection'][rw] = dict_merge(default_values,
ipsec['remote_access']['connection'][rw])
+ if 'remote_access' in ipsec and 'radius' in ipsec['remote_access'] and 'server' in ipsec['remote_access']['radius']:
+ default_values = defaults(base + ['remote-access', 'radius', 'server'])
+ for server in ipsec['remote_access']['radius']['server']:
+ ipsec['remote_access']['radius']['server'][server] = dict_merge(default_values,
+ ipsec['remote_access']['radius']['server'][server])
+
ipsec['dhcp_no_address'] = {}
ipsec['install_routes'] = 'no' if conf.exists(base + ["options", "disable-route-autoinstall"]) else default_install_routes
ipsec['interface_change'] = leaf_node_changed(conf, base + ['interface'])
@@ -243,6 +250,11 @@ def verify(ipsec):
if 'ike_group' in ra_conf:
if 'ike_group' not in ipsec or ra_conf['ike_group'] not in ipsec['ike_group']:
raise ConfigError(f"Invalid ike-group on {name} remote-access config")
+
+ ike = ra_conf['ike_group']
+ if dict_search(f'ike_group.{ike}.key_exchange', ipsec) != 'ikev2':
+ raise ConfigError('IPSec remote-access connections requires IKEv2!')
+
else:
raise ConfigError(f"Missing ike-group on {name} remote-access config")
@@ -263,13 +275,19 @@ def verify(ipsec):
if 'pre_shared_secret' not in ra_conf['authentication']:
raise ConfigError(f"Missing pre-shared-key on {name} remote-access config")
+
+ if 'client_mode' in ra_conf['authentication']:
+ if ra_conf['authentication']['client_mode'] == 'eap-radius':
+ if 'radius' not in ipsec['remote_access'] or 'server' not in ipsec['remote_access']['radius'] or len(ipsec['remote_access']['radius']['server']) == 0:
+ raise ConfigError('RADIUS authentication requires at least one server')
+
if 'pool' in ra_conf:
if 'dhcp' in ra_conf['pool'] and len(ra_conf['pool']) > 1:
raise ConfigError(f'Can not use both DHCP and a predefined address pool for "{name}"!')
for pool in ra_conf['pool']:
if pool == 'dhcp':
- if dict_search('options.remote_access.dhcp.server', ipsec) == None:
+ if dict_search('remote_access.dhcp.server', ipsec) == None:
raise ConfigError('IPSec DHCP server is not configured!')
elif 'pool' not in ipsec['remote_access'] or pool not in ipsec['remote_access']['pool']:
@@ -297,6 +315,10 @@ def verify(ipsec):
if v4_addr_and_exclude or v6_addr_and_exclude:
raise ConfigError('Must use both IPv4 or IPv6 addresses for pool prefix and exclude prefixes!')
+ if 'radius' in ipsec['remote_access'] and 'server' in ipsec['remote_access']['radius']:
+ for server, server_config in ipsec['remote_access']['radius']['server'].items():
+ if 'key' not in server_config:
+ raise ConfigError(f'Missing RADIUS secret key for server "{server}"')
if 'site_to_site' in ipsec and 'peer' in ipsec['site_to_site']:
for peer, peer_conf in ipsec['site_to_site']['peer'].items():
@@ -449,7 +471,8 @@ def generate(ipsec):
cleanup_pki_files()
if not ipsec:
- for config_file in [ipsec_conf, ipsec_secrets, charon_dhcp_conf, interface_conf, swanctl_conf]:
+ for config_file in [ipsec_conf, ipsec_secrets, charon_dhcp_conf,
+ charon_radius_conf, interface_conf, swanctl_conf]:
if os.path.isfile(config_file):
os.unlink(config_file)
render(charon_conf, 'ipsec/charon.tmpl', {'install_routes': default_install_routes})
@@ -518,6 +541,7 @@ def generate(ipsec):
render(ipsec_secrets, 'ipsec/ipsec.secrets.tmpl', ipsec)
render(charon_conf, 'ipsec/charon.tmpl', ipsec)
render(charon_dhcp_conf, 'ipsec/charon/dhcp.conf.tmpl', ipsec)
+ render(charon_radius_conf, 'ipsec/charon/eap-radius.conf.tmpl', ipsec)
render(interface_conf, 'ipsec/interfaces_use.conf.tmpl', ipsec)
render(swanctl_conf, 'ipsec/swanctl.conf.tmpl', ipsec)
diff --git a/src/etc/sysctl.d/30-vyos-router.conf b/src/etc/sysctl.d/30-vyos-router.conf
index 8265e12dc..e03d3a29c 100644
--- a/src/etc/sysctl.d/30-vyos-router.conf
+++ b/src/etc/sysctl.d/30-vyos-router.conf
@@ -72,6 +72,12 @@ net.ipv4.conf.default.send_redirects=1
# Increase size of buffer for netlink
net.core.rmem_max=2097152
+# Remove IPv4 and IPv6 routes from forward information base when link goes down
+net.ipv4.conf.all.ignore_routes_with_linkdown=1
+net.ipv4.conf.default.ignore_routes_with_linkdown=1
+net.ipv6.conf.all.ignore_routes_with_linkdown=1
+net.ipv6.conf.default.ignore_routes_with_linkdown=1
+
# Enable packet forwarding for IPv6
net.ipv6.conf.all.forwarding=1
@@ -81,6 +87,7 @@ net.ipv6.route.max_size = 262144
# Do not forget IPv6 addresses when a link goes down
net.ipv6.conf.default.keep_addr_on_down=1
net.ipv6.conf.all.keep_addr_on_down=1
+net.ipv6.route.skip_notify_on_dev_down=1
# Default value of 20 seems to interfere with larger OSPF and VRRP setups
net.ipv4.igmp_max_memberships = 512
diff --git a/src/migration-scripts/interfaces/19-to-20 b/src/migration-scripts/interfaces/19-to-20
index 06e07572f..e96663e54 100755
--- a/src/migration-scripts/interfaces/19-to-20
+++ b/src/migration-scripts/interfaces/19-to-20
@@ -18,62 +18,6 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
-def migrate_ospf(config, path, interface):
- path = path + ['ospf']
- if config.exists(path):
- new_base = ['protocols', 'ospf', 'interface']
- config.set(new_base)
- config.set_tag(new_base)
- config.copy(path, new_base + [interface])
- config.delete(path)
-
- # if "ip ospf" was the only setting, we can clean out the empty
- # ip node afterwards
- if len(config.list_nodes(path[:-1])) == 0:
- config.delete(path[:-1])
-
-def migrate_ospfv3(config, path, interface):
- path = path + ['ospfv3']
- if config.exists(path):
- new_base = ['protocols', 'ospfv3', 'interface']
- config.set(new_base)
- config.set_tag(new_base)
- config.copy(path, new_base + [interface])
- config.delete(path)
-
- # if "ipv6 ospfv3" was the only setting, we can clean out the empty
- # ip node afterwards
- if len(config.list_nodes(path[:-1])) == 0:
- config.delete(path[:-1])
-
-def migrate_rip(config, path, interface):
- path = path + ['rip']
- if config.exists(path):
- new_base = ['protocols', 'rip', 'interface']
- config.set(new_base)
- config.set_tag(new_base)
- config.copy(path, new_base + [interface])
- config.delete(path)
-
- # if "ip rip" was the only setting, we can clean out the empty
- # ip node afterwards
- if len(config.list_nodes(path[:-1])) == 0:
- config.delete(path[:-1])
-
-def migrate_ripng(config, path, interface):
- path = path + ['ripng']
- if config.exists(path):
- new_base = ['protocols', 'ripng', 'interface']
- config.set(new_base)
- config.set_tag(new_base)
- config.copy(path, new_base + [interface])
- config.delete(path)
-
- # if "ipv6 ripng" was the only setting, we can clean out the empty
- # ip node afterwards
- if len(config.list_nodes(path[:-1])) == 0:
- config.delete(path[:-1])
-
if __name__ == '__main__':
if (len(argv) < 1):
print("Must specify file name!")
@@ -85,57 +29,29 @@ if __name__ == '__main__':
config = ConfigTree(config_file)
- #
- # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0"
- #
- for type in config.list_nodes(['interfaces']):
- for interface in config.list_nodes(['interfaces', type]):
- ip_base = ['interfaces', type, interface, 'ip']
- ipv6_base = ['interfaces', type, interface, 'ipv6']
- migrate_rip(config, ip_base, interface)
- migrate_ripng(config, ipv6_base, interface)
- migrate_ospf(config, ip_base, interface)
- migrate_ospfv3(config, ipv6_base, interface)
-
- vif_path = ['interfaces', type, interface, 'vif']
- if config.exists(vif_path):
- for vif in config.list_nodes(vif_path):
- vif_ip_base = vif_path + [vif, 'ip']
- vif_ipv6_base = vif_path + [vif, 'ipv6']
- ifname = f'{interface}.{vif}'
-
- migrate_rip(config, vif_ip_base, ifname)
- migrate_ripng(config, vif_ipv6_base, ifname)
- migrate_ospf(config, vif_ip_base, ifname)
- migrate_ospfv3(config, vif_ipv6_base, ifname)
-
-
- vif_s_path = ['interfaces', type, interface, 'vif-s']
- if config.exists(vif_s_path):
- for vif_s in config.list_nodes(vif_s_path):
- vif_s_ip_base = vif_s_path + [vif_s, 'ip']
- vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6']
-
- # vif-c interfaces MUST be migrated before their parent vif-s
- # interface as the migrate_*() functions delete the path!
- vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
- if config.exists(vif_c_path):
- for vif_c in config.list_nodes(vif_c_path):
- vif_c_ip_base = vif_c_path + [vif_c, 'ip']
- vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6']
- ifname = f'{interface}.{vif_s}.{vif_c}'
-
- migrate_rip(config, vif_c_ip_base, ifname)
- migrate_ripng(config, vif_c_ipv6_base, ifname)
- migrate_ospf(config, vif_c_ip_base, ifname)
- migrate_ospfv3(config, vif_c_ipv6_base, ifname)
-
-
- ifname = f'{interface}.{vif_s}'
- migrate_rip(config, vif_s_ip_base, ifname)
- migrate_ripng(config, vif_s_ipv6_base, ifname)
- migrate_ospf(config, vif_s_ip_base, ifname)
- migrate_ospfv3(config, vif_s_ipv6_base, ifname)
+ for type in ['tunnel', 'l2tpv3']:
+ base = ['interfaces', type]
+ if not config.exists(base):
+ # Nothing to do
+ continue
+
+ for interface in config.list_nodes(base):
+ # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap
+ encap_path = base + [interface, 'encapsulation']
+ if type == 'tunnel' and config.exists(encap_path):
+ tmp = config.return_value(encap_path)
+ if tmp == 'gre-bridge':
+ config.set(encap_path, value='gretap')
+
+ # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address
+ # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote
+ local_ip_path = base + [interface, 'local-ip']
+ if config.exists(local_ip_path):
+ config.rename(local_ip_path, 'source-address')
+
+ remote_ip_path = base + [interface, 'remote-ip']
+ if config.exists(remote_ip_path):
+ config.rename(remote_ip_path, 'remote')
try:
with open(file_name, 'w') as f:
diff --git a/src/migration-scripts/interfaces/20-to-21 b/src/migration-scripts/interfaces/20-to-21
index e96663e54..06e07572f 100755
--- a/src/migration-scripts/interfaces/20-to-21
+++ b/src/migration-scripts/interfaces/20-to-21
@@ -18,6 +18,62 @@ from sys import argv
from sys import exit
from vyos.configtree import ConfigTree
+def migrate_ospf(config, path, interface):
+ path = path + ['ospf']
+ if config.exists(path):
+ new_base = ['protocols', 'ospf', 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(path, new_base + [interface])
+ config.delete(path)
+
+ # if "ip ospf" was the only setting, we can clean out the empty
+ # ip node afterwards
+ if len(config.list_nodes(path[:-1])) == 0:
+ config.delete(path[:-1])
+
+def migrate_ospfv3(config, path, interface):
+ path = path + ['ospfv3']
+ if config.exists(path):
+ new_base = ['protocols', 'ospfv3', 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(path, new_base + [interface])
+ config.delete(path)
+
+ # if "ipv6 ospfv3" was the only setting, we can clean out the empty
+ # ip node afterwards
+ if len(config.list_nodes(path[:-1])) == 0:
+ config.delete(path[:-1])
+
+def migrate_rip(config, path, interface):
+ path = path + ['rip']
+ if config.exists(path):
+ new_base = ['protocols', 'rip', 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(path, new_base + [interface])
+ config.delete(path)
+
+ # if "ip rip" was the only setting, we can clean out the empty
+ # ip node afterwards
+ if len(config.list_nodes(path[:-1])) == 0:
+ config.delete(path[:-1])
+
+def migrate_ripng(config, path, interface):
+ path = path + ['ripng']
+ if config.exists(path):
+ new_base = ['protocols', 'ripng', 'interface']
+ config.set(new_base)
+ config.set_tag(new_base)
+ config.copy(path, new_base + [interface])
+ config.delete(path)
+
+ # if "ipv6 ripng" was the only setting, we can clean out the empty
+ # ip node afterwards
+ if len(config.list_nodes(path[:-1])) == 0:
+ config.delete(path[:-1])
+
if __name__ == '__main__':
if (len(argv) < 1):
print("Must specify file name!")
@@ -29,29 +85,57 @@ if __name__ == '__main__':
config = ConfigTree(config_file)
- for type in ['tunnel', 'l2tpv3']:
- base = ['interfaces', type]
- if not config.exists(base):
- # Nothing to do
- continue
-
- for interface in config.list_nodes(base):
- # Migrate "interface tunnel <tunX> encapsulation gre-bridge" to gretap
- encap_path = base + [interface, 'encapsulation']
- if type == 'tunnel' and config.exists(encap_path):
- tmp = config.return_value(encap_path)
- if tmp == 'gre-bridge':
- config.set(encap_path, value='gretap')
-
- # Migrate "interface tunnel|l2tpv3 <interface> local-ip" to source-address
- # Migrate "interface tunnel|l2tpv3 <interface> remote-ip" to remote
- local_ip_path = base + [interface, 'local-ip']
- if config.exists(local_ip_path):
- config.rename(local_ip_path, 'source-address')
-
- remote_ip_path = base + [interface, 'remote-ip']
- if config.exists(remote_ip_path):
- config.rename(remote_ip_path, 'remote')
+ #
+ # Migrate "interface ethernet eth0 ip ospf" to "protocols ospf interface eth0"
+ #
+ for type in config.list_nodes(['interfaces']):
+ for interface in config.list_nodes(['interfaces', type]):
+ ip_base = ['interfaces', type, interface, 'ip']
+ ipv6_base = ['interfaces', type, interface, 'ipv6']
+ migrate_rip(config, ip_base, interface)
+ migrate_ripng(config, ipv6_base, interface)
+ migrate_ospf(config, ip_base, interface)
+ migrate_ospfv3(config, ipv6_base, interface)
+
+ vif_path = ['interfaces', type, interface, 'vif']
+ if config.exists(vif_path):
+ for vif in config.list_nodes(vif_path):
+ vif_ip_base = vif_path + [vif, 'ip']
+ vif_ipv6_base = vif_path + [vif, 'ipv6']
+ ifname = f'{interface}.{vif}'
+
+ migrate_rip(config, vif_ip_base, ifname)
+ migrate_ripng(config, vif_ipv6_base, ifname)
+ migrate_ospf(config, vif_ip_base, ifname)
+ migrate_ospfv3(config, vif_ipv6_base, ifname)
+
+
+ vif_s_path = ['interfaces', type, interface, 'vif-s']
+ if config.exists(vif_s_path):
+ for vif_s in config.list_nodes(vif_s_path):
+ vif_s_ip_base = vif_s_path + [vif_s, 'ip']
+ vif_s_ipv6_base = vif_s_path + [vif_s, 'ipv6']
+
+ # vif-c interfaces MUST be migrated before their parent vif-s
+ # interface as the migrate_*() functions delete the path!
+ vif_c_path = ['interfaces', type, interface, 'vif-s', vif_s, 'vif-c']
+ if config.exists(vif_c_path):
+ for vif_c in config.list_nodes(vif_c_path):
+ vif_c_ip_base = vif_c_path + [vif_c, 'ip']
+ vif_c_ipv6_base = vif_c_path + [vif_c, 'ipv6']
+ ifname = f'{interface}.{vif_s}.{vif_c}'
+
+ migrate_rip(config, vif_c_ip_base, ifname)
+ migrate_ripng(config, vif_c_ipv6_base, ifname)
+ migrate_ospf(config, vif_c_ip_base, ifname)
+ migrate_ospfv3(config, vif_c_ipv6_base, ifname)
+
+
+ ifname = f'{interface}.{vif_s}'
+ migrate_rip(config, vif_s_ip_base, ifname)
+ migrate_ripng(config, vif_s_ipv6_base, ifname)
+ migrate_ospf(config, vif_s_ip_base, ifname)
+ migrate_ospfv3(config, vif_s_ipv6_base, ifname)
try:
with open(file_name, 'w') as f:
diff --git a/src/op_mode/ikev2_profile_generator.py b/src/op_mode/ikev2_profile_generator.py
index 4ff37341c..d45525431 100755
--- a/src/op_mode/ikev2_profile_generator.py
+++ b/src/op_mode/ikev2_profile_generator.py
@@ -19,17 +19,99 @@ import argparse
from jinja2 import Template
from sys import exit
from socket import getfqdn
+from cryptography.x509.oid import NameOID
from vyos.config import Config
-from vyos.template import render_to_string
-from cryptography.x509.oid import NameOID
from vyos.pki import load_certificate
+from vyos.template import render_to_string
+from vyos.util import ask_input
+
+# Apple profiles only support one IKE/ESP encryption cipher and hash, whereas
+# VyOS comes with a multitude of different proposals for a connection.
+#
+# We take all available proposals from the VyOS CLI and ask the user which one
+# he would like to get enabled in his profile - thus there is limited possibility
+# to select a proposal that is not supported on the connection profile.
+#
+# IOS supports IKE-SA encryption algorithms:
+# - DES
+# - 3DES
+# - AES-128
+# - AES-256
+# - AES-128-GCM
+# - AES-256-GCM
+# - ChaCha20Poly1305
+#
+vyos2apple_cipher = {
+ '3des' : '3DES',
+ 'aes128' : 'AES-128',
+ 'aes256' : 'AES-256',
+ 'aes128gcm128' : 'AES-128-GCM',
+ 'aes256gcm128' : 'AES-256-GCM',
+ 'chacha20poly1305' : 'ChaCha20Poly1305',
+}
+
+# Windows supports IKE-SA encryption algorithms:
+# - DES3
+# - AES128
+# - AES192
+# - AES256
+# - GCMAES128
+# - GCMAES192
+# - GCMAES256
+#
+vyos2windows_cipher = {
+ '3des' : 'DES3',
+ 'aes128' : 'AES128',
+ 'aes192' : 'AES192',
+ 'aes256' : 'AES256',
+ 'aes128gcm128' : 'GCMAES128',
+ 'aes192gcm128' : 'GCMAES192',
+ 'aes256gcm128' : 'GCMAES256',
+}
+
+# IOS supports IKE-SA integrity algorithms:
+# - SHA1-96
+# - SHA1-160
+# - SHA2-256
+# - SHA2-384
+# - SHA2-512
+#
+vyos2apple_integrity = {
+ 'sha1' : 'SHA1-96',
+ 'sha1_160' : 'SHA1-160',
+ 'sha256' : 'SHA2-256',
+ 'sha384' : 'SHA2-384',
+ 'sha512' : 'SHA2-512',
+}
+
+# Windows supports IKE-SA integrity algorithms:
+# - SHA1-96
+# - SHA1-160
+# - SHA2-256
+# - SHA2-384
+# - SHA2-512
+#
+vyos2windows_integrity = {
+ 'sha1' : 'SHA196',
+ 'sha256' : 'SHA256',
+ 'aes128gmac' : 'GCMAES128',
+ 'aes192gmac' : 'GCMAES192',
+ 'aes256gmac' : 'GCMAES256',
+}
+
+# IOS 14.2 and later do no support dh-group 1,2 and 5. Supported DH groups would
+# be: 14, 15, 16, 17, 18, 19, 20, 21, 31
+ios_supported_dh_groups = ['14', '15', '16', '17', '18', '19', '20', '21', '31']
+# Windows 10 only allows a limited set of DH groups
+windows_supported_dh_groups = ['1', '2', '14', '24']
parser = argparse.ArgumentParser()
-parser.add_argument("--connection", action="store", help="IPsec IKEv2 remote-access connection name from CLI", required=True)
-parser.add_argument("--remote", action="store", help="VPN connection remote-address where the client will connect to", required=True)
-parser.add_argument("--profile", action="store", help="IKEv2 profile name used in the profile list on the device")
-parser.add_argument("--name", action="store", help="VPN connection name as seen in the VPN application later")
+parser.add_argument('--os', const='all', nargs='?', choices=['ios', 'windows'], help='Operating system used for config generation', required=True)
+parser.add_argument("--connection", action="store", help='IPsec IKEv2 remote-access connection name from CLI', required=True)
+parser.add_argument("--remote", action="store", help='VPN connection remote-address where the client will connect to', required=True)
+parser.add_argument("--profile", action="store", help='IKEv2 profile name used in the profile list on the device')
+parser.add_argument("--name", action="store", help='VPN connection name as seen in the VPN application later')
args = parser.parse_args()
ipsec_base = ['vpn', 'ipsec']
@@ -43,7 +125,7 @@ profile_name = 'VyOS IKEv2 Profile'
if args.profile:
profile_name = args.profile
-vpn_name = 'VyOS IKEv2 Profile'
+vpn_name = 'VyOS IKEv2 VPN'
if args.name:
vpn_name = args.name
@@ -73,7 +155,76 @@ data['ca_cn'] = ca_cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].v
data['cert_cn'] = cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value
data['ca_cert'] = conf.return_value(pki_base + ['ca', ca_name, 'certificate'])
-data['esp_proposal'] = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'], key_mangling=('-', '_'), get_first_key=True)
-data['ike_proposal'] = conf.get_config_dict(ipsec_base + ['ike-group', data['ike_group'], 'proposal'], key_mangling=('-', '_'), get_first_key=True)
+esp_proposals = conf.get_config_dict(ipsec_base + ['esp-group', data['esp_group'], 'proposal'],
+ key_mangling=('-', '_'), get_first_key=True)
+ike_proposal = conf.get_config_dict(ipsec_base + ['ike-group', data['ike_group'], 'proposal'],
+ key_mangling=('-', '_'), get_first_key=True)
+
+
+# This script works only for Apple iOS/iPadOS and Windows. Both operating systems
+# have different limitations thus we load the limitations based on the operating
+# system used.
+
+vyos2client_cipher = vyos2apple_cipher if args.os == 'ios' else vyos2windows_cipher;
+vyos2client_integrity = vyos2apple_integrity if args.os == 'ios' else vyos2windows_integrity;
+supported_dh_groups = ios_supported_dh_groups if args.os == 'ios' else windows_supported_dh_groups;
+
+# Create a dictionary containing client conform IKE settings
+ike = {}
+count = 1
+for _, proposal in ike_proposal.items():
+ if {'dh_group', 'encryption', 'hash'} <= set(proposal):
+ if (proposal['encryption'] in set(vyos2client_cipher) and
+ proposal['hash'] in set(vyos2client_integrity) and
+ proposal['dh_group'] in set(supported_dh_groups)):
+
+ # We 're-code' from the VyOS IPSec proposals to the Apple naming scheme
+ proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
+ proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
+
+ ike.update( { str(count) : proposal } )
+ count += 1
+
+# Create a dictionary containing Apple conform ESP settings
+esp = {}
+count = 1
+for _, proposal in esp_proposals.items():
+ if {'encryption', 'hash'} <= set(proposal):
+ if proposal['encryption'] in set(vyos2client_cipher) and proposal['hash'] in set(vyos2client_integrity):
+ # We 're-code' from the VyOS IPSec proposals to the Apple naming scheme
+ proposal['encryption'] = vyos2client_cipher[ proposal['encryption'] ]
+ proposal['hash'] = vyos2client_integrity[ proposal['hash'] ]
+
+ esp.update( { str(count) : proposal } )
+ count += 1
+try:
+ if len(ike) > 1:
+ # Propare the input questions for the user
+ tmp = '\n'
+ for number, options in ike.items():
+ tmp += f'({number}) Encryption {options["encryption"]}, Integrity {options["hash"]}, DH group {options["dh_group"]}\n'
+ tmp += '\nSelect one of the above IKE groups: '
+ data['ike_encryption'] = ike[ ask_input(tmp, valid_responses=list(ike)) ]
+ else:
+ data['ike_encryption'] = ike['1']
+
+ if len(esp) > 1:
+ tmp = '\n'
+ for number, options in esp.items():
+ tmp += f'({number}) Encryption {options["encryption"]}, Integrity {options["hash"]}\n'
+ tmp += '\nSelect one of the above ESP groups: '
+ data['esp_encryption'] = esp[ ask_input(tmp, valid_responses=list(esp)) ]
+ else:
+ data['esp_encryption'] = esp['1']
+
+except KeyboardInterrupt:
+ exit("Interrupted")
-print(render_to_string('ipsec/ios_profile.tmpl', data))
+print('\n\n==== <snip> ====')
+if args.os == 'ios':
+ print(render_to_string('ipsec/ios_profile.tmpl', data))
+ print('==== </snip> ====\n')
+ print('Save the XML from above to a new file named "vyos.mobileconfig" and E-Mail it to your phone.')
+elif args.os == 'windows':
+ print(render_to_string('ipsec/windows_profile.tmpl', data))
+ print('==== </snip> ====\n')
diff --git a/src/op_mode/show_dhcp.py b/src/op_mode/show_dhcp.py
index ff1e3cc56..4df275e04 100755
--- a/src/op_mode/show_dhcp.py
+++ b/src/op_mode/show_dhcp.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -27,8 +27,7 @@ from datetime import datetime
from isc_dhcp_leases import Lease, IscDhcpLeases
from vyos.config import Config
-from vyos.util import call
-
+from vyos.util import is_systemd_service_running
lease_file = "/config/dhcpd.leases"
pool_key = "shared-networkname"
@@ -217,7 +216,7 @@ if __name__ == '__main__':
exit(0)
# if dhcp server is down, inactive leases may still be shown as active, so warn the user.
- if call('systemctl -q is-active isc-dhcp-server.service') != 0:
+ if not is_systemd_service_running('isc-dhcp-server.service'):
print("WARNING: DHCP server is configured but not started. Data may be stale.")
if args.leases:
diff --git a/src/op_mode/show_dhcpv6.py b/src/op_mode/show_dhcpv6.py
index f70f04298..1f987ff7b 100755
--- a/src/op_mode/show_dhcpv6.py
+++ b/src/op_mode/show_dhcpv6.py
@@ -1,6 +1,6 @@
#!/usr/bin/env python3
#
-# Copyright (C) 2018-2020 VyOS maintainers and contributors
+# Copyright (C) 2018-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
@@ -27,7 +27,7 @@ from datetime import datetime
from isc_dhcp_leases import Lease, IscDhcpLeases
from vyos.config import Config
-from vyos.util import call
+from vyos.util import is_systemd_service_running
lease_file = "/config/dhcpdv6.leases"
pool_key = "shared-networkname"
@@ -202,7 +202,7 @@ if __name__ == '__main__':
exit(0)
# if dhcp server is down, inactive leases may still be shown as active, so warn the user.
- if call('systemctl -q is-active isc-dhcp-server6.service') != 0:
+ if not is_systemd_service_running('isc-dhcp-server6.service'):
print("WARNING: DHCPv6 server is configured but not started. Data may be stale.")
if args.leases:
diff --git a/src/services/vyos-configd b/src/services/vyos-configd
index 6f770b696..670b6e66a 100755
--- a/src/services/vyos-configd
+++ b/src/services/vyos-configd
@@ -133,8 +133,7 @@ def explicit_print(path, mode, msg):
logger.critical("error explicit_print")
def run_script(script, config, args) -> int:
- if args:
- script.argv = args
+ script.argv = args
config.set_level([])
try:
c = script.get_config(config)
@@ -208,7 +207,7 @@ def process_node_data(config, data) -> int:
return R_ERROR_DAEMON
script_name = None
- args = None
+ args = []
res = re.match(r'^(VYOS_TAGNODE_VALUE=[^/]+)?.*\/([^/]+).py(.*)', data)
if res.group(1):
@@ -221,7 +220,7 @@ def process_node_data(config, data) -> int:
return R_ERROR_DAEMON
if res.group(3):
args = res.group(3).split()
- args.insert(0, f'{script_name}.py')
+ args.insert(0, f'{script_name}.py')
if script_name not in include_set:
return R_PASS
diff --git a/src/systemd/isc-dhcp-server.service b/src/systemd/isc-dhcp-server.service
index 9aa70a7cc..a7d86e69c 100644
--- a/src/systemd/isc-dhcp-server.service
+++ b/src/systemd/isc-dhcp-server.service
@@ -14,10 +14,10 @@ Environment=PID_FILE=/run/dhcp-server/dhcpd.pid CONFIG_FILE=/run/dhcp-server/dhc
PIDFile=/run/dhcp-server/dhcpd.pid
ExecStartPre=/bin/sh -ec '\
touch ${LEASE_FILE}; \
-chown dhcpd:nogroup ${LEASE_FILE}* ; \
+chown dhcpd:vyattacfg ${LEASE_FILE}* ; \
chmod 664 ${LEASE_FILE}* ; \
-/usr/sbin/dhcpd -4 -t -T -q -user dhcpd -group nogroup -pf ${PID_FILE} -cf ${CONFIG_FILE} -lf ${LEASE_FILE} '
-ExecStart=/usr/sbin/dhcpd -4 -q -user dhcpd -group nogroup -pf ${PID_FILE} -cf ${CONFIG_FILE} -lf ${LEASE_FILE}
+/usr/sbin/dhcpd -4 -t -T -q -user dhcpd -group vyattacfg -pf ${PID_FILE} -cf ${CONFIG_FILE} -lf ${LEASE_FILE} '
+ExecStart=/usr/sbin/dhcpd -4 -q -user dhcpd -group vyattacfg -pf ${PID_FILE} -cf ${CONFIG_FILE} -lf ${LEASE_FILE}
Restart=always
[Install]