summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--data/templates/https/nginx.default.tmpl7
-rw-r--r--data/templates/l2tp/chap-secrets.tmpl7
-rw-r--r--data/templates/pppoe-server/chap-secrets.tmpl5
-rw-r--r--data/templates/pptp/chap-secrets.tmpl2
-rw-r--r--interface-definitions/https.xml.in9
-rw-r--r--interface-definitions/interfaces-tunnel.xml.in2
-rw-r--r--python/vyos/util.py50
-rwxr-xr-xsrc/conf_mode/https.py10
-rwxr-xr-xsrc/conf_mode/interfaces-bonding.py19
-rwxr-xr-xsrc/conf_mode/interfaces-bridge.py3
-rwxr-xr-xsrc/conf_mode/interfaces-dummy.py11
-rwxr-xr-xsrc/conf_mode/interfaces-geneve.py13
-rwxr-xr-xsrc/conf_mode/interfaces-l2tpv3.py23
-rwxr-xr-xsrc/conf_mode/interfaces-openvpn.py10
-rwxr-xr-xsrc/conf_mode/interfaces-pseudo-ethernet.py8
-rwxr-xr-xsrc/conf_mode/interfaces-vxlan.py11
-rwxr-xr-xsrc/conf_mode/interfaces-wireguard.py12
-rwxr-xr-xsrc/conf_mode/interfaces-wireless.py10
-rwxr-xr-xsrc/conf_mode/interfaces-wirelessmodem.py9
-rwxr-xr-xsrc/migration-scripts/https/1-to-254
20 files changed, 219 insertions, 56 deletions
diff --git a/data/templates/https/nginx.default.tmpl b/data/templates/https/nginx.default.tmpl
index b6d3aaf83..33f7b2820 100644
--- a/data/templates/https/nginx.default.tmpl
+++ b/data/templates/https/nginx.default.tmpl
@@ -51,10 +51,17 @@ server {
error_page 501 502 503 =200 @50*_json;
+{% if api_somewhere %}
+ location @50*_json {
+ default_type application/json;
+ return 200 '{"error": "service https api unavailable at this proxy address: set service https api-restrict virtual-host"}';
+ }
+{% else %}
location @50*_json {
default_type application/json;
return 200 '{"error": "Start service in configuration mode: set service https api"}';
}
+{% endif %}
}
diff --git a/data/templates/l2tp/chap-secrets.tmpl b/data/templates/l2tp/chap-secrets.tmpl
index 323528b3c..0db295fdc 100644
--- a/data/templates/l2tp/chap-secrets.tmpl
+++ b/data/templates/l2tp/chap-secrets.tmpl
@@ -1,11 +1,10 @@
# username server password acceptable local IP addresses shaper
{% for user in authentication['local-users'] %}
{% if authentication['local-users'][user]['state'] == 'enabled' %}
-{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
-{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
+{% if authentication['local-users'][user]['upload'] and authentication['local-users'][user]['download'] %}
+{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {{ authentication['local-users'][user]['download'] }} / {{ authentication['local-users'][user]['upload'] }}
{% else %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }}
{% endif %}
{% endif %}
{% endfor %}
diff --git a/data/templates/pppoe-server/chap-secrets.tmpl b/data/templates/pppoe-server/chap-secrets.tmpl
index 808debccb..907ac6ed7 100644
--- a/data/templates/pppoe-server/chap-secrets.tmpl
+++ b/data/templates/pppoe-server/chap-secrets.tmpl
@@ -2,10 +2,9 @@
{% for user in authentication['local-users'] %}
{% if authentication['local-users'][user]['state'] == 'enabled' %}
{% if (authentication['local-users'][user]['upload']) and (authentication['local-users'][user]['download']) %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}\t\
-{{authentication['local-users'][user]['download']}}/{{authentication['local-users'][user]['upload']}}
+{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }} {{ authentication['local-users'][user]['download'] }} / {{ authentication['local-users'][user]['upload'] }}
{% else %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }}
{% endif %}
{% endif %}
{% endfor %}
diff --git a/data/templates/pptp/chap-secrets.tmpl b/data/templates/pptp/chap-secrets.tmpl
index 6bfa2d64e..f93f4607b 100644
--- a/data/templates/pptp/chap-secrets.tmpl
+++ b/data/templates/pptp/chap-secrets.tmpl
@@ -1,6 +1,6 @@
# username server password acceptable local IP addresses
{% for user in authentication['local-users'] %}
{% if authentication['local-users'][user]['state'] == 'enabled' %}
-{{user}}\t*\t{{authentication['local-users'][user]['passwd']}}\t{{authentication['local-users'][user]['ip']}}
+{{ "%-12s" | format(user) }} * {{ "%-16s" | format(authentication['local-users'][user]['passwd']) }} {{ "%-16s" | format(authentication['local-users'][user]['ip']) }}
{% endif %}
{% endfor %}
diff --git a/interface-definitions/https.xml.in b/interface-definitions/https.xml.in
index 49bd25b82..9bb96f1f0 100644
--- a/interface-definitions/https.xml.in
+++ b/interface-definitions/https.xml.in
@@ -1,7 +1,7 @@
<?xml version="1.0"?>
<!-- HTTPS configuration -->
<interfaceDefinition>
- <syntaxVersion component='https' version='1'></syntaxVersion>
+ <syntaxVersion component='https' version='2'></syntaxVersion>
<node name="service">
<children>
<node name="https" owner="${vyos_conf_scripts_dir}/https.py">
@@ -111,6 +111,13 @@
<hidden/>
</properties>
</leafNode>
+ </children>
+ </node>
+ <node name="api-restrict">
+ <properties>
+ <help>Restrict api proxy to subset of virtual hosts</help>
+ </properties>
+ <children>
<leafNode name="virtual-host">
<properties>
<help>Restrict proxy to virtual host(s)</help>
diff --git a/interface-definitions/interfaces-tunnel.xml.in b/interface-definitions/interfaces-tunnel.xml.in
index 3ba82067f..e1ac60319 100644
--- a/interface-definitions/interfaces-tunnel.xml.in
+++ b/interface-definitions/interfaces-tunnel.xml.in
@@ -107,7 +107,7 @@
</leafNode>
<leafNode name="encapsulation">
<properties>
- <help>Ignore link state changes</help>
+ <help>Encapsulation of this tunnel interface</help>
<completionHelp>
<list>gre gre-bridge ipip sit ipip6 ip6ip6 ip6gre</list>
</completionHelp>
diff --git a/python/vyos/util.py b/python/vyos/util.py
index fa2b4dd99..16cfae92d 100644
--- a/python/vyos/util.py
+++ b/python/vyos/util.py
@@ -18,10 +18,6 @@ import re
import sys
from subprocess import Popen, PIPE, STDOUT, DEVNULL
-
-# debugging
-
-
def debug(flag):
return flag if os.path.isfile(f'/tmp/vyos.{flag}.debug') else ''
@@ -31,11 +27,9 @@ def debug_msg(message, section=''):
print(f'DEBUG/{section:<6} {message}')
-# commands
-
-# popen does not raise
-# it returns the output of the command and the error code
-def popen(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
+def popen(command, section='', shell=None, input=None, timeout=None, env=None,
+ universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
+ """ popen does not raise, returns the output and error code of command """
use_shell = shell
if shell is None:
use_shell = True if ' ' in command else False
@@ -52,9 +46,10 @@ def popen(command, section='', shell=None, input=None, timeout=None, env=None, u
debug_msg(f"returned:\n{decoded}", section)
return decoded, p.returncode
-# run does not raise
-# it returns the error code
-def run(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
+
+def run(command, section='', shell=None, input=None, timeout=None, env=None,
+ universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None):
+ """ does not raise exception on error, returns error code """
_, code = popen(
command, section,
stdout=stdout, stderr=stderr,
@@ -65,9 +60,11 @@ def run(command, section='', shell=None, input=None, timeout=None, env=None, uni
)
return code
-# cmd does raise
-# it returns the output
-def cmd(command, section='', shell=None, input=None, timeout=None, env=None, universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None, raising=None, message=''):
+
+def cmd(command, section='', shell=None, input=None, timeout=None, env=None,
+ universal_newlines=None, stdout=PIPE, stderr=STDOUT, decode=None,
+ raising=None, message=''):
+ """ does raise exception, returns output of command """
decoded, code = popen(
command, section,
stdout=stdout, stderr=stderr,
@@ -89,9 +86,6 @@ def cmd(command, section='', shell=None, input=None, timeout=None, env=None, uni
return decoded
-# file manipulation
-
-
def read_file(path):
""" Read a file to string """
with open(path, 'r') as f:
@@ -109,7 +103,6 @@ def chown_file(path, user, group):
gid = getgrnam(group).gr_gid
os.chown(path, uid, gid)
-
def chmod_x(path):
""" make file executable """
from stat import S_IRUSR, S_IWUSR, S_IXUSR, S_IRGRP, S_IXGRP, S_IROTH, S_IXOTH
@@ -328,3 +321,22 @@ def mac2eui64(mac, prefix=None):
return str(net[euil])
except: # pylint: disable=bare-except
return
+
+def is_bridge_member(interface):
+ """
+ Checks if passed interfaces is part of a bridge device or not.
+
+ Returns a tuple:
+ False, None -> Not part of a bridge
+ True, bridge-name -> If it is assigned to a bridge
+ """
+ from vyos.config import Config
+ c = Config()
+ base = ['interfaces', 'bridge']
+ for bridge in c.list_nodes(base):
+ members = c.list_nodes(base + [bridge, 'member', 'interface'])
+ if interface in members:
+ return (True, bridge)
+
+ return False, None
+
diff --git a/src/conf_mode/https.py b/src/conf_mode/https.py
index e46f1a4e7..777792229 100755
--- a/src/conf_mode/https.py
+++ b/src/conf_mode/https.py
@@ -88,14 +88,16 @@ def get_config():
# certbot organizes certificates by first domain
sb['certbot_dir'] = certbot_domains[0]
+ api_somewhere = False
api_data = {}
if conf.exists('api'):
+ api_somewhere = True
api_data = vyos.defaults.api_data
if conf.exists('api port'):
port = conf.return_value('api port')
api_data['port'] = port
- if conf.exists('api virtual-host'):
- vhosts = conf.return_values('api virtual-host')
+ if conf.exists('api-restrict virtual-host'):
+ vhosts = conf.return_values('api-restrict virtual-host')
api_data['vhost'] = vhosts[:]
if api_data:
@@ -110,7 +112,9 @@ def get_config():
if block['id'] in vhost_list:
block['api'] = api_data
- https = {'server_block_list' : server_block_list, 'certbot': certbot}
+ https = {'server_block_list' : server_block_list,
+ 'api_somewhere': api_somewhere,
+ 'certbot': certbot}
return https
def verify(https):
diff --git a/src/conf_mode/interfaces-bonding.py b/src/conf_mode/interfaces-bonding.py
index 19f43f725..6a002bc06 100755
--- a/src/conf_mode/interfaces-bonding.py
+++ b/src/conf_mode/interfaces-bonding.py
@@ -24,9 +24,8 @@ from vyos.ifconfig import BondIf
from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
from vyos.configdict import list_diff, vlan_to_dict
from vyos.config import Config
+from vyos.util import run, is_bridge_member
from vyos import ConfigError
-from vyos.util import run
-
default_config_data = {
'address': [],
@@ -278,17 +277,23 @@ def get_config():
def verify(bond):
+ if bond['deleted']:
+ interface = bond['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
+ return None
+
if len (bond['arp_mon_tgt']) > 16:
raise ConfigError('The maximum number of targets that can be specified is 16')
if bond['primary']:
if bond['mode'] not in ['active-backup', 'balance-tlb', 'balance-alb']:
raise ConfigError('Mode dependency failed, primary not supported ' \
- 'in this mode.'.format())
-
- if bond['primary'] not in bond['member']:
- raise ConfigError('Interface "{}" is not part of the bond' \
- .format(bond['primary']))
+ 'in mode "{}"!'.format(bond['mode']))
vrf_name = bond['vrf']
if vrf_name and vrf_name not in interfaces():
diff --git a/src/conf_mode/interfaces-bridge.py b/src/conf_mode/interfaces-bridge.py
index 28e5957e4..79247ee51 100755
--- a/src/conf_mode/interfaces-bridge.py
+++ b/src/conf_mode/interfaces-bridge.py
@@ -243,6 +243,9 @@ def verify(bridge):
if intf['name'] not in interfaces():
raise ConfigError('Can not add non existing interface "{}" to bridge "{}"'.format(intf['name'], bridge['intf']))
+ if intf['name'] == 'lo':
+ raise ConfigError('Loopback interface "lo" can not be added to a bridge')
+
# bridge members are not allowed to be bond members, too
for intf in bridge['member']:
for bond in conf.list_nodes('interfaces bonding'):
diff --git a/src/conf_mode/interfaces-dummy.py b/src/conf_mode/interfaces-dummy.py
index b7b75517d..a256103af 100755
--- a/src/conf_mode/interfaces-dummy.py
+++ b/src/conf_mode/interfaces-dummy.py
@@ -23,6 +23,7 @@ from netifaces import interfaces
from vyos.ifconfig import DummyIf
from vyos.configdict import list_diff
from vyos.config import Config
+from vyos.util import is_bridge_member
from vyos import ConfigError
default_config_data = {
@@ -78,6 +79,16 @@ def get_config():
return dummy
def verify(dummy):
+ if dummy['deleted']:
+ interface = dummy['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
+ return None
+
vrf_name = dummy['vrf']
if vrf_name and vrf_name not in interfaces():
raise ConfigError(f'VRF "{vrf_name}" does not exist')
diff --git a/src/conf_mode/interfaces-geneve.py b/src/conf_mode/interfaces-geneve.py
index eaa678d3e..e47473d76 100755
--- a/src/conf_mode/interfaces-geneve.py
+++ b/src/conf_mode/interfaces-geneve.py
@@ -18,11 +18,12 @@ import os
from sys import exit
from copy import deepcopy
+from netifaces import interfaces
from vyos.config import Config
-from vyos.ifconfig import GeneveIf, Interface
+from vyos.ifconfig import GeneveIf
+from vyos.util import is_bridge_member
from vyos import ConfigError
-from netifaces import interfaces
default_config_data = {
'address': [],
@@ -92,7 +93,13 @@ def get_config():
def verify(geneve):
if geneve['deleted']:
- # bail out early
+ interface = geneve['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
if not geneve['remote']:
diff --git a/src/conf_mode/interfaces-l2tpv3.py b/src/conf_mode/interfaces-l2tpv3.py
index 4b5fc8306..0400cb849 100755
--- a/src/conf_mode/interfaces-l2tpv3.py
+++ b/src/conf_mode/interfaces-l2tpv3.py
@@ -22,7 +22,7 @@ from copy import deepcopy
from vyos.config import Config
from vyos.ifconfig import L2TPv3If, Interface
from vyos import ConfigError
-from vyos.util import run
+from vyos.util import run, is_bridge_member
from netifaces import interfaces
default_config_data = {
@@ -154,27 +154,34 @@ def get_config():
def verify(l2tpv3):
+ interface = l2tpv3['intf']
+
if l2tpv3['deleted']:
- # bail out early
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
if not l2tpv3['local_address']:
- raise ConfigError('Must configure the l2tpv3 local-ip for {}'.format(l2tpv3['intf']))
+ raise ConfigError(f'Must configure the l2tpv3 local-ip for {interface}')
if not l2tpv3['remote_address']:
- raise ConfigError('Must configure the l2tpv3 remote-ip for {}'.format(l2tpv3['intf']))
+ raise ConfigError(f'Must configure the l2tpv3 remote-ip for {interface}')
if not l2tpv3['tunnel_id']:
- raise ConfigError('Must configure the l2tpv3 tunnel-id for {}'.format(l2tpv3['intf']))
+ raise ConfigError(f'Must configure the l2tpv3 tunnel-id for {interface}')
if not l2tpv3['peer_tunnel_id']:
- raise ConfigError('Must configure the l2tpv3 peer-tunnel-id for {}'.format(l2tpv3['intf']))
+ raise ConfigError(f'Must configure the l2tpv3 peer-tunnel-id for {interface}')
if not l2tpv3['session_id']:
- raise ConfigError('Must configure the l2tpv3 session-id for {}'.format(l2tpv3['intf']))
+ raise ConfigError(f'Must configure the l2tpv3 session-id for {interface}')
if not l2tpv3['peer_session_id']:
- raise ConfigError('Must configure the l2tpv3 peer-session-id for {}'.format(l2tpv3['intf']))
+ raise ConfigError(f'Must configure the l2tpv3 peer-session-id for {interface}')
return None
diff --git a/src/conf_mode/interfaces-openvpn.py b/src/conf_mode/interfaces-openvpn.py
index 1fe1143cd..e9b40bb38 100755
--- a/src/conf_mode/interfaces-openvpn.py
+++ b/src/conf_mode/interfaces-openvpn.py
@@ -31,7 +31,7 @@ from shutil import rmtree
from vyos.config import Config
from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import VTunIf
-from vyos.util import process_running, cmd
+from vyos.util import process_running, cmd, is_bridge_member
from vyos.validate import is_addr_assigned
from vyos import ConfigError
@@ -444,8 +444,16 @@ def get_config():
def verify(openvpn):
if openvpn['deleted']:
+ interface = openvpn['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
+
if not openvpn['mode']:
raise ConfigError('Must specify OpenVPN operation mode')
diff --git a/src/conf_mode/interfaces-pseudo-ethernet.py b/src/conf_mode/interfaces-pseudo-ethernet.py
index 56d4fdfc3..50b5a12a0 100755
--- a/src/conf_mode/interfaces-pseudo-ethernet.py
+++ b/src/conf_mode/interfaces-pseudo-ethernet.py
@@ -24,6 +24,7 @@ from vyos.ifconfig import MACVLANIf
from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
from vyos.configdict import list_diff, vlan_to_dict
from vyos.config import Config
+from vyos.util import is_bridge_member
from vyos import ConfigError
default_config_data = {
@@ -217,6 +218,13 @@ def get_config():
def verify(peth):
if peth['deleted']:
+ interface = peth['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
if not peth['link']:
diff --git a/src/conf_mode/interfaces-vxlan.py b/src/conf_mode/interfaces-vxlan.py
index 1f9636729..b9bfb242a 100755
--- a/src/conf_mode/interfaces-vxlan.py
+++ b/src/conf_mode/interfaces-vxlan.py
@@ -18,11 +18,12 @@ import os
from sys import exit
from copy import deepcopy
+from netifaces import interfaces
from vyos.config import Config
from vyos.ifconfig import VXLANIf, Interface
+from vyos.util import is_bridge_member
from vyos import ConfigError
-from netifaces import interfaces
default_config_data = {
'address': [],
@@ -148,7 +149,13 @@ def get_config():
def verify(vxlan):
if vxlan['deleted']:
- # bail out early
+ interface = vxlan['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
if vxlan['mtu'] < 1500:
diff --git a/src/conf_mode/interfaces-wireguard.py b/src/conf_mode/interfaces-wireguard.py
index 4fa0dd8c0..54121a6c1 100755
--- a/src/conf_mode/interfaces-wireguard.py
+++ b/src/conf_mode/interfaces-wireguard.py
@@ -24,7 +24,7 @@ from netifaces import interfaces
from vyos import ConfigError
from vyos.config import Config
from vyos.configdict import list_diff
-from vyos.util import run
+from vyos.util import run, is_bridge_member
from vyos.ifconfig import WireGuardIf
kdir = r'/config/auth/wireguard'
@@ -179,6 +179,16 @@ def verify(c):
if not c:
return None
+ if c['delete']:
+ interface = c['intfc']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
+ return None
+
if not os.path.exists(c['pk']):
raise ConfigError(
"No keys found, generate them by executing: \'run generate wireguard [keypair|named-keypairs]\'")
diff --git a/src/conf_mode/interfaces-wireless.py b/src/conf_mode/interfaces-wireless.py
index 188d0ee22..709085b0f 100755
--- a/src/conf_mode/interfaces-wireless.py
+++ b/src/conf_mode/interfaces-wireless.py
@@ -29,7 +29,7 @@ from vyos.configdict import list_diff, vlan_to_dict
from vyos.defaults import directories as vyos_data_dir
from vyos.ifconfig import WiFiIf
from vyos.ifconfig_vlan import apply_vlan_config, verify_vlan_config
-from vyos.util import process_running, chmod_x, chown_file, run
+from vyos.util import process_running, chmod_x, chown_file, run, is_bridge_member
from vyos import ConfigError
user = 'root'
@@ -554,8 +554,16 @@ def get_config():
def verify(wifi):
if wifi['deleted']:
+ interface = wifi['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
+
if wifi['op_mode'] != 'monitor' and not wifi['ssid']:
raise ConfigError('SSID must be set for {}'.format(wifi['intf']))
diff --git a/src/conf_mode/interfaces-wirelessmodem.py b/src/conf_mode/interfaces-wirelessmodem.py
index 5e10cfce7..49445aaa4 100755
--- a/src/conf_mode/interfaces-wirelessmodem.py
+++ b/src/conf_mode/interfaces-wirelessmodem.py
@@ -23,7 +23,7 @@ from netifaces import interfaces
from vyos.config import Config
from vyos.defaults import directories as vyos_data_dir
-from vyos.util import chown_file, chmod_x, cmd, run
+from vyos.util import chown_file, chmod_x, cmd, run, is_bridge_member
from vyos import ConfigError
default_config_data = {
@@ -115,6 +115,13 @@ def get_config():
def verify(wwan):
if wwan['deleted']:
+ interface = wwan['intf']
+ is_member, bridge = is_bridge_member(interface)
+ if is_member:
+ # can not use a f'' formatted-string here as bridge would not get
+ # expanded in the print statement
+ raise ConfigError('Can not delete interface "{0}" as it ' \
+ 'is a member of bridge "{1}"!'.format(interface, bridge))
return None
if not wwan['apn']:
diff --git a/src/migration-scripts/https/1-to-2 b/src/migration-scripts/https/1-to-2
new file mode 100755
index 000000000..b1cf37ea6
--- /dev/null
+++ b/src/migration-scripts/https/1-to-2
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+#
+# Copyright (C) 2020 VyOS maintainers and contributors
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License version 2 or later as
+# published by the Free Software Foundation.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+# * Move 'api virtual-host' list to 'api-restrict virtual-host' so it
+# is owned by https.py instead of http-api.py
+
+import sys
+
+from vyos.configtree import ConfigTree
+
+if (len(sys.argv) < 2):
+ print("Must specify file name!")
+ sys.exit(1)
+
+file_name = sys.argv[1]
+
+with open(file_name, 'r') as f:
+ config_file = f.read()
+
+config = ConfigTree(config_file)
+
+old_base = ['service', 'https', 'api', 'virtual-host']
+if not config.exists(old_base):
+ # Nothing to do
+ sys.exit(0)
+else:
+ new_base = ['service', 'https', 'api-restrict', 'virtual-host']
+ config.set(new_base)
+
+ names = config.return_values(old_base)
+ for name in names:
+ config.set(new_base, value=name, replace=False)
+
+ config.delete(old_base)
+
+ 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))
+ sys.exit(1)