summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/firewall.py1
-rwxr-xr-xsrc/conf_mode/interfaces_openvpn.py6
-rwxr-xr-xsrc/conf_mode/load-balancing_reverse-proxy.py8
-rwxr-xr-xsrc/conf_mode/pki.py36
-rwxr-xr-xsrc/conf_mode/protocols_bgp.py16
-rwxr-xr-xsrc/conf_mode/system_ip.py5
-rwxr-xr-xsrc/conf_mode/vrf.py14
-rw-r--r--src/etc/rsyslog.conf30
-rwxr-xr-xsrc/helpers/vyos-failover.py5
-rwxr-xr-xsrc/helpers/vyos-vrrp-conntracksync.sh4
-rwxr-xr-xsrc/init/vyos-router1
-rwxr-xr-xsrc/migration-scripts/firewall/14-to-1546
-rwxr-xr-xsrc/op_mode/connect_disconnect.py6
-rwxr-xr-xsrc/op_mode/image_installer.py25
-rwxr-xr-xsrc/op_mode/openvpn.py7
-rwxr-xr-xsrc/op_mode/pki.py4
-rwxr-xr-xsrc/op_mode/uptime.py2
17 files changed, 168 insertions, 48 deletions
diff --git a/src/conf_mode/firewall.py b/src/conf_mode/firewall.py
index 3cf618363..e96e57154 100755
--- a/src/conf_mode/firewall.py
+++ b/src/conf_mode/firewall.py
@@ -44,6 +44,7 @@ nftables_conf = '/run/nftables.conf'
sysfs_config = {
'all_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_all', 'enable': '0', 'disable': '1'},
'broadcast_ping': {'sysfs': '/proc/sys/net/ipv4/icmp_echo_ignore_broadcasts', 'enable': '0', 'disable': '1'},
+ 'directed_broadcast' : {'sysfs': '/proc/sys/net/ipv4/conf/all/bc_forwarding', 'enable': '1', 'disable': '0'},
'ip_src_route': {'sysfs': '/proc/sys/net/ipv4/conf/*/accept_source_route'},
'ipv6_receive_redirects': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_redirects'},
'ipv6_src_route': {'sysfs': '/proc/sys/net/ipv6/conf/*/accept_source_route', 'enable': '0', 'disable': '-1'},
diff --git a/src/conf_mode/interfaces_openvpn.py b/src/conf_mode/interfaces_openvpn.py
index 505ec55c6..0ecffd3be 100755
--- a/src/conf_mode/interfaces_openvpn.py
+++ b/src/conf_mode/interfaces_openvpn.py
@@ -198,6 +198,12 @@ def verify_pki(openvpn):
raise ConfigError(f'Cannot use encrypted private key on openvpn interface {interface}')
if 'dh_params' in tls:
+ if 'dh' not in pki:
+ raise ConfigError(f'pki dh is not configured')
+ proposed_dh = tls['dh_params']
+ if proposed_dh not in pki['dh'].keys():
+ raise ConfigError(f"pki dh '{proposed_dh}' is not configured")
+
pki_dh = pki['dh'][tls['dh_params']]
dh_params = load_dh_parameters(pki_dh['parameters'])
dh_numbers = dh_params.parameter_numbers()
diff --git a/src/conf_mode/load-balancing_reverse-proxy.py b/src/conf_mode/load-balancing_reverse-proxy.py
index 694a4e1ea..1569d8d71 100755
--- a/src/conf_mode/load-balancing_reverse-proxy.py
+++ b/src/conf_mode/load-balancing_reverse-proxy.py
@@ -75,6 +75,10 @@ def verify(lb):
raise ConfigError(f'"TCP" port "{tmp_port}" is used by another service')
for back, back_config in lb['backend'].items():
+ if 'http-check' in back_config:
+ http_check = back_config['http-check']
+ if 'expect' in http_check and 'status' in http_check['expect'] and 'string' in http_check['expect']:
+ raise ConfigError(f'"expect status" and "expect string" can not be configured together!')
if 'server' not in back_config:
raise ConfigError(f'"{back} server" must be configured!')
for bk_server, bk_server_conf in back_config['server'].items():
@@ -84,6 +88,10 @@ def verify(lb):
if {'send_proxy', 'send_proxy_v2'} <= set(bk_server_conf):
raise ConfigError(f'Cannot use both "send-proxy" and "send-proxy-v2" for server "{bk_server}"')
+ if 'ssl' in back_config:
+ if {'no_verify', 'ca_certificate'} <= set(back_config['ssl']):
+ raise ConfigError(f'backend {back} cannot have both ssl options no-verify and ca-certificate set!')
+
for front, front_config in lb['service'].items():
for cert in dict_search('ssl.certificate', front_config) or []:
verify_pki_certificate(lb, cert)
diff --git a/src/conf_mode/pki.py b/src/conf_mode/pki.py
index 3ab6ac5c3..8deec0e85 100755
--- a/src/conf_mode/pki.py
+++ b/src/conf_mode/pki.py
@@ -24,6 +24,8 @@ from vyos.config import config_dict_merge
from vyos.configdep import set_dependents
from vyos.configdep import call_dependents
from vyos.configdict import node_changed
+from vyos.configdiff import Diff
+from vyos.configdiff import get_config_diff
from vyos.defaults import directories
from vyos.pki import is_ca_certificate
from vyos.pki import load_certificate
@@ -136,32 +138,32 @@ def get_config(config=None):
if len(argv) > 1 and argv[1] == 'certbot_renew':
pki['certbot_renew'] = {}
- tmp = node_changed(conf, base + ['ca'], recursive=True)
+ tmp = node_changed(conf, base + ['ca'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'ca' : tmp})
- tmp = node_changed(conf, base + ['certificate'], recursive=True)
+ tmp = node_changed(conf, base + ['certificate'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'certificate' : tmp})
- tmp = node_changed(conf, base + ['dh'], recursive=True)
+ tmp = node_changed(conf, base + ['dh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'dh' : tmp})
- tmp = node_changed(conf, base + ['key-pair'], recursive=True)
+ tmp = node_changed(conf, base + ['key-pair'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'key_pair' : tmp})
- tmp = node_changed(conf, base + ['openssh'], recursive=True)
+ tmp = node_changed(conf, base + ['openssh'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'openssh' : tmp})
- tmp = node_changed(conf, base + ['openvpn', 'shared-secret'], recursive=True)
+ tmp = node_changed(conf, base + ['openvpn', 'shared-secret'], recursive=True, expand_nodes=Diff.DELETE | Diff.ADD)
if tmp:
if 'changed' not in pki: pki.update({'changed':{}})
pki['changed'].update({'openvpn' : tmp})
@@ -198,6 +200,7 @@ def get_config(config=None):
pki['system'] = conf.get_config_dict([], key_mangling=('-', '_'),
get_first_key=True,
no_tag_node_value_mangle=True)
+ D = get_config_diff(conf)
for search in sync_search:
for key in search['keys']:
@@ -217,15 +220,22 @@ def get_config(config=None):
if not search_dict:
continue
for found_name, found_path in dict_search_recursive(search_dict, key):
- if found_name == item_name:
- path = search['path']
- path_str = ' '.join(path + found_path)
- print(f'PKI: Updating config: {path_str} {found_name}')
+ if isinstance(found_name, list) and item_name not in found_name:
+ continue
+
+ if isinstance(found_name, str) and found_name != item_name:
+ continue
+
+ path = search['path']
+ path_str = ' '.join(path + found_path)
+ print(f'PKI: Updating config: {path_str} {item_name}')
- if path[0] == 'interfaces':
- ifname = found_path[0]
+ if path[0] == 'interfaces':
+ ifname = found_path[0]
+ if not D.node_changed_presence(path + [ifname]):
set_dependents(path[1], conf, ifname)
- else:
+ else:
+ if not D.node_changed_presence(path):
set_dependents(path[1], conf)
return pki
diff --git a/src/conf_mode/protocols_bgp.py b/src/conf_mode/protocols_bgp.py
index 2b16de775..4df97d133 100755
--- a/src/conf_mode/protocols_bgp.py
+++ b/src/conf_mode/protocols_bgp.py
@@ -473,6 +473,22 @@ def verify(bgp):
if peer_group_as is None or (peer_group_as != 'internal' and peer_group_as != bgp['system_as']):
raise ConfigError('route-reflector-client only supported for iBGP peers')
+ # T5833 not all AFIs are supported for VRF
+ if 'vrf' in bgp and 'address_family' in peer_config:
+ unsupported_vrf_afi = {
+ 'ipv4_flowspec',
+ 'ipv6_flowspec',
+ 'ipv4_labeled_unicast',
+ 'ipv6_labeled_unicast',
+ 'ipv4_vpn',
+ 'ipv6_vpn',
+ }
+ for afi in peer_config['address_family']:
+ if afi in unsupported_vrf_afi:
+ raise ConfigError(
+ f"VRF is not allowed for address-family '{afi.replace('_', '-')}'"
+ )
+
# Throw an error if a peer group is not configured for allow range
for prefix in dict_search('listen.range', bgp) or []:
# we can not use dict_search() here as prefix contains dots ...
diff --git a/src/conf_mode/system_ip.py b/src/conf_mode/system_ip.py
index b945b51f2..2a0bda91a 100755
--- a/src/conf_mode/system_ip.py
+++ b/src/conf_mode/system_ip.py
@@ -81,11 +81,6 @@ def apply(opt):
value = '0' if (tmp != None) else '1'
write_file('/proc/sys/net/ipv4/conf/all/forwarding', value)
- # enable/disable IPv4 directed broadcast forwarding
- tmp = dict_search('disable_directed_broadcast', opt)
- value = '0' if (tmp != None) else '1'
- write_file('/proc/sys/net/ipv4/conf/all/bc_forwarding', value)
-
# configure multipath
tmp = dict_search('multipath.ignore_unreachable_nexthops', opt)
value = '1' if (tmp != None) else '0'
diff --git a/src/conf_mode/vrf.py b/src/conf_mode/vrf.py
index 1fc813189..587309005 100755
--- a/src/conf_mode/vrf.py
+++ b/src/conf_mode/vrf.py
@@ -315,6 +315,20 @@ def apply(vrf):
for chain, rule in nftables_rules.items():
cmd(f'nft flush chain inet vrf_zones {chain}')
+ # Return default ip rule values
+ if 'name' not in vrf:
+ for afi in ['-4', '-6']:
+ # move lookup local to pref 0 (from 32765)
+ if not has_rule(afi, 0, 'local'):
+ call(f'ip {afi} rule add pref 0 from all lookup local')
+ if has_rule(afi, 32765, 'local'):
+ call(f'ip {afi} rule del pref 32765 table local')
+
+ if has_rule(afi, 1000, 'l3mdev'):
+ call(f'ip {afi} rule del pref 1000 l3mdev protocol kernel')
+ if has_rule(afi, 2000, 'l3mdev'):
+ call(f'ip {afi} rule del pref 2000 l3mdev unreachable')
+
# Apply FRR filters
zebra_daemon = 'zebra'
# Save original configuration prior to starting any commit actions
diff --git a/src/etc/rsyslog.conf b/src/etc/rsyslog.conf
index 9781f0835..b3f41acb6 100644
--- a/src/etc/rsyslog.conf
+++ b/src/etc/rsyslog.conf
@@ -15,21 +15,6 @@ $KLogPath /proc/kmsg
#### GLOBAL DIRECTIVES ####
###########################
-# The lines below cause all listed daemons/processes to be logged into
-# /var/log/auth.log, then drops the message so it does not also go to the
-# regular syslog so that messages are not duplicated
-
-$outchannel auth_log,/var/log/auth.log
-if $programname == 'CRON' or
- $programname == 'sudo' or
- $programname == 'su'
- then :omfile:$auth_log
-
-if $programname == 'CRON' or
- $programname == 'sudo' or
- $programname == 'su'
- then stop
-
# Use traditional timestamp format.
# To enable high precision timestamps, comment out the following line.
# A modern-style logfile format similar to TraditionalFileFormat, buth with high-precision timestamps and timezone information
@@ -60,6 +45,21 @@ $Umask 0022
#
$IncludeConfig /etc/rsyslog.d/*.conf
+# The lines below cause all listed daemons/processes to be logged into
+# /var/log/auth.log, then drops the message so it does not also go to the
+# regular syslog so that messages are not duplicated
+
+$outchannel auth_log,/var/log/auth.log
+if $programname == 'CRON' or
+ $programname == 'sudo' or
+ $programname == 'su'
+ then :omfile:$auth_log
+
+if $programname == 'CRON' or
+ $programname == 'sudo' or
+ $programname == 'su'
+ then stop
+
###############
#### RULES ####
###############
diff --git a/src/helpers/vyos-failover.py b/src/helpers/vyos-failover.py
index f34c18916..348974364 100755
--- a/src/helpers/vyos-failover.py
+++ b/src/helpers/vyos-failover.py
@@ -197,6 +197,7 @@ if __name__ == '__main__':
proto = nexthop_config.get('check').get('type')
target = nexthop_config.get('check').get('target')
timeout = nexthop_config.get('check').get('timeout')
+ onlink = 'onlink' if 'onlink' in nexthop_config else ''
# Route not found in the current routing table
if not is_route_exists(route, next_hop, conf_iface, conf_metric):
@@ -206,14 +207,14 @@ if __name__ == '__main__':
if debug: print(f' [ ADD ] -- ip route add {route} via {next_hop} dev {conf_iface} '
f'metric {conf_metric} proto failover\n###')
rc, command = rc_cmd(f'ip route add {route} via {next_hop} dev {conf_iface} '
- f'metric {conf_metric} proto failover')
+ f'{onlink} metric {conf_metric} proto failover')
# If something is wrong and gateway not added
# Example: Error: Next-hop has invalid gateway.
if rc !=0:
if debug: print(f'{command} -- return-code [RC: {rc}] {next_hop} dev {conf_iface}')
else:
journal.send(f'ip route add {route} via {next_hop} dev {conf_iface} '
- f'metric {conf_metric} proto failover', SYSLOG_IDENTIFIER=my_name)
+ f'{onlink} metric {conf_metric} proto failover', SYSLOG_IDENTIFIER=my_name)
else:
if debug: print(f' [ TARGET_FAIL ] target checks fails for [{target}], do nothing')
journal.send(f'Check fail for route {route} target {target} proto {proto} '
diff --git a/src/helpers/vyos-vrrp-conntracksync.sh b/src/helpers/vyos-vrrp-conntracksync.sh
index 0cc718938..90fa77f23 100755
--- a/src/helpers/vyos-vrrp-conntracksync.sh
+++ b/src/helpers/vyos-vrrp-conntracksync.sh
@@ -25,7 +25,7 @@ LOGCMD="logger -t $TAG -p $FACILITY.$LEVEL"
VRRP_GRP="VRRP sync-group [$2]"
FAILOVER_STATE="/var/run/vyatta-conntrackd-failover-state"
-$LOGCMD "vyatta-vrrp-conntracksync invoked at `date`"
+$LOGCMD "vyos-vrrp-conntracksync invoked at `date`"
if ! systemctl is-active --quiet conntrackd.service; then
echo "conntrackd service not running"
@@ -148,7 +148,7 @@ case "$1" in
*)
echo UNKNOWN at `date` > $FAILOVER_STATE
$LOGCMD "ERROR: `uname -n` unknown state transition for $VRRP_GRP"
- echo "Usage: vyatta-vrrp-conntracksync.sh {master|backup|fault}"
+ echo "Usage: vyos-vrrp-conntracksync.sh {master|backup|fault}"
exit 1
;;
esac
diff --git a/src/init/vyos-router b/src/init/vyos-router
index 06fea140d..15e37df07 100755
--- a/src/init/vyos-router
+++ b/src/init/vyos-router
@@ -451,6 +451,7 @@ start ()
touch /tmp/vyos.ifconfig.debug
touch /tmp/vyos.frr.debug
touch /tmp/vyos.container.debug
+ touch /tmp/vyos.smoketest.debug
fi
log_action_begin_msg "Mounting VyOS Config"
diff --git a/src/migration-scripts/firewall/14-to-15 b/src/migration-scripts/firewall/14-to-15
new file mode 100755
index 000000000..735839365
--- /dev/null
+++ b/src/migration-scripts/firewall/14-to-15
@@ -0,0 +1,46 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2022-2024 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# T5535: Migrate <set system ip disable-directed-broadcast> to <set firewall global-options directed-broadcas [enable|disable]
+
+from sys import argv
+from sys import exit
+
+from vyos.configtree import ConfigTree
+
+if len(argv) < 2:
+ print("Must specify file name!")
+ exit(1)
+
+file_name = argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+base = ['firewall']
+
+if config.exists(['system', 'ip', 'disable-directed-broadcast']):
+ config.set(['firewall', 'global-options', 'directed-broadcast'], value='disable')
+ config.delete(['system', 'ip', 'disable-directed-broadcast'])
+
+try:
+ with open(file_name, 'w') as f:
+ f.write(config.to_string())
+except OSError as e:
+ print("Failed to save the modified config: {}".format(e))
+ exit(1) \ No newline at end of file
diff --git a/src/op_mode/connect_disconnect.py b/src/op_mode/connect_disconnect.py
index bd02dc6ea..373f9e953 100755
--- a/src/op_mode/connect_disconnect.py
+++ b/src/op_mode/connect_disconnect.py
@@ -48,7 +48,7 @@ def connect(interface):
if os.path.isdir(f'/sys/class/net/{interface}'):
print(f'Interface {interface}: already connected!')
elif check_ppp_running(interface):
- print(f'Interface {interface}: connection is beeing established!')
+ print(f'Interface {interface}: connection is being established!')
else:
print(f'Interface {interface}: connecting...')
call(f'systemctl restart ppp@{interface}.service')
@@ -58,7 +58,7 @@ def connect(interface):
else:
call(f'VYOS_TAGNODE_VALUE={interface} /usr/libexec/vyos/conf_mode/interfaces_wwan.py')
else:
- print(f'Unknown interface {interface}, can not connect. Aborting!')
+ print(f'Unknown interface {interface}, cannot connect. Aborting!')
# Reaply QoS configuration
config = ConfigTreeQuery()
@@ -90,7 +90,7 @@ def disconnect(interface):
modem = interface.lstrip('wwan')
call(f'mmcli --modem {modem} --simple-disconnect', stdout=DEVNULL)
else:
- print(f'Unknown interface {interface}, can not disconnect. Aborting!')
+ print(f'Unknown interface {interface}, cannot disconnect. Aborting!')
def main():
parser = argparse.ArgumentParser()
diff --git a/src/op_mode/image_installer.py b/src/op_mode/image_installer.py
index 9f6949fb3..ba0e3b6db 100755
--- a/src/op_mode/image_installer.py
+++ b/src/op_mode/image_installer.py
@@ -26,6 +26,7 @@ from os import environ
from typing import Union
from urllib.parse import urlparse
from passlib.hosts import linux_context
+from errno import ENOSPC
from psutil import disk_partitions
@@ -60,7 +61,8 @@ MSG_INPUT_CONFIG_CHOICE: str = 'The following config files are available for boo
MSG_INPUT_CONFIG_CHOOSE: str = 'Which file would you like as boot config?'
MSG_INPUT_IMAGE_NAME: str = 'What would you like to name this image?'
MSG_INPUT_IMAGE_DEFAULT: str = 'Would you like to set the new image as the default one for boot?'
-MSG_INPUT_PASSWORD: str = 'Please enter a password for the "vyos" user'
+MSG_INPUT_PASSWORD: str = 'Please enter a password for the "vyos" user:'
+MSG_INPUT_PASSWORD_CONFIRM: str = 'Please confirm password for the "vyos" user:'
MSG_INPUT_ROOT_SIZE_ALL: str = 'Would you like to use all the free space on the drive?'
MSG_INPUT_ROOT_SIZE_SET: str = 'Please specify the size (in GB) of the root partition (min is 1.5 GB)?'
MSG_INPUT_CONSOLE_TYPE: str = 'What console should be used by default? (K: KVM, S: Serial, U: USB-Serial)?'
@@ -74,6 +76,7 @@ MSG_WARN_ROOT_SIZE_TOOBIG: str = 'The size is too big. Try again.'
MSG_WARN_ROOT_SIZE_TOOSMALL: str = 'The size is too small. Try again'
MSG_WARN_IMAGE_NAME_WRONG: str = 'The suggested name is unsupported!\n'\
'It must be between 1 and 64 characters long and contains only the next characters: .+-_ a-z A-Z 0-9'
+MSG_WARN_PASSWORD_CONFIRM: str = 'The entered values did not match. Try again'
CONST_MIN_DISK_SIZE: int = 2147483648 # 2 GB
CONST_MIN_ROOT_SIZE: int = 1610612736 # 1.5 GB
# a reserved space: 2MB for header, 1 MB for BIOS partition, 256 MB for EFI
@@ -695,8 +698,14 @@ def install_image() -> None:
print(MSG_WARN_IMAGE_NAME_WRONG)
# ask for password
- user_password: str = ask_input(MSG_INPUT_PASSWORD, default='vyos',
- no_echo=True)
+ while True:
+ user_password: str = ask_input(MSG_INPUT_PASSWORD, no_echo=True,
+ non_empty=True)
+ confirm: str = ask_input(MSG_INPUT_PASSWORD_CONFIRM, no_echo=True,
+ non_empty=True)
+ if user_password == confirm:
+ break
+ print(MSG_WARN_PASSWORD_CONFIRM)
# ask for default console
console_type: str = ask_input(MSG_INPUT_CONSOLE_TYPE,
@@ -931,6 +940,16 @@ def add_image(image_path: str, vrf: str = None, username: str = '',
if set_as_default:
grub.set_default(image_name, root_dir)
+ except OSError as e:
+ # if no space error, remove image dir and cleanup
+ if e.errno == ENOSPC:
+ cleanup(mounts=[str(iso_path)],
+ remove_items=[f'{root_dir}/boot/{image_name}'])
+ else:
+ # unmount an ISO and cleanup
+ cleanup([str(iso_path)])
+ exit(f'Error: {e}')
+
except Exception as err:
# unmount an ISO and cleanup
cleanup([str(iso_path)])
diff --git a/src/op_mode/openvpn.py b/src/op_mode/openvpn.py
index d54a67199..092873909 100755
--- a/src/op_mode/openvpn.py
+++ b/src/op_mode/openvpn.py
@@ -48,9 +48,12 @@ def _get_tunnel_address(peer_host, peer_port, status_file):
# 10.10.2.0/25,client1,...
lst = [l for l in lst[1:] if '/' not in l.split(',')[0]]
- tunnel_ip = lst[0].split(',')[0]
+ if lst:
+ tunnel_ip = lst[0].split(',')[0]
- return tunnel_ip
+ return tunnel_ip
+
+ return 'n/a'
def _get_interface_status(mode: str, interface: str) -> dict:
status_file = f'/run/openvpn/{interface}.status'
diff --git a/src/op_mode/pki.py b/src/op_mode/pki.py
index ad2c1ada0..b1ca6ee29 100755
--- a/src/op_mode/pki.py
+++ b/src/op_mode/pki.py
@@ -306,7 +306,7 @@ def parse_san_string(san_string):
output.append(ipaddress.IPv4Address(value))
elif tag == 'ipv6':
output.append(ipaddress.IPv6Address(value))
- elif tag == 'dns':
+ elif tag == 'dns' or tag == 'rfc822':
output.append(value)
return output
@@ -324,7 +324,7 @@ def generate_certificate_request(private_key=None, key_type=None, return_request
subject_alt_names = None
if ask_san and ask_yes_no('Do you want to configure Subject Alternative Names?'):
- print("Enter alternative names in a comma separate list, example: ipv4:1.1.1.1,ipv6:fe80::1,dns:vyos.net")
+ print("Enter alternative names in a comma separate list, example: ipv4:1.1.1.1,ipv6:fe80::1,dns:vyos.net,rfc822:user@vyos.net")
san_string = ask_input('Enter Subject Alternative Names:')
subject_alt_names = parse_san_string(san_string)
diff --git a/src/op_mode/uptime.py b/src/op_mode/uptime.py
index d6adf6f4d..059a4c3f6 100755
--- a/src/op_mode/uptime.py
+++ b/src/op_mode/uptime.py
@@ -49,7 +49,7 @@ def _get_raw_data():
res = {}
res["uptime_seconds"] = _get_uptime_seconds()
- res["uptime"] = seconds_to_human(_get_uptime_seconds())
+ res["uptime"] = seconds_to_human(_get_uptime_seconds(), separator=' ')
res["load_average"] = _get_load_averages()
return res