summaryrefslogtreecommitdiff
path: root/src/op_mode
diff options
context:
space:
mode:
Diffstat (limited to 'src/op_mode')
-rwxr-xr-xsrc/op_mode/dhcp.py4
-rwxr-xr-xsrc/op_mode/firewall.py240
-rwxr-xr-xsrc/op_mode/ipsec.py39
-rwxr-xr-xsrc/op_mode/otp.py124
-rwxr-xr-xsrc/op_mode/vyos-op-cmd-wrapper.sh6
5 files changed, 343 insertions, 70 deletions
diff --git a/src/op_mode/dhcp.py b/src/op_mode/dhcp.py
index f558c18b7..77f38992b 100755
--- a/src/op_mode/dhcp.py
+++ b/src/op_mode/dhcp.py
@@ -338,10 +338,12 @@ def _get_formatted_client_leases(lease_data, family):
from time import localtime
from time import strftime
- from vyos.validate import is_intf_addr_assigned
+ from vyos.utils.network import is_intf_addr_assigned
data_entries = []
for lease in lease_data:
+ if not lease.get('new_ip_address'):
+ continue
data_entries.append(["Interface", lease['interface']])
if 'new_ip_address' in lease:
tmp = '[Active]' if is_intf_addr_assigned(lease['interface'], lease['new_ip_address']) else '[Inactive]'
diff --git a/src/op_mode/firewall.py b/src/op_mode/firewall.py
index 852a7248a..3434707ec 100755
--- a/src/op_mode/firewall.py
+++ b/src/op_mode/firewall.py
@@ -24,26 +24,39 @@ from vyos.config import Config
from vyos.utils.process import cmd
from vyos.utils.dict import dict_search_args
-def get_config_firewall(conf, hook=None, priority=None, ipv6=False, interfaces=True):
+def get_config_firewall(conf, family=None, hook=None, priority=None):
config_path = ['firewall']
- if hook:
- config_path += ['ipv6' if ipv6 else 'ipv4', hook]
- if priority:
- config_path += [priority]
+ if family:
+ config_path += [family]
+ if hook:
+ config_path += [hook]
+ if priority:
+ config_path += [priority]
firewall = conf.get_config_dict(config_path, key_mangling=('-', '_'),
get_first_key=True, no_tag_node_value_mangle=True)
return firewall
-def get_nftables_details(hook, priority, ipv6=False):
- suffix = '6' if ipv6 else ''
- name_prefix = 'NAME6_' if ipv6 else 'NAME_'
+def get_nftables_details(family, hook, priority):
+ if family == 'ipv6':
+ suffix = 'ip6'
+ name_prefix = 'NAME6_'
+ aux='IPV6_'
+ elif family == 'ipv4':
+ suffix = 'ip'
+ name_prefix = 'NAME_'
+ aux=''
+ else:
+ suffix = 'bridge'
+ name_prefix = 'NAME_'
+ aux=''
+
if hook == 'name' or hook == 'ipv6-name':
- command = f'sudo nft list chain ip{suffix} vyos_filter {name_prefix}{priority}'
+ command = f'sudo nft list chain {suffix} vyos_filter {name_prefix}{priority}'
else:
up_hook = hook.upper()
- command = f'sudo nft list chain ip{suffix} vyos_filter VYOS_{up_hook}_{priority}'
+ command = f'sudo nft list chain {suffix} vyos_filter VYOS_{aux}{up_hook}_{priority}'
try:
results = cmd(command)
@@ -67,11 +80,10 @@ def get_nftables_details(hook, priority, ipv6=False):
out[rule_id] = rule
return out
-def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_id=None):
- ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {priority}"\n')
+def output_firewall_name(family, hook, priority, firewall_conf, single_rule_id=None):
+ print(f'\n---------------------------------\n{family} Firewall "{hook} {priority}"\n')
- details = get_nftables_details(hook, priority, ipv6)
+ details = get_nftables_details(family, hook, priority)
rows = []
if 'rule' in firewall_conf:
@@ -102,11 +114,10 @@ def output_firewall_name(hook, priority, firewall_conf, ipv6=False, single_rule_
header = ['Rule', 'Action', 'Protocol', 'Packets', 'Bytes', 'Conditions']
print(tabulate.tabulate(rows, header) + '\n')
-def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_rule_id=None):
- ip_str = 'IPv6' if ipv6 else 'IPv4'
- print(f'\n---------------------------------\n{ip_str} Firewall "{hook} {prior}"\n')
+def output_firewall_name_statistics(family, hook, prior, prior_conf, single_rule_id=None):
+ print(f'\n---------------------------------\n{family} Firewall "{hook} {prior}"\n')
- details = get_nftables_details(prior, ipv6)
+ details = get_nftables_details(family, hook, prior)
rows = []
if 'rule' in prior_conf:
@@ -117,8 +128,57 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
if 'disable' in rule_conf:
continue
- source_addr = dict_search_args(rule_conf, 'source', 'address') or '0.0.0.0/0'
- dest_addr = dict_search_args(rule_conf, 'destination', 'address') or '0.0.0.0/0'
+ # Get source
+ source_addr = dict_search_args(rule_conf, 'source', 'address')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'group', 'address_group')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'group', 'network_group')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'group', 'domain_group')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'fqdn')
+ if not source_addr:
+ source_addr = dict_search_args(rule_conf, 'source', 'geoip', 'country_code')
+ if source_addr:
+ source_addr = str(source_addr)[1:-1].replace('\'','')
+ if 'inverse_match' in dict_search_args(rule_conf, 'source', 'geoip'):
+ source_addr = 'NOT ' + str(source_addr)
+ if not source_addr:
+ source_addr = 'any'
+
+ # Get destination
+ dest_addr = dict_search_args(rule_conf, 'destination', 'address')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'address_group')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'network_group')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'group', 'domain_group')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'fqdn')
+ if not dest_addr:
+ dest_addr = dict_search_args(rule_conf, 'destination', 'geoip', 'country_code')
+ if dest_addr:
+ dest_addr = str(dest_addr)[1:-1].replace('\'','')
+ if 'inverse_match' in dict_search_args(rule_conf, 'destination', 'geoip'):
+ dest_addr = 'NOT ' + str(dest_addr)
+ if not dest_addr:
+ dest_addr = 'any'
+
+ # Get inbound interface
+ iiface = dict_search_args(rule_conf, 'inbound_interface', 'interface_name')
+ if not iiface:
+ iiface = dict_search_args(rule_conf, 'inbound_interface', 'interface_group')
+ if not iiface:
+ iiface = 'any'
+
+ # Get outbound interface
+ oiface = dict_search_args(rule_conf, 'outbound_interface', 'interface_name')
+ if not oiface:
+ oiface = dict_search_args(rule_conf, 'outbound_interface', 'interface_group')
+ if not oiface:
+ oiface = 'any'
row = [rule_id]
if rule_id in details:
@@ -131,9 +191,26 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
row.append(rule_conf['action'])
row.append(source_addr)
row.append(dest_addr)
+ row.append(iiface)
+ row.append(oiface)
rows.append(row)
- if 'default_action' in prior_conf and not single_rule_id:
+
+ if hook in ['input', 'forward', 'output']:
+ row = ['default']
+ row.append('N/A')
+ row.append('N/A')
+ if 'default_action' in prior_conf:
+ row.append(prior_conf['default_action'])
+ else:
+ row.append('accept')
+ row.append('any')
+ row.append('any')
+ row.append('any')
+ row.append('any')
+ rows.append(row)
+
+ elif 'default_action' in prior_conf and not single_rule_id:
row = ['default']
if 'default-action' in details:
rule_details = details['default-action']
@@ -143,12 +220,14 @@ def output_firewall_name_statistics(hook, prior, prior_conf, ipv6=False, single_
row.append('0')
row.append('0')
row.append(prior_conf['default_action'])
- row.append('0.0.0.0/0') # Source
- row.append('0.0.0.0/0') # Dest
+ row.append('any') # Source
+ row.append('any') # Dest
+ row.append('any') # inbound-interface
+ row.append('any') # outbound-interface
rows.append(row)
if rows:
- header = ['Rule', 'Packets', 'Bytes', 'Action', 'Source', 'Destination']
+ header = ['Rule', 'Packets', 'Bytes', 'Action', 'Source', 'Destination', 'Inbound-Interface', 'Outbound-interface']
print(tabulate.tabulate(rows, header) + '\n')
def show_firewall():
@@ -160,15 +239,11 @@ def show_firewall():
if not firewall:
return
- if 'ipv4' in firewall:
- for hook, hook_conf in firewall['ipv4'].items():
- for prior, prior_conf in firewall['ipv4'][hook].items():
- output_firewall_name(hook, prior, prior_conf, ipv6=False)
-
- if 'ipv6' in firewall:
- for hook, hook_conf in firewall['ipv6'].items():
- for prior, prior_conf in firewall['ipv6'][hook].items():
- output_firewall_name(hook, prior, prior_conf, ipv6=True)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if family in firewall:
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ output_firewall_name(family, hook, prior, prior_conf)
def show_firewall_family(family):
print(f'Rulesets {family} Information')
@@ -176,35 +251,32 @@ def show_firewall_family(family):
conf = Config()
firewall = get_config_firewall(conf)
- if not firewall:
+ if not firewall or family not in firewall:
return
for hook, hook_conf in firewall[family].items():
for prior, prior_conf in firewall[family][hook].items():
- if family == 'ipv6':
- output_firewall_name(hook, prior, prior_conf, ipv6=True)
- else:
- output_firewall_name(hook, prior, prior_conf, ipv6=False)
+ output_firewall_name(family, hook, prior, prior_conf)
-def show_firewall_name(hook, priority, ipv6=False):
+def show_firewall_name(family, hook, priority):
print('Ruleset Information')
conf = Config()
- firewall = get_config_firewall(conf, hook, priority, ipv6)
+ firewall = get_config_firewall(conf, family, hook, priority)
if firewall:
- output_firewall_name(hook, priority, firewall, ipv6)
+ output_firewall_name(family, hook, priority, firewall)
-def show_firewall_rule(hook, priority, rule_id, ipv6=False):
+def show_firewall_rule(family, hook, priority, rule_id):
print('Rule Information')
conf = Config()
- firewall = get_config_firewall(conf, hook, priority, ipv6)
+ firewall = get_config_firewall(conf, family, hook, priority)
if firewall:
- output_firewall_name(hook, priority, firewall, ipv6, rule_id)
+ output_firewall_name(family, hook, priority, firewall, rule_id)
def show_firewall_group(name=None):
conf = Config()
- firewall = get_config_firewall(conf, interfaces=False)
+ firewall = get_config_firewall(conf)
if 'group' not in firewall:
return
@@ -225,18 +297,39 @@ def show_firewall_group(name=None):
for item in family:
for name_type in ['name', 'ipv6_name', 'forward', 'input', 'output']:
- if name_type not in firewall[item]:
- continue
- for name, name_conf in firewall[item][name_type].items():
- if 'rule' not in name_conf:
+ if item in firewall:
+ if name_type not in firewall[item]:
continue
- for rule_id, rule_conf in name_conf['rule'].items():
- source_group = dict_search_args(rule_conf, 'source', 'group', group_type)
- dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type)
- if source_group and group_name == source_group:
- out.append(f'{name}-{rule_id}')
- elif dest_group and group_name == dest_group:
- out.append(f'{name}-{rule_id}')
+ for priority, priority_conf in firewall[item][name_type].items():
+ if priority not in firewall[item][name_type]:
+ continue
+ if 'rule' not in priority_conf:
+ continue
+ for rule_id, rule_conf in priority_conf['rule'].items():
+ source_group = dict_search_args(rule_conf, 'source', 'group', group_type)
+ dest_group = dict_search_args(rule_conf, 'destination', 'group', group_type)
+ in_interface = dict_search_args(rule_conf, 'inbound_interface', 'interface_group')
+ out_interface = dict_search_args(rule_conf, 'outbound_interface', 'interface_group')
+ if source_group:
+ if source_group[0] == "!":
+ source_group = source_group[1:]
+ if group_name == source_group:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
+ if dest_group:
+ if dest_group[0] == "!":
+ dest_group = dest_group[1:]
+ if group_name == dest_group:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
+ if in_interface:
+ if in_interface[0] == "!":
+ in_interface = in_interface[1:]
+ if group_name == in_interface:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
+ if out_interface:
+ if out_interface[0] == "!":
+ out_interface = out_interface[1:]
+ if group_name == out_interface:
+ out.append(f'{item}-{name_type}-{priority}-{rule_id}')
return out
header = ['Name', 'Type', 'References', 'Members']
@@ -248,7 +341,7 @@ def show_firewall_group(name=None):
continue
references = find_references(group_type, group_name)
- row = [group_name, group_type, '\n'.join(references) or 'N/A']
+ row = [group_name, group_type, '\n'.join(references) or 'N/D']
if 'address' in group_conf:
row.append("\n".join(sorted(group_conf['address'])))
elif 'network' in group_conf:
@@ -257,8 +350,10 @@ def show_firewall_group(name=None):
row.append("\n".join(sorted(group_conf['mac_address'])))
elif 'port' in group_conf:
row.append("\n".join(sorted(group_conf['port'])))
+ elif 'interface' in group_conf:
+ row.append("\n".join(sorted(group_conf['interface'])))
else:
- row.append('N/A')
+ row.append('N/D')
rows.append(row)
if rows:
@@ -277,6 +372,7 @@ def show_summary():
header = ['Ruleset Hook', 'Ruleset Priority', 'Description', 'References']
v4_out = []
v6_out = []
+ br_out = []
if 'ipv4' in firewall:
for hook, hook_conf in firewall['ipv4'].items():
@@ -290,6 +386,12 @@ def show_summary():
description = prior_conf.get('description', '')
v6_out.append([hook, prior, description])
+ if 'bridge' in firewall:
+ for hook, hook_conf in firewall['bridge'].items():
+ for prior, prior_conf in firewall['bridge'][hook].items():
+ description = prior_conf.get('description', '')
+ br_out.append([hook, prior, description])
+
if v6_out:
print('\nIPv6 Ruleset:\n')
print(tabulate.tabulate(v6_out, header) + '\n')
@@ -298,6 +400,10 @@ def show_summary():
print('\nIPv4 Ruleset:\n')
print(tabulate.tabulate(v4_out, header) + '\n')
+ if br_out:
+ print('\nBridge Ruleset:\n')
+ print(tabulate.tabulate(br_out, header) + '\n')
+
show_firewall_group()
def show_statistics():
@@ -309,15 +415,11 @@ def show_statistics():
if not firewall:
return
- if 'ipv4' in firewall:
- for hook, hook_conf in firewall['ipv4'].items():
- for prior, prior_conf in firewall['ipv4'][hook].items():
- output_firewall_name_statistics(hook,prior, prior_conf, ipv6=False)
-
- if 'ipv6' in firewall:
- for hook, hook_conf in firewall['ipv6'].items():
- for prior, prior_conf in firewall['ipv6'][hook].items():
- output_firewall_name_statistics(hook,prior, prior_conf, ipv6=True)
+ for family in ['ipv4', 'ipv6', 'bridge']:
+ if family in firewall:
+ for hook, hook_conf in firewall[family].items():
+ for prior, prior_conf in firewall[family][hook].items():
+ output_firewall_name_statistics(family, hook,prior, prior_conf)
if __name__ == '__main__':
parser = argparse.ArgumentParser()
@@ -333,9 +435,9 @@ if __name__ == '__main__':
if args.action == 'show':
if not args.rule:
- show_firewall_name(args.hook, args.priority, args.ipv6)
+ show_firewall_name(args.family, args.hook, args.priority)
else:
- show_firewall_rule(args.hook, args.priority, args.rule, args.ipv6)
+ show_firewall_rule(args.family, args.hook, args.priority, args.rule)
elif args.action == 'show_all':
show_firewall()
elif args.action == 'show_family':
diff --git a/src/op_mode/ipsec.py b/src/op_mode/ipsec.py
index 57d3cfed9..44d41219e 100755
--- a/src/op_mode/ipsec.py
+++ b/src/op_mode/ipsec.py
@@ -779,6 +779,45 @@ def show_ra_summary(raw: bool):
return _get_formatted_output_ra_summary(list_sa)
+# PSK block
+def _get_raw_psk():
+ conf: ConfigTreeQuery = ConfigTreeQuery()
+ config_path = ['vpn', 'ipsec', 'authentication', 'psk']
+ psk_config = conf.get_config_dict(config_path, key_mangling=('-', '_'),
+ get_first_key=True,
+ no_tag_node_value_mangle=True)
+
+ psk_list = []
+ for psk, psk_data in psk_config.items():
+ psk_data['psk'] = psk
+ psk_list.append(psk_data)
+
+ return psk_list
+
+
+def _get_formatted_psk(psk_list):
+ headers = ["PSK", "Id", "Secret"]
+ formatted_data = []
+
+ for psk_data in psk_list:
+ formatted_data.append([psk_data["psk"], "\n".join(psk_data["id"]), psk_data["secret"]])
+
+ return tabulate(formatted_data, headers=headers)
+
+
+def show_psk(raw: bool):
+ config = ConfigTreeQuery()
+ if not config.exists('vpn ipsec authentication psk'):
+ raise vyos.opmode.UnconfiguredSubsystem('VPN ipsec psk authentication is not configured')
+
+ psk = _get_raw_psk()
+ if raw:
+ return psk
+ return _get_formatted_psk(psk)
+
+# PSK block end
+
+
if __name__ == '__main__':
try:
res = vyos.opmode.run(sys.modules[__name__])
diff --git a/src/op_mode/otp.py b/src/op_mode/otp.py
new file mode 100755
index 000000000..6d4298894
--- /dev/null
+++ b/src/op_mode/otp.py
@@ -0,0 +1,124 @@
+#!/usr/bin/env python3
+
+# Copyright 2017, 2022 VyOS maintainers and contributors <maintainers@vyos.io>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2.1 of the License, or (at your option) any later version.
+#
+# This library 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
+# 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, see <http://www.gnu.org/licenses/>.
+
+
+import sys
+import os
+import vyos.opmode
+from jinja2 import Template
+from vyos.configquery import ConfigTreeQuery
+from vyos.xml import defaults
+from vyos.configdict import dict_merge
+from vyos.utils.process import popen
+
+
+users_otp_template = Template("""
+{% if info == "full" %}
+# You can share it with the user, he just needs to scan the QR in his OTP app
+# username: {{username}}
+# OTP KEY: {{key_base32}}
+# OTP URL: {{otp_url}}
+{{qrcode}}
+# To add this OTP key to configuration, run the following commands:
+set system login user {{username}} authentication otp key '{{key_base32}}'
+{% if rate_limit != "3" %}
+set system login user {{username}} authentication otp rate-limit '{{rate_limit}}'
+{% endif %}
+{% if rate_time != "30" %}
+set system login user {{username}} authentication otp rate-time '{{rate_time}}'
+{% endif %}
+{% if window_size != "3" %}
+set system login user {{username}} authentication otp window-size '{{window_size}}'
+{% endif %}
+{% elif info == "key-b32" %}
+# OTP key in Base32 for system user {{username}}:
+{{key_base32}}
+{% elif info == "qrcode" %}
+# QR code for system user '{{username}}'
+{{qrcode}}
+{% elif info == "uri" %}
+# URI for system user '{{username}}'
+{{otp_url}}
+{% endif %}
+""", trim_blocks=True, lstrip_blocks=True)
+
+
+def _check_uname_otp(username:str):
+ """
+ Check if "username" exists and have an OTP key
+ """
+ config = ConfigTreeQuery()
+ base_key = ['system', 'login', 'user', username, 'authentication', 'otp', 'key']
+ if not config.exists(base_key):
+ return None
+ return True
+
+def _get_login_otp(username: str, info:str):
+ """
+ Retrieve user settings from configuration and set some defaults
+ """
+ config = ConfigTreeQuery()
+ base = ['system', 'login', 'user', username]
+ if not config.exists(base):
+ return None
+ user_otp = config.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ # We have gathered the dict representation of the CLI, but there are default
+ # options which we need to update into the dictionary retrived.
+ default_values = defaults(['system', 'login', 'user'])
+ user_otp = dict_merge(default_values, user_otp)
+ result = user_otp['authentication']['otp']
+ # Filling in the system and default options
+ result['info'] = info
+ result['hostname'] = os.uname()[1]
+ result['username'] = username
+ result['key_base32'] = result['key']
+ result['otp_length'] = '6'
+ result['interval'] = '30'
+ result['token_type'] = 'hotp-time'
+ if result['token_type'] == 'hotp-time':
+ token_type_acrn = 'totp'
+ result['otp_url'] = ''.join(["otpauth://",token_type_acrn,"/",username,"@",\
+ result['hostname'],"?secret=",result['key_base32'],"&digits=",\
+ result['otp_length'],"&period=",result['interval']])
+ result['qrcode'],err = popen('qrencode -t ansiutf8', input=result['otp_url'])
+ return result
+
+def show_login(raw: bool, username: str, info:str):
+ '''
+ Display OTP parameters for <username>
+ '''
+ check_otp = _check_uname_otp(username)
+ if check_otp:
+ user_otp_params = _get_login_otp(username, info)
+ else:
+ print(f'There is no such user ("{username}") with an OTP key configured')
+ print('You can use the following command to generate a key for a user:\n')
+ print(f'generate system login username {username} otp-key hotp-time')
+ sys.exit(0)
+ if raw:
+ return user_otp_params
+ return users_otp_template.render(user_otp_params)
+
+
+if __name__ == '__main__':
+ try:
+ res = vyos.opmode.run(sys.modules[__name__])
+ if res:
+ print(res)
+ except (ValueError, vyos.opmode.Error) as e:
+ print(e)
+ sys.exit(1)
diff --git a/src/op_mode/vyos-op-cmd-wrapper.sh b/src/op_mode/vyos-op-cmd-wrapper.sh
new file mode 100755
index 000000000..a89211b2b
--- /dev/null
+++ b/src/op_mode/vyos-op-cmd-wrapper.sh
@@ -0,0 +1,6 @@
+#!/bin/vbash
+shopt -s expand_aliases
+source /etc/default/vyatta
+source /etc/bash_completion.d/vyatta-op
+_vyatta_op_init
+_vyatta_op_run "$@"