Page MenuHomeVyOS Platform

strip_config.py
No OneTemporary

Size
11 KB
Referenced Files
None
Subscribers
None

strip_config.py

#!/usr/bin/python3
#
# Copyright 2024 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/>.
# XXX: these functions assume that the config is at the top level,
# and aren't capable of anonymizing config subtress.
# They shouldn't be used as a basis for a strip-private filter
# until we figure out if we can pass the config path information to the filter.
import copy
import vyos.configtree
def __anonymize_password(v):
return "<PASSWORD REDACTED>"
def __anonymize_key(v):
return "<KEY DATA REDACTED>"
def __anonymize_data(v):
return "<DATA REDACTED>"
__secret_paths = [
# System user password hashes
{"base_path": ['system', 'login', 'user'], "secret_path": ["authentication", "encrypted-password"], "func": __anonymize_password},
# PKI data
{"base_path": ["pki", "ca"], "secret_path": ["private", "key"], "func": __anonymize_key},
{"base_path": ["pki", "ca"], "secret_path": ["certificate"], "func": __anonymize_key},
{"base_path": ["pki", "ca"], "secret_path": ["crl"], "func": __anonymize_key},
{"base_path": ["pki", "certificate"], "secret_path": ["private", "key"], "func": __anonymize_key},
{"base_path": ["pki", "certificate"], "secret_path": ["certificate"], "func": __anonymize_key},
{"base_path": ["pki", "certificate"], "secret_path": ["acme", "email"], "func": __anonymize_data},
{"base_path": ["pki", "key-pair"], "secret_path": ["private", "key"], "func": __anonymize_key},
{"base_path": ["pki", "key-pair"], "secret_path": ["public", "key"], "func": __anonymize_key},
{"base_path": ["pki", "openssh"], "secret_path": ["private", "key"], "func": __anonymize_key},
{"base_path": ["pki", "openssh"], "secret_path": ["public", "key"], "func": __anonymize_key},
{"base_path": ["pki", "openvpn", "shared-secret"], "secret_path": ["key"], "func": __anonymize_key},
{"base_path": ["pki", "dh"], "secret_path": ["parameters"], "func": __anonymize_key},
# IPsec pre-shared secrets
{"base_path": ['vpn', 'ipsec', 'authentication', 'psk'], "secret_path": ["secret"], "func": __anonymize_password},
# IPsec x509 passphrases
{"base_path": ['vpn', 'ipsec', 'site-to-site', 'peer'], "secret_path": ['authentication', 'x509'], "func": __anonymize_password},
# IPsec remote-access secrets and passwords
{"base_path": ["vpn", "ipsec", "remote-access", "connection"], "secret_path": ["authentication", "pre-shared-secret"], "func": __anonymize_password},
# Passwords in remote-access IPsec local users have their own fixup
# due to deeper nesting.
# PPTP passwords
{"base_path": ['vpn', 'pptp', 'remote-access', 'authentication', 'local-users', 'username'], "secret_path": ['password'], "func": __anonymize_password},
# L2TP passwords
{"base_path": ['vpn', 'l2tp', 'remote-access', 'authentication', 'local-users', 'username'], "secret_path": ['password'], "func": __anonymize_password},
{"path": ['vpn', 'l2tp', 'remote-access', 'ipsec-settings', 'authentication', 'pre-shared-secret'], "func": __anonymize_password},
# SSTP passwords
{"base_path": ['vpn', 'sstp', 'remote-access', 'authentication', 'local-users', 'username'], "secret_path": ['password'], "func": __anonymize_password},
# OpenConnect passwords
{"base_path": ['vpn', 'openconnect', 'authentication', 'local-users', 'username'], "secret_path": ['password'], "func": __anonymize_password},
# PPPoE server passwords
{"base_path": ['service', 'pppoe-server', 'authentication', 'local-users', 'username'], "secret_path": ['password'], "func": __anonymize_password},
# RADIUS PSKs for VPN services
{"base_path": ["vpn", "sstp", "authentication", "radius", "server"], "secret_path": ["key"], "func": __anonymize_password},
{"base_path": ["vpn", "l2tp", "authentication", "radius", "server"], "secret_path": ["key"], "func": __anonymize_password},
{"base_path": ["vpn", "pptp", "authentication", "radius", "server"], "secret_path": ["key"], "func": __anonymize_password},
{"base_path": ["vpn", "openconnect", "authentication", "radius", "server"], "secret_path": ["key"], "func": __anonymize_password},
{"base_path": ["service", "ipoe-server", "authentication", "radius", "server"], "secret_path": ["key"], "func": __anonymize_password},
{"base_path": ["service", "pppoe-server", "authentication", "radius", "server"], "secret_path": ["key"], "func": __anonymize_password},
# VRRP passwords
{"base_path": ['high-availability', 'vrrp', 'group'], "secret_path": ['authentication', 'password'], "func": __anonymize_password},
# BGP neighbor and peer group passwords
{"base_path": ['protocols', 'bgp', 'neighbor'], "secret_path": ["password"], "func": __anonymize_password},
{"base_path": ['protocols', 'bgp', 'peer-group'], "secret_path": ["password"], "func": __anonymize_password},
# WireGuard private keys
{"base_path": ["interfaces", "wireguard"], "secret_path": ["private-key"], "func": __anonymize_password},
# NHRP passwords
{"base_path": ["protocols", "nhrp", "tunnel"], "secret_path": ["cisco-authentication"], "func": __anonymize_password},
# RIP passwords
{"base_path": ["protocols", "rip", "interface"], "secret_path": ["authentication", "plaintext-password"], "func": __anonymize_password},
# IS-IS passwords
{"path": ["protocols", "isis", "area-password", "plaintext-password"], "func": __anonymize_password},
{"base_path": ["protocols", "isis", "interface"], "secret_path": ["password", "plaintext-password"], "func": __anonymize_password},
# HTTP API servers
{"base_path": ["service", "https", "api", "keys", "id"], "secret_path": ["key"], "func": __anonymize_password},
# Telegraf
{"path": ["service", "monitoring", "telegraf", "prometheus-client", "authentication", "password"], "func": __anonymize_password},
{"path": ["service", "monitoring", "telegraf", "influxdb", "authentication", "token"], "func": __anonymize_password},
{"path": ["service", "monitoring", "telegraf", "azure-data-explorer", "authentication", "client-secret"], "func": __anonymize_password},
{"path": ["service", "monitoring", "telegraf", "splunk", "authentication", "token"], "func": __anonymize_password},
# SNMPv3 passwords
{"base_path": ["service", "snmp", "v3", "user"], "secret_path": ["privacy", "encrypted-password"], "func": __anonymize_password},
{"base_path": ["service", "snmp", "v3", "user"], "secret_path": ["privacy", "plaintext-password"], "func": __anonymize_password},
{"base_path": ["service", "snmp", "v3", "user"], "secret_path": ["auth", "encrypted-password"], "func": __anonymize_password},
{"base_path": ["service", "snmp", "v3", "user"], "secret_path": ["auth", "encrypted-password"], "func": __anonymize_password},
]
def __prepare_secret_paths(config_tree, secret_paths):
""" Generate a list of secret paths for the current system,
adjusted for variable parts such as VRFs and remote access IPsec instances
"""
# Fixup for remote-access IPsec local users that are nested under two tag nodes
# We generate the list of their paths dynamically
ipsec_ra_base = {"base_path": ["vpn", "ipsec", "remote-access", "connection"], "func": __anonymize_password}
if config_tree.exists(ipsec_ra_base["base_path"]):
for conn in config_tree.list_nodes(ipsec_ra_base["base_path"]):
if config_tree.exists(ipsec_ra_base["base_path"] + [conn] + ["authentication", "local-users", "username"]):
for u in config_tree.list_nodes(ipsec_ra_base["base_path"] + [conn] + ["authentication", "local-users", "username"]):
p = copy.copy(ipsec_ra_base)
p["base_path"] = p["base_path"] + [conn] + ["authentication", "local-users", "username"]
p["secret_path"] = ["password"]
secret_paths.append(p)
# Fixup for VRFs that may contain routing protocols and other nodes nested under them
vrf_paths = []
vrf_base_path = ["vrf", "name"]
if config_tree.exists(vrf_base_path):
for v in config_tree.list_nodes(vrf_base_path):
vrf_secret_paths = copy.deepcopy(secret_paths)
for sp in vrf_secret_paths:
if "base_path" in sp:
sp["base_path"] = vrf_base_path + [v] + sp["base_path"]
elif "path" in sp:
sp["path"] = vrf_base_path + [v] + sp["path"]
vrf_paths.append(sp)
secret_paths = secret_paths + vrf_paths
# Fixup for user SSH keys, that are nested under a tag node
#ssh_key_base_path = {"base_path": ['system', 'login', 'user'], "secret_path": ["authentication", "encrypted-password"], "func": __anonymize_password},
user_base_path = ['system', 'login', 'user']
ssh_key_paths = []
if config_tree.exists(user_base_path):
for u in config_tree.list_nodes(user_base_path):
kp = {"base_path": user_base_path + [u, "authentication", "public-keys"], "secret_path": ["key"], "func": __anonymize_key}
ssh_key_paths.append(kp)
secret_paths = secret_paths + ssh_key_paths
# Fixup for OSPF passwords and keys that are nested under OSPF interfaces
ospf_base_path = ["protocols", "ospf", "interface"]
ospf_paths = []
if config_tree.exists(ospf_base_path):
for i in config_tree.list_nodes(ospf_base_path):
# Plaintext password, there can be only one
opp = {"path": ospf_base_path + [i, "authentication", "plaintext-password"], "func": __anonymize_password}
md5kp = {"base_path": ospf_base_path + [i, "authentication", "md5", "key-id"], "secret_path": ["md5-key"], "func": __anonymize_password}
ospf_paths.append(opp)
ospf_paths.append(md5kp)
secret_paths = secret_paths + ospf_paths
return secret_paths
def __strip_private(ct, secret_paths):
for sp in secret_paths:
if "base_path" in sp:
if ct.exists(sp["base_path"]):
for n in ct.list_nodes(sp["base_path"]):
if ct.exists(sp["base_path"] + [n] + sp["secret_path"]):
secret = ct.return_value(sp["base_path"] + [n] + sp["secret_path"])
ct.set(sp["base_path"] + [n] + sp["secret_path"], value=sp["func"](secret))
elif "path" in sp:
if ct.exists(sp["path"]):
secret = ct.return_value(sp["path"])
ct.set(sp["path"], value=sp["func"](secret))
else:
raise ValueError("Malformed secret path dict, has neither base_path nor path in it ")
return ct.to_string()
def strip_config_source(config_source):
config_tree = vyos.configtree.ConfigTree(config_source)
secret_paths = __prepare_secret_paths(config_tree, __secret_paths)
stripped_config = __strip_private(config_tree, secret_paths)
return stripped_config
def strip_config_tree(config_tree):
secret_paths = __prepare_secret_paths(config_tree, __secret_paths)
return __strip_private(config_tree, secret_paths)

File Metadata

Mime Type
text/x-script.python
Expires
Sat, Oct 17, 12:00 AM (5 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4402845
Default Alt Text
strip_config.py (11 KB)

Event Timeline