晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。 林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田、美池、桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。 见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:“不足为外人道也。”(间隔 一作:隔绝) 既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。 南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。
|
Server : Apache System : Linux srv.rainic.com 4.18.0-553.47.1.el8_10.x86_64 #1 SMP Wed Apr 2 05:45:37 EDT 2025 x86_64 User : rainic ( 1014) PHP Version : 7.4.33 Disable Function : exec,passthru,shell_exec,system Directory : /usr/lib/python3.6/site-packages/sos/cleaner/mappings/ |
Upload File : |
# Copyright 2022 Red Hat, Inc. Jake Hunsaker <jhunsake@redhat.com>
# This file is part of the sos project: https://github.com/sosreport/sos
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# version 2 of the GNU General Public License.
#
# See the LICENSE file in the source distribution for further information.
import ipaddress
from random import getrandbits
from sos.cleaner.mappings import SoSMap
def generate_hextets(hextets):
"""Generate a random set of hextets, based on the length of the source
hextet. If any hextets are compressed, keep that compression.
E.G. '::1234:bcd' will generate a leading empty '' hextet, followed by two
4-character hextets.
:param hextets: The extracted hextets from a source address
:type hextets: ``list``
:returns: A set of randomized hextets for use in an obfuscated
address
:rtype: ``list``
"""
return [random_hex(4) if h else '' for h in hextets]
def random_hex(length):
"""Generate a string of size length of random hex characters.
:param length: The number of characters to generate
:type length: ``int``
:returns: A string of ``length`` hex characters
:rtype: ``str``
"""
return f"{getrandbits(4*length):0{length}x}"
class SoSIPv6Map(SoSMap):
"""Mapping for IPv6 addresses and networks.
Much like the IP map handles IPv4 addresses, this map is designed to take
IPv6 strings and obfuscate them consistently to maintain network topology.
To do this, addresses will be manipulated by the ipaddress library.
If an IPv6 address is encountered without a netmask, it is assumed to be a
/64 address.
"""
networks = {}
ignore_matches = [
r'^::1/.*',
r'::/0',
r'fd53:.*',
r'^53..:'
]
first_hexes = ['534f']
compile_regexes = False
version = 1
def conf_update(self, config):
"""Override the base conf_update() so that we can load the existing
networks into ObfuscatedIPv6Network() objects for the current run.
"""
if 'networks' not in config:
return
for network in config['networks']:
_orig = ipaddress.ip_network(network)
_obfuscated = config['networks'][network]['obfuscated']
_net = self._get_network(_orig, _obfuscated)
self.dataset[_net.original_address] = _net.obfuscated_address
for host in config['networks'][network]['hosts']:
_ob_host = config['networks'][network]['hosts'][host]
_net.add_obfuscated_host_address(host, _ob_host)
self.dataset[host] = _ob_host
def sanitize_item(self, item):
_prefix = item.split('/')[-1] if '/' in item else ''
_ipaddr = item
if not _prefix:
# assume a /64 default per protocol
_ipaddr += "/64"
try:
_addr = ipaddress.ip_network(_ipaddr)
# ipaddr was an actual network per protocol
_net = self._get_network(_addr)
_ipaddr = _net.obfuscated_address
except ValueError:
# A ValueError is raised from the ipaddress module when passing
# an address such as 2620:52:0:2d80::4fe/64, which has host bits
# '::4fe' set - the /64 is generally interpreted only for network
# addresses. We use this behavior to properly obfuscate the network
# before obfuscating a host address within that network
_addr = ipaddress.ip_network(_ipaddr, strict=False)
_net = self._get_network(_addr)
if _net.network_addr not in self.dataset:
self.dataset[_net.original_address] = _net.obfuscated_address
# then, get the address within the network
_hostaddr = ipaddress.ip_address(_ipaddr.split('/')[0])
_ipaddr = _net.obfuscate_host_address(_hostaddr)
if _prefix and '/' not in _ipaddr:
return f"{_ipaddr}/{_prefix}"
return _ipaddr
def _get_network(self, address, obfuscated=''):
"""Attempt to find an existing ObfuscatedIPv6Network object from which
to either find an existing obfuscated match, or create a new one. If
no such object already exists, create it.
"""
_addr = address.compressed
if _addr not in self.networks:
self.networks[_addr] = ObfuscatedIPv6Network(address, obfuscated,
self.first_hexes)
return self.networks[_addr]
class ObfuscatedIPv6Network():
"""An abstraction class that represents a network that is (to be) handled
by sos.
Each distinct IPv6 network that we encounter will have a representative
instance of this class, from which new obfuscated subnets and host
addresses will be generated.
This class should be built from an ``ipaddress.IPv6Network`` object. If
an obfuscation string is not passed, one will be created during init.
"""
def __init__(self, addr, obfuscation='', used_hexes=None):
"""Basic setup for the obfuscated network. Minor validation on the addr
used to create the instance, as well as on an optional ``obfuscation``
which if set, will serve as the obfuscated_network address.
:param addr: The *un*obfuscated network to be handled
:type addr: ``ipaddress.IPv6Network``
:param obfuscation: An optional pre-determined string representation of
the obfuscated network address
:type obfuscation: ``str``
:param used_hexes: A list of already used hexes for the first hextet
of a potential global address obfuscation
:type used_hexes: ``list``
"""
if not isinstance(addr, ipaddress.IPv6Network):
raise Exception('Invalid network: not an IPv6Network object')
self.addr = addr
self.prefix = addr.prefixlen
self.network_addr = addr.network_address.compressed
self.hosts = {}
if used_hexes is None:
self.first_hexes = ['534f']
else:
self.first_hexes = used_hexes
if not obfuscation:
self._obfuscated_network = self._obfuscate_network_address()
else:
if not isinstance(obfuscation, str):
raise TypeError(f"Pre-determined obfuscated network address "
f"must be str, not {type(obfuscation)}")
self._obfuscated_network = obfuscation.split('/')[0]
@property
def obfuscated_address(self):
return f"{self._obfuscated_network}/{self.prefix}"
@property
def original_address(self):
return self.addr.compressed
def _obfuscate_network_address(self):
"""Generate the obfuscated pair for the network address. This is
determined based on the netmask of the network this class was built
on top of.
"""
if self.addr.is_global:
return self._obfuscate_global_address()
if self.addr.is_link_local:
# link-local addresses are always fe80::/64. This is not sensitive
# in itself, and retaining the information that an address is a
# link-local address is important for problem analysis, so don't
# obfuscate this network information.
return self.network_addr
if self.addr.is_private:
return self._obfuscate_private_address()
return self.network_addr
def _obfuscate_global_address(self):
"""Global unicast addresses have a 48-bit global routing prefix and a
16-bit subnet. We set the global routing prefix to a static
sos-specific identifier that could never be seen in the wild,
'534f:'
We then randomize the subnet hextet.
"""
_hextets = self.network_addr.split(':')[1:]
_ob_hex = ['534f']
if all(not c for c in _hextets):
# we have only a single defined hextet, e.g. ff00::/64, so we need
# to not use the standard first-hex identifier or we'll overlap
# every similar address obfuscation.
# Set the leading bits to 53, but increment upwards from there for
# when we exceed 256 networks obfuscated in this manner.
_start = 53 + (len(self.first_hexes) // 256)
_ob_hex = f"{_start}{random_hex(2)}"
while _ob_hex in self.first_hexes:
# prevent duplicates
_ob_hex = f"{_start}{random_hex(2)}"
self.first_hexes.append(_ob_hex)
_ob_hex = [_ob_hex]
_ob_hex.extend(generate_hextets(_hextets))
return ':'.join(_ob_hex)
def _obfuscate_private_address(self):
"""The first 8 bits will always be 'fd', the next 40 bits are meant
to be a global ID, followed by 16 bits for the subnet. To keep things
relatively simply we maintain the first hextet as 'fd53', and then
randomize any remaining hextets
"""
_hextets = self.network_addr.split(':')[1:]
_ob_hex = ['fd53']
_ob_hex.extend(generate_hextets(_hextets))
return ':'.join(_ob_hex)
def obfuscate_host_address(self, addr):
"""Given an unobfuscated address, generate an obfuscated match for it,
and save it to this network for tracking during the execution of clean.
Note: another way to do this would be to convert the obfuscated network
to bytes, and add a random amount to that based on the number of
addresses that the network can support and from that new bytes count
craft a new IPv6 address. This has the advantage of absolutely
guaranteeing the new address is within the network space (whereas the
method employed below could *theoretically* generate an overlapping
address), but would in turn remove any ability to compress obfuscated
addresses to match the general format/syntax of the address it is
replacing. For the moment, it is assumed that being able to maintain a
quick mental note of "unobfuscated device ff00::1 is obfuscated device
53ad::a1b2" is more desireable than "ff00::1 is now obfuscated as
53ad::1234:abcd:9876:a1b2:".
:param addr: The unobfuscated IPv6 address
:type addr: ``ipaddress.IPv6Address``
:returns: An obfuscated address within this network
:rtype: ``str``
"""
def _generate_address(host):
return ''.join([
self._obfuscated_network,
':'.join(generate_hextets(host.split(':')))
])
if addr.compressed not in self.hosts:
# separate host from the address by removing its network prefix
_n = self.network_addr.rstrip(':')
_host = addr.compressed[len(_n):].lstrip(':')
_ob_host = _generate_address(_host)
while _ob_host in self.hosts.values():
_ob_host = _generate_address(_host)
self.add_obfuscated_host_address(addr.compressed, _ob_host)
return self.hosts[addr.compressed]
def add_obfuscated_host_address(self, host, obfuscated):
"""Adds an obfuscated pair to the class for tracking and ongoing
consistency in obfuscation.
"""
self.hosts[host] = obfuscated