Page MenuHomeVyOS Platform

No OneTemporary

Size
12 KB
Referenced Files
None
Subscribers
None
diff --git a/python/vyos/remote.py b/python/vyos/remote.py
index ad9706a82..18e772cc8 100644
--- a/python/vyos/remote.py
+++ b/python/vyos/remote.py
@@ -1,127 +1,122 @@
# Copyright 2021 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 os
import sys
import tempfile
from ftplib import FTP
import urllib.parse
import urllib.request
from vyos.util import cmd
from paramiko import SSHClient
def upload_ftp(local_path, hostname, remote_path,\
username='anonymous', password='', port=21):
with open(local_path, 'rb') as file:
with FTP() as conn:
conn.connect(hostname, port)
conn.login(username, password)
conn.storbinary(f'STOR {remote_path}', file)
def download_ftp(local_path, hostname, remote_path,\
username='anonymous', password='', port=21):
with open(local_path, 'wb') as file:
with FTP() as conn:
conn.connect(hostname, port)
conn.login(username, password)
conn.retrbinary(f'RETR {remote_path}', file.write)
def upload_sftp(local_path, hostname, remote_path,\
username=None, password=None, port=22):
with SSHClient() as ssh:
ssh.load_system_host_keys()
ssh.connect(hostname, port, username, password)
with ssh.open_sftp() as sftp:
sftp.put(local_path, remote_path)
def download_sftp(local_path, hostname, remote_path,\
username=None, password=None, port=22):
with SSHClient() as ssh:
ssh.load_system_host_keys()
ssh.connect(hostname, port, username, password)
with ssh.open_sftp() as sftp:
sftp.get(remote_path, local_path)
def upload_tftp(local_path, hostname, remote_path, port=69):
with open(local_path, 'rb') as file:
- cmd(f'curl -s -T - tftp://{hostname}:{port}/{remote_path}', stderr=None, input=file.read())
+ cmd(f'curl -s -T - tftp://{hostname}:{port}/{remote_path}', stderr=None, input=file.read()).encode()
def download_tftp(local_path, hostname, remote_path, port=69):
with open(local_path, 'wb') as file:
- file.write(cmd(f'curl -s tftp://{hostname}:{port}/{remote_path}', stderr=None))
+ file.write(cmd(f'curl -s tftp://{hostname}:{port}/{remote_path}', stderr=None).encode())
def download_http(urlstring, local_path):
with open(local_path, 'wb') as file:
with urllib.request.urlopen(urlstring) as response:
file.write(response.read())
def download(local_path, urlstring):
"""
Dispatch the appropriate download function for the given URL and save to local path.
"""
url = urllib.parse.urlparse(urlstring)
if url.scheme == 'http' or url.scheme == 'https':
download_http(urlstring, local_path)
elif url.scheme == 'ftp':
username = url.username if url.username else 'anonymous'
download_ftp(local_path, url.hostname, url.path, username, url.password)
elif url.scheme == 'sftp' or url.scheme == 'scp':
- # None means we don't want to use password authentication.
- # An empty string (what urlparse returns when a password doesn't
- # exist in the URL) means the password is an empty string.
- password = url.password if url.password else None
download_sftp(local_path, url.hostname, url.path, url.username, password)
elif url.scheme == 'tftp':
download_tftp(local_path, url.hostname, url.path)
else:
ValueError(f'Unsupported URL scheme: {url.scheme}')
def upload(local_path, urlstring):
"""
Dispatch the appropriate upload function for the given URL and upload from local path.
"""
url = urllib.parse.urlparse(urlstring)
if url.scheme == 'ftp':
username = url.username if url.username else 'anonymous'
upload_ftp(local_path, url.hostname, url.path, username, url.password)
elif url.scheme == 'sftp' or url.scheme == 'scp':
- password = url.password if url.password else None
upload_sftp(local_path, url.hostname, url.path, url.username, password)
elif url.scheme == 'tftp':
upload_tftp(local_path, url.hostname, url.path)
else:
ValueError(f'Unsupported URL scheme: {url.scheme}')
def get_remote_config(urlstring):
"""
Download remote (config) file and return the contents.
Args:
remote file URI:
scp://<user>[:<passwd>]@<host>/<file>
sftp://<user>[:<passwd>]@<host>/<file>
http://<host>/<file>
https://<host>/<file>
ftp://[<user>[:<passwd>]@]<host>/<file>
tftp://<host>/<file>
"""
url = urllib.parse.urlparse(urlstring)
temp = tempfile.NamedTemporaryFile(delete=False).name
try:
download(temp, urlstring)
with open(temp, 'r') as file:
return file.read()
finally:
os.remove(temp)
diff --git a/src/helpers/strip-private.py b/src/helpers/strip-private.py
index 023298eec..420a039eb 100755
--- a/src/helpers/strip-private.py
+++ b/src/helpers/strip-private.py
@@ -1,142 +1,147 @@
#!/usr/bin/python3
# Copyright 2021 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 argparse
import re
import sys
from netaddr import IPNetwork, AddrFormatError
parser = argparse.ArgumentParser(description='strip off private information from VyOS config')
strictness = parser.add_mutually_exclusive_group()
strictness.add_argument('--loose', action='store_true', help='remove only information specified as arguments')
strictness.add_argument('--strict', action='store_true', help='remove any private information (implies all arguments below). This is the default behavior.')
parser.add_argument('--mac', action='store_true', help='strip off MAC addresses')
parser.add_argument('--hostname', action='store_true', help='strip off system host and domain names')
parser.add_argument('--username', action='store_true', help='strip off user names')
parser.add_argument('--dhcp', action='store_true', help='strip off DHCP shared network and static mapping names')
parser.add_argument('--domain', action='store_true', help='strip off domain names')
parser.add_argument('--asn', action='store_true', help='strip off BGP ASNs')
parser.add_argument('--snmp', action='store_true', help='strip off SNMP location information')
parser.add_argument('--lldp', action='store_true', help='strip off LLDP location information')
address_preserval = parser.add_mutually_exclusive_group()
address_preserval.add_argument('--address', action='store_true', help='strip off all IPv4 and IPv6 addresses')
address_preserval.add_argument('--public-address', action='store_true', help='only strip off public IPv4 and IPv6 addresses')
address_preserval.add_argument('--keep-address', action='store_true', help='preserve all IPv4 and IPv6 addresses')
# Censor the first half of the address.
ipv4_re = re.compile(r'(\d{1,3}\.){2}(\d{1,3}\.\d{1,3})')
ipv4_subst = r'xxx.xxx.\2'
# Censor all but the first two fields.
ipv6_re = re.compile(r'([0-9a-fA-F]{1,4}\:){2}(\S+)')
ipv6_subst = r'xxxx:xxxx:\2'
def ip_match(match: re.Match, subst: str) -> str:
"""
Take a Match and a substitution pattern, check if the match contains a valid IP address, strip
information if it is. This routine is intended to be passed to `re.sub' as a replacement pattern.
"""
result = match.group(0)
# Is this a valid IP address?
try:
addr = IPNetwork(result).ip
# No? Then we've got nothing to do with it.
except AddrFormatError:
return result
# Should we strip it?
if args.address or (args.public_address and not addr.is_private()):
return match.expand(subst)
# No? Then we'll leave it as is.
else:
return result
def strip_address(line: str) -> str:
"""
Strip IPv4 and IPv6 addresses from the given string.
"""
return ipv4_re.sub(lambda match: ip_match(match, ipv4_subst), ipv6_re.sub(lambda match: ip_match(match, ipv6_subst), line))
def strip_lines(rules: tuple) -> None:
"""
Read stdin line by line and apply the given stripping rules.
"""
- for line in sys.stdin:
- if not args.keep_address:
- line = strip_address(line)
- for (condition, regexp, subst) in rules:
- if condition:
- line = regexp.sub(subst, line)
- print(line, end='')
+ try:
+ for line in sys.stdin:
+ if not args.keep_address:
+ line = strip_address(line)
+ for (condition, regexp, subst) in rules:
+ if condition:
+ line = regexp.sub(subst, line)
+ print(line, end='')
+ # stdin can be cut for any reason, such as user interrupt or the pager terminating before the text can be read.
+ # All we can do is gracefully exit.
+ except (BrokenPipeError, EOFError, KeyboardInterrupt):
+ sys.exit(1)
if __name__ == "__main__":
args = parser.parse_args()
# Strict mode is the default and the absence of loose mode implies presence of strict mode.
if not args.loose:
for arg in [args.mac, args.domain, args.hostname, args.username, args.dhcp, args.asn, args.snmp, args.lldp]:
arg = True
if not args.public_address and not args.keep_address:
args.address = True
elif not args.address and not args.public_address:
args.keep_address = True
# (condition, precompiled regexp, substitution string)
stripping_rules = [
# Strip passwords
(True, re.compile(r'password \S+'), 'password xxxxxx'),
# Strip public key information
(True, re.compile(r'public-keys \S+'), 'public-keys xxxx@xxx.xxx'),
(True, re.compile(r'type \'ssh-(rsa|dss)\''), 'type ssh-xxx'),
(True, re.compile(r' key \S+'), ' key xxxxxx'),
# Strip OpenVPN secrets
(True, re.compile(r'(shared-secret-key-file|ca-cert-file|cert-file|dh-file|key-file|client) (\S+)'), r'\1 xxxxxx'),
# Strip IPSEC secrets
(True, re.compile(r'pre-shared-secret \S+'), 'pre-shared-secret xxxxxx'),
# Strip OSPF md5-key
(True, re.compile(r'md5-key \S+'), 'md5-key xxxxxx'),
# Strip MAC addresses
(args.mac, re.compile(r'([0-9a-fA-F]{2}\:){5}([0-9a-fA-F]{2}((\:{0,1})){3})'), r'XX:XX:XX:XX:XX:\2'),
# Strip host-name, domain-name, and domain-search
(args.hostname, re.compile(r'(host-name|domain-name|domain-search) \S+'), r'\1 xxxxxx'),
# Strip user-names
(args.username, re.compile(r'(user|username|user-id) \S+'), r'\1 xxxxxx'),
# Strip full-name
(args.username, re.compile(r'(full-name) [ -_A-Z a-z]+'), r'\1 xxxxxx'),
# Strip DHCP static-mapping and shared network names
(args.dhcp, re.compile(r'(shared-network-name|static-mapping) \S+'), r'\1 xxxxxx'),
# Strip host/domain names
(args.domain, re.compile(r' (peer|remote-host|local-host|server) ([\w-]+\.)+[\w-]+'), r' \1 xxxxx.tld'),
# Strip BGP ASNs
(args.asn, re.compile(r'(bgp|remote-as) (\d+)'), r'\1 XXXXXX'),
# Strip LLDP location parameters
(args.lldp, re.compile(r'(altitude|datum|latitude|longitude|ca-value|country-code) (\S+)'), r'\1 xxxxxx'),
# Strip SNMP location
(args.snmp, re.compile(r'(location) \S+'), r'\1 xxxxxx'),
]
strip_lines(stripping_rules)

File Metadata

Mime Type
text/x-diff
Expires
Sat, Sep 26, 10:13 AM (1 d, 11 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
4284911
Default Alt Text
(12 KB)

Event Timeline