mirror of
https://github.com/dkmstr/openuds.git
synced 2024-12-23 17:34:17 +03:00
fixing up network & more to support ipv6
This commit is contained in:
parent
659013db56
commit
89e1c2eac5
@ -41,33 +41,58 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class NetTest(UDSTestCase):
|
||||
|
||||
def testNetworkFromString(self):
|
||||
def testNetworkFromStringIPv4(self):
|
||||
for n in (
|
||||
('*', 0, 4294967295),
|
||||
('192.168.0.1', 3232235521, 3232235521),
|
||||
('192.168.0.*', 3232235520, 3232235775),
|
||||
('192.168.*.*', 3232235520, 3232301055),
|
||||
('192.168.*', 3232235520, 3232301055),
|
||||
('192.*.*.*', 3221225472, 3238002687),
|
||||
('192.*.*', 3221225472, 3238002687),
|
||||
('192.*', 3221225472, 3238002687),
|
||||
('192.168.0.1 netmask 255.255.255.0', 3232235520, 3232235775),
|
||||
('192.168.0.1/8', 3221225472, 3238002687),
|
||||
('192.168.0.1/28', 3232235520, 3232235535),
|
||||
('192.168.0.1-192.168.0.87', 3232235521, 3232235607),
|
||||
('192.168.0.1 netmask 255.255.255.0', 3232235520, 3232235775),
|
||||
):
|
||||
('*', 0, 4294967295),
|
||||
('192.168.0.1', 3232235521, 3232235521),
|
||||
('192.168.0.*', 3232235520, 3232235775),
|
||||
('192.168.*.*', 3232235520, 3232301055),
|
||||
('192.168.*', 3232235520, 3232301055),
|
||||
('192.*.*.*', 3221225472, 3238002687),
|
||||
('192.*.*', 3221225472, 3238002687),
|
||||
('192.*', 3221225472, 3238002687),
|
||||
('192.168.0.1 netmask 255.255.255.0', 3232235520, 3232235775),
|
||||
('192.168.0.1/8', 3221225472, 3238002687),
|
||||
('192.168.0.1/28', 3232235520, 3232235535),
|
||||
('192.168.0.1-192.168.0.87', 3232235521, 3232235607),
|
||||
('192.168.0.1 netmask 255.255.255.0', 3232235520, 3232235775),
|
||||
):
|
||||
try:
|
||||
multiple_net: typing.List[net.NetworkType] = net.networksFromString(n[0])
|
||||
self.assertEqual(len(multiple_net), 1, 'Incorrect number of network returned from {0}'.format(n[0]))
|
||||
self.assertEqual(multiple_net[0][0], n[1], 'Incorrect network start value for {0}'.format(n[0]))
|
||||
self.assertEqual(multiple_net[0][1], n[2], 'Incorrect network end value for {0}'.format(n[0]))
|
||||
multiple_net: typing.List[net.NetworkType] = net.networksFromString(
|
||||
n[0]
|
||||
)
|
||||
self.assertEqual(
|
||||
len(multiple_net),
|
||||
1,
|
||||
'Incorrect number of network returned from {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
multiple_net[0][0],
|
||||
n[1],
|
||||
'Incorrect network start value for {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
multiple_net[0][1],
|
||||
n[2],
|
||||
'Incorrect network end value for {0}'.format(n[0]),
|
||||
)
|
||||
|
||||
single_net: net.NetworkType = net.networkFromString(n[0])
|
||||
self.assertEqual(len(single_net), 2, 'Incorrect number of network returned from {0}'.format(n[0]))
|
||||
self.assertEqual(single_net[0], n[1], 'Incorrect network start value for {0}'.format(n[0]))
|
||||
self.assertEqual(single_net[1], n[2], 'Incorrect network end value for {0}'.format(n[0]))
|
||||
self.assertEqual(
|
||||
len(single_net),
|
||||
3,
|
||||
'Incorrect number of network returned from {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
single_net[0],
|
||||
n[1],
|
||||
'Incorrect network start value for {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
single_net[1],
|
||||
n[2],
|
||||
'Incorrect network end value for {0}'.format(n[0]),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception('Running test')
|
||||
raise Exception('Value Error: {}. Input string: {}'.format(e, n[0]))
|
||||
@ -76,12 +101,91 @@ class NetTest(UDSTestCase):
|
||||
with self.assertRaises(ValueError):
|
||||
net.networksFromString(n)
|
||||
|
||||
self.assertEqual(net.ipToLong('192.168.0.5'), 3232235525)
|
||||
self.assertEqual(net.longToIp(3232235525), '192.168.0.5')
|
||||
self.assertEqual(net.ipToLong('192.168.0.5').ip, 3232235525)
|
||||
self.assertEqual(net.longToIp(3232235525, 4), '192.168.0.5')
|
||||
for n in range(0, 255):
|
||||
self.assertTrue(net.ipInNetwork('192.168.0.{}'.format(n), '192.168.0.0/24'))
|
||||
|
||||
for n in range(4294):
|
||||
self.assertTrue(net.ipInNetwork(n*1000, [net.NetworkType(0, 4294967295)]))
|
||||
self.assertTrue(net.ipInNetwork(n*1000, net.NetworkType(0, 4294967295)))
|
||||
self.assertTrue(
|
||||
net.ipInNetwork(n * 1000, [net.NetworkType(0, 4294967295, 4)])
|
||||
)
|
||||
self.assertTrue(
|
||||
net.ipInNetwork(n * 1000, net.NetworkType(0, 4294967295, 4))
|
||||
)
|
||||
|
||||
def testNetworkFromStringIPv6(self):
|
||||
# IPv6 only support standard notation, and '*', but not "netmask" or "range"
|
||||
for n in (
|
||||
(
|
||||
'*',
|
||||
0,
|
||||
2**128 - 1,
|
||||
), # This could be confused with ipv4 *, so we take care
|
||||
(
|
||||
'2001:db8::1',
|
||||
42540766411282592856903984951653826561,
|
||||
42540766411282592856903984951653826561,
|
||||
),
|
||||
(
|
||||
'2001:db8::1/64',
|
||||
42540766411282592856903984951653826560,
|
||||
42540766411282592875350729025363378175,
|
||||
),
|
||||
(
|
||||
'2001:db8::1/28',
|
||||
42540765777457292742789284203302223872,
|
||||
42540767045107892971018685700005429247,
|
||||
),
|
||||
(
|
||||
'2222:3333:4444:5555:6666:7777:8888:9999/64',
|
||||
45371328414530988873481865147602436096,
|
||||
45371328414530988891928609221311987711,
|
||||
),
|
||||
(
|
||||
'fe80::/10',
|
||||
33828852492726108965401889684134769408,
|
||||
33828852492726108965401889684134769408 + 2**118 - 1,
|
||||
),
|
||||
):
|
||||
try:
|
||||
multiple_net: typing.List[net.NetworkType] = net.networksFromString(
|
||||
n[0], version=(6 if n[0] == '*' else 0)
|
||||
)
|
||||
self.assertEqual(
|
||||
len(multiple_net),
|
||||
1,
|
||||
'Incorrect number of network returned from {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
multiple_net[0][0],
|
||||
n[1],
|
||||
'Incorrect network start value for {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
multiple_net[0][1],
|
||||
n[2],
|
||||
'Incorrect network end value for {0}'.format(n[0]),
|
||||
)
|
||||
|
||||
single_net: net.NetworkType = net.networkFromString(
|
||||
n[0], version=(6 if n[0] == '*' else 0)
|
||||
)
|
||||
self.assertEqual(
|
||||
len(single_net),
|
||||
3,
|
||||
'Incorrect number of network returned from {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
single_net[0],
|
||||
n[1],
|
||||
'Incorrect network start value for {0}'.format(n[0]),
|
||||
)
|
||||
self.assertEqual(
|
||||
single_net[1],
|
||||
n[2],
|
||||
'Incorrect network end value for {0}'.format(n[0]),
|
||||
)
|
||||
except Exception as e:
|
||||
logger.exception('Running test')
|
||||
raise Exception('Value Error: {}. Input string: {}'.format(e, n[0]))
|
||||
|
@ -229,6 +229,7 @@ class Register(ActorV3Action):
|
||||
username=self._user.pretty_name,
|
||||
ip_from=self._request.ip,
|
||||
ip=self._params['ip'],
|
||||
ip_version=self._request.ip_version,
|
||||
hostname=self._params['hostname'],
|
||||
mac=self._params['mac'],
|
||||
pre_command=self._params['pre_command'],
|
||||
|
@ -67,21 +67,23 @@ class Networks(ModelHandler):
|
||||
}
|
||||
},
|
||||
{'net_string': {'title': _('Range')}},
|
||||
{'transports_count': {'title': _('Transports'), 'type': 'numeric', 'width': '8em'}},
|
||||
{'authenticators_count': {'title': _('Authenticators'), 'type': 'numeric', 'width': '8em'}},
|
||||
{
|
||||
'transports_count': {
|
||||
'title': _('Transports'),
|
||||
'type': 'numeric',
|
||||
'width': '8em',
|
||||
}
|
||||
},
|
||||
{
|
||||
'authenticators_count': {
|
||||
'title': _('Authenticators'),
|
||||
'type': 'numeric',
|
||||
'width': '8em',
|
||||
}
|
||||
},
|
||||
{'tags': {'title': _('tags'), 'visible': False}},
|
||||
]
|
||||
|
||||
def beforeSave(self, fields: typing.Dict[str, typing.Any]) -> None:
|
||||
logger.debug('Before %s', fields)
|
||||
try:
|
||||
nr = net.networkFromString(fields['net_string'])
|
||||
fields['net_start'] = nr[0]
|
||||
fields['net_end'] = nr[1]
|
||||
except Exception as e:
|
||||
raise SaveException(gettext('Invalid network: {}').format(e))
|
||||
logger.debug('Processed %s', fields)
|
||||
|
||||
def getGui(self, type_: str) -> typing.List[typing.Any]:
|
||||
return self.addField(
|
||||
self.addDefaultFields([], ['name', 'tags']),
|
||||
|
@ -116,11 +116,11 @@ class TunnelTicket(Handler):
|
||||
received=recv,
|
||||
tunnel=extra.get('t', 'unknown'),
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
except Exception as e:
|
||||
logger.warning('Error logging tunnel close event: %s', e)
|
||||
|
||||
else:
|
||||
if net.ipToLong(self._args[1][:32]) == 0:
|
||||
if net.ipToLong(self._args[1][:32]).version == 0:
|
||||
raise Exception('Invalid from IP')
|
||||
events.addEvent(
|
||||
userService.deployed_service,
|
||||
|
@ -162,6 +162,9 @@ class GlobalRequestMiddleware:
|
||||
request.ip_proxy = proxies[1] if len(proxies) > 1 else request.ip
|
||||
logger.debug('Behind a proxy is active')
|
||||
|
||||
# Check if ip are ipv6 and set version field
|
||||
request.ip_version = 6 if ':' in request.ip else 4
|
||||
|
||||
logger.debug('ip: %s, ip_proxy: %s', request.ip, request.ip_proxy)
|
||||
|
||||
@staticmethod
|
||||
|
@ -34,60 +34,63 @@ import re
|
||||
import socket
|
||||
import logging
|
||||
import typing
|
||||
import ipaddress
|
||||
import enum
|
||||
|
||||
class IpType(typing.NamedTuple):
|
||||
ip: int
|
||||
version: typing.Literal[4, 6, 0] # 0 is only used for invalid detected ip
|
||||
|
||||
class NetworkType(typing.NamedTuple):
|
||||
start: int
|
||||
end: int
|
||||
version: typing.Literal[4, 6] # 4 or 6
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Test patters for networks
|
||||
reCIDR = re.compile(
|
||||
# Test patters for networks IPv4
|
||||
reCIDRIPv4 = re.compile(
|
||||
r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})/([0-9]{1,2})$'
|
||||
)
|
||||
reMask = re.compile(
|
||||
reMaskIPv4 = re.compile(
|
||||
r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})netmask([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
||||
)
|
||||
re1Asterisk = re.compile(r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.\*$')
|
||||
re2Asterisk = re.compile(r'^([0-9]{1,3})\.([0-9]{1,3})\.\*\.?\*?$')
|
||||
re3Asterisk = re.compile(r'^([0-9]{1,3})\.\*\.?\*?\.?\*?$')
|
||||
reRange = re.compile(
|
||||
re1AsteriskIPv4 = re.compile(r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.\*$')
|
||||
re2AsteriskIPv4 = re.compile(r'^([0-9]{1,3})\.([0-9]{1,3})\.\*\.?\*?$')
|
||||
re3AsteriskIPv4 = re.compile(r'^([0-9]{1,3})\.\*\.?\*?\.?\*?$')
|
||||
reRangeIPv4 = re.compile(
|
||||
r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})-([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$'
|
||||
)
|
||||
reHost = re.compile(r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$')
|
||||
reSingleIPv4 = re.compile(r'^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})$')
|
||||
|
||||
|
||||
def ipToLong(ip: str) -> int:
|
||||
def ipToLong(ip: str) -> IpType:
|
||||
"""
|
||||
convert decimal dotted quad string to long integer
|
||||
Convert an ipv4 or ipv6 address to its long representation
|
||||
"""
|
||||
# First, check if it's an ipv6 address
|
||||
try:
|
||||
hexn = int(''.join(["%02X" % int(i) for i in ip.split('.')]), 16)
|
||||
logger.debug('IP %s is %s', ip, hexn)
|
||||
return hexn
|
||||
if ':' in ip:
|
||||
return IpType(int(ipaddress.IPv6Address(ip)), 6)
|
||||
else: # ipv4
|
||||
return IpType(int(ipaddress.IPv4Address(ip)), 4)
|
||||
except Exception as e:
|
||||
logger.error('Ivalid value: %s (%s)', ip, e)
|
||||
return 0 # Invalid values will map to "0.0.0.0" --> 0
|
||||
return IpType(0, 0) # Invalid values will map to "0.0.0.0" --> 0
|
||||
|
||||
|
||||
def longToIp(n: int) -> str:
|
||||
def longToIp(n: int, version: typing.Literal[0, 4, 6] = 0) -> str:
|
||||
"""
|
||||
convert long int to dotted quad string
|
||||
convert long int to ipv4 or ipv6 address, depending on size
|
||||
"""
|
||||
try:
|
||||
d = 1 << 24
|
||||
q = []
|
||||
while d > 0:
|
||||
m, n = divmod(n, d)
|
||||
q.append(str(m)) # As m is an integer, this works on py2 and p3 correctly
|
||||
d >>= 8
|
||||
|
||||
return '.'.join(q)
|
||||
except Exception:
|
||||
return '0.0.0.0' # nosec: Invalid values will map to "0.0.0.0"
|
||||
if n > 2**32 or version == 6:
|
||||
return str(ipaddress.IPv6Address(n))
|
||||
else:
|
||||
return str(ipaddress.IPv4Address(n))
|
||||
|
||||
|
||||
def networkFromString(strNets: str) -> NetworkType:
|
||||
def networkFromStringIPv4(strNets: str, version: typing.Literal[0, 4, 6] = 0) -> NetworkType:
|
||||
'''
|
||||
Parses the network from strings in this forms:
|
||||
- A.* (or A.*.* or A.*.*.*)
|
||||
@ -99,7 +102,7 @@ def networkFromString(strNets: str) -> NetworkType:
|
||||
- A.B.C.D
|
||||
returns a named tuple with networks start and network end
|
||||
'''
|
||||
|
||||
|
||||
inputString = strNets
|
||||
logger.debug('Getting network from %s', strNets)
|
||||
|
||||
@ -125,11 +128,11 @@ def networkFromString(strNets: str) -> NetworkType:
|
||||
strNets = strNets.replace(' ', '')
|
||||
|
||||
if strNets == '*':
|
||||
return NetworkType(0, 4294967295)
|
||||
return NetworkType(0, 2**32 - 1, 4)
|
||||
|
||||
try:
|
||||
# Test patterns
|
||||
m = reCIDR.match(strNets)
|
||||
m = reCIDRIPv4.match(strNets)
|
||||
if m is not None:
|
||||
logger.debug('Format is CIDR')
|
||||
check(*m.groups())
|
||||
@ -139,18 +142,18 @@ def networkFromString(strNets: str) -> NetworkType:
|
||||
val = toNum(*m.groups())
|
||||
bits = maskFromBits(bits)
|
||||
noBits = ~bits & 0xFFFFFFFF
|
||||
return NetworkType(val & bits, val | noBits)
|
||||
return NetworkType(val & bits, val | noBits, 4)
|
||||
|
||||
m = reMask.match(strNets)
|
||||
m = reMaskIPv4.match(strNets)
|
||||
if m is not None:
|
||||
logger.debug('Format is network mask')
|
||||
check(*m.groups())
|
||||
val = toNum(*(m.groups()[0:4]))
|
||||
bits = toNum(*(m.groups()[4:8]))
|
||||
noBits = ~bits & 0xFFFFFFFF
|
||||
return NetworkType(val & bits, val | noBits)
|
||||
return NetworkType(val & bits, val | noBits, 4)
|
||||
|
||||
m = reRange.match(strNets)
|
||||
m = reRangeIPv4.match(strNets)
|
||||
if m is not None:
|
||||
logger.debug('Format is network range')
|
||||
check(*m.groups())
|
||||
@ -158,23 +161,23 @@ def networkFromString(strNets: str) -> NetworkType:
|
||||
val2 = toNum(*(m.groups()[4:8]))
|
||||
if val2 < val:
|
||||
raise Exception()
|
||||
return NetworkType(val, val2)
|
||||
return NetworkType(val, val2, 4)
|
||||
|
||||
m = reHost.match(strNets)
|
||||
m = reSingleIPv4.match(strNets)
|
||||
if m is not None:
|
||||
logger.debug('Format is a single host')
|
||||
check(*m.groups())
|
||||
val = toNum(*m.groups())
|
||||
return NetworkType(val, val)
|
||||
return NetworkType(val, val, 4)
|
||||
|
||||
for v in ((re1Asterisk, 3), (re2Asterisk, 2), (re3Asterisk, 1)):
|
||||
for v in ((re1AsteriskIPv4, 3), (re2AsteriskIPv4, 2), (re3AsteriskIPv4, 1)):
|
||||
m = v[0].match(strNets)
|
||||
if m is not None:
|
||||
check(*m.groups())
|
||||
val = toNum(*(m.groups()[0 : v[1] + 1]))
|
||||
bits = maskFromBits(v[1] * 8)
|
||||
noBits = ~bits & 0xFFFFFFFF
|
||||
return NetworkType(val & bits, val | noBits)
|
||||
return NetworkType(val & bits, val | noBits, 4)
|
||||
|
||||
# No pattern recognized, invalid network
|
||||
raise Exception()
|
||||
@ -183,8 +186,38 @@ def networkFromString(strNets: str) -> NetworkType:
|
||||
raise ValueError(inputString)
|
||||
|
||||
|
||||
def networkFromStringIPv6(strNets: str, version: typing.Literal[0, 4, 6] = 0) -> NetworkType:
|
||||
'''
|
||||
returns a named tuple with networks start and network end
|
||||
'''
|
||||
logger.debug('Getting network from %s', strNets)
|
||||
|
||||
# if '*' or '::*', return the whole IPv6 range
|
||||
if strNets == '*' or strNets == '::*':
|
||||
return NetworkType(0, 2**128 - 1, 6)
|
||||
|
||||
try:
|
||||
# using ipaddress module
|
||||
net = ipaddress.ip_network(strNets, strict=False)
|
||||
return NetworkType(int(net.network_address), int(net.broadcast_address), 6)
|
||||
except Exception as e:
|
||||
logger.error('Invalid network found: %s %s', strNets, e)
|
||||
raise ValueError(strNets)
|
||||
|
||||
|
||||
def networkFromString(
|
||||
strNets: str,
|
||||
version: typing.Literal[0, 4, 6] = 0,
|
||||
) -> NetworkType:
|
||||
if not ':' in strNets and version != 6:
|
||||
return networkFromStringIPv4(strNets, version)
|
||||
else: # ':' in strNets or version == 6:
|
||||
return networkFromStringIPv6(strNets, version)
|
||||
|
||||
|
||||
def networksFromString(
|
||||
strNets: str
|
||||
strNets: str,
|
||||
version: typing.Literal[0, 4, 6] = 0,
|
||||
) -> typing.List[NetworkType]:
|
||||
"""
|
||||
If allowMultipleNetworks is True, it allows ',' and ';' separators (and, ofc, more than 1 network)
|
||||
@ -192,35 +225,39 @@ def networksFromString(
|
||||
"""
|
||||
res = []
|
||||
for strNet in re.split('[;,]', strNets):
|
||||
if strNet != '':
|
||||
res.append(typing.cast(NetworkType, networkFromString(strNet)))
|
||||
if strNet:
|
||||
res.append(networkFromString(strNet, version))
|
||||
return res
|
||||
|
||||
|
||||
def ipInNetwork(
|
||||
ip: typing.Union[str, int], network: typing.Union[str, NetworkType, typing.List[NetworkType]]
|
||||
ip: typing.Union[str, int],
|
||||
networks: typing.Union[str, NetworkType, typing.List[NetworkType]],
|
||||
version: typing.Literal[0, 4, 6] = 0,
|
||||
) -> bool:
|
||||
if isinstance(ip, str):
|
||||
ip = ipToLong(ip)
|
||||
if isinstance(network, str):
|
||||
network = networksFromString(network)
|
||||
elif isinstance(network, NetworkType):
|
||||
network = [network]
|
||||
ip, version = ipToLong(ip) # Ip overrides protocol version
|
||||
if isinstance(networks, str):
|
||||
if networks == '*':
|
||||
return True # All IPs are in the * network
|
||||
networks = networksFromString(networks, version)
|
||||
elif isinstance(networks, NetworkType):
|
||||
networks = [networks]
|
||||
|
||||
for net in network:
|
||||
if net[0] <= ip <= net[1]:
|
||||
# Ensure that the IP is in the same family as the network on checks
|
||||
for net in networks:
|
||||
if net.start <= ip <= net.end:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isValidIp(value: str) -> bool:
|
||||
return (
|
||||
re.match(
|
||||
r'^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$',
|
||||
value,
|
||||
)
|
||||
is not None
|
||||
)
|
||||
def isValidIp(value: str, version: typing.Literal[0, 4, 6] = 0) -> bool:
|
||||
# Using ipaddress module
|
||||
try:
|
||||
addr = ipaddress.ip_address(value)
|
||||
return version == 0 or addr.version == version
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def isValidFQDN(value: str) -> bool:
|
||||
|
@ -41,6 +41,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class ExtendedHttpRequest(HttpRequest):
|
||||
ip: str
|
||||
ip_version: int
|
||||
ip_proxy: str
|
||||
os: DictAsObj
|
||||
user: typing.Optional[User]
|
||||
|
@ -1,5 +1,4 @@
|
||||
# Generated by Django 4.1.3 on 2022-11-12 21:03
|
||||
import logging
|
||||
# Generated by Django 4.1.3 on 2022-11-29 02:37
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
@ -8,21 +7,33 @@ import uds.models.notifications
|
||||
import uds.models.user_service_session
|
||||
import uds.models.util
|
||||
|
||||
logger = logging.getLogger('uds')
|
||||
|
||||
def remove_servicepool_with_null_service(apps, schema_editor):
|
||||
""" In fact, there should be no Service Pool with no service, but we have found some in the wild"""
|
||||
# Remove ServicePools with null service field
|
||||
def remove_null_service_pools(apps, schema_editor):
|
||||
ServicePool = apps.get_model('uds', 'ServicePool')
|
||||
# Log in the django.db.backends logger removed services
|
||||
logger.info('Removing ServicePools with null service')
|
||||
for i in ServicePool.objects.filter(service=None):
|
||||
logger.info(' * Removing ServicePool %s - %s', i.uuid, i.name)
|
||||
i.delete()
|
||||
ServicePool.objects.filter(service__isnull=True).delete()
|
||||
|
||||
def null_backwards(apps, schema_editor):
|
||||
# Remove null services backwards is not possible, we have deleted them
|
||||
# No-Op backwards migration
|
||||
def nop(apps, schema_editor): # pragma: no cover
|
||||
pass
|
||||
|
||||
|
||||
# Python update network fields to allow ipv6
|
||||
# We will
|
||||
def update_network_model(apps, schema_editor):
|
||||
import uds.models.network
|
||||
Network = apps.get_model('uds', 'Network')
|
||||
try:
|
||||
for net in Network.objects.all():
|
||||
# Store the net_start and net_end on new fields "start" and "end", that are strings
|
||||
# to allow us to store ipv6 addresses
|
||||
net.start = uds.models.network.Network._hexlify(net.net_start)
|
||||
net.end = uds.models.network.Network._hexlify(net.net_end)
|
||||
net.version = 4 # Previous versions only supported ipv4
|
||||
net.save()
|
||||
except Exception as e:
|
||||
print('Error updating network model: {}'.format(e))
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
@ -30,8 +41,7 @@ class Migration(migrations.Migration):
|
||||
]
|
||||
|
||||
operations = [
|
||||
# First, we remove all DeployedServices with null service because we have fixed foreign key
|
||||
migrations.RunPython(remove_servicepool_with_null_service, null_backwards),
|
||||
migrations.RunPython(remove_null_service_pools, nop),
|
||||
migrations.CreateModel(
|
||||
name="Notification",
|
||||
fields=[
|
||||
@ -137,6 +147,10 @@ class Migration(migrations.Migration):
|
||||
migrations.DeleteModel(
|
||||
name="DBFile",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="userpreference",
|
||||
name="user",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="authenticator",
|
||||
name="visible",
|
||||
@ -153,6 +167,11 @@ class Migration(migrations.Migration):
|
||||
model_name="userservice",
|
||||
name="cluster_node",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="actortoken",
|
||||
name="ip_version",
|
||||
field=models.IntegerField(default=4),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="authenticator",
|
||||
name="net_filtering",
|
||||
@ -182,6 +201,31 @@ class Migration(migrations.Migration):
|
||||
to="uds.authenticator",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="network",
|
||||
name="end",
|
||||
field=models.CharField(db_index=True, default="0", max_length=40),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="network",
|
||||
name="start",
|
||||
field=models.CharField(db_index=True, default="0", max_length=40),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="network",
|
||||
name="version",
|
||||
field=models.IntegerField(default=4),
|
||||
),
|
||||
# Run python code to update network model
|
||||
migrations.RunPython(update_network_model, nop),
|
||||
migrations.RemoveField(
|
||||
model_name="network",
|
||||
name="net_end",
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name="network",
|
||||
name="net_start",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="service",
|
||||
name="max_services_count_type",
|
||||
@ -211,6 +255,21 @@ class Migration(migrations.Migration):
|
||||
default=uds.core.util.model.generateUuid, max_length=50, unique=True
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="actortoken",
|
||||
name="hostname",
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="actortoken",
|
||||
name="ip",
|
||||
field=models.CharField(max_length=45),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="actortoken",
|
||||
name="ip_from",
|
||||
field=models.CharField(max_length=45),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="authenticator",
|
||||
name="uuid",
|
||||
@ -288,6 +347,11 @@ class Migration(migrations.Migration):
|
||||
default=uds.core.util.model.generateUuid, max_length=50, unique=True
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="network",
|
||||
name="net_string",
|
||||
field=models.CharField(default="", max_length=240),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="network",
|
||||
name="uuid",
|
||||
@ -316,6 +380,11 @@ class Migration(migrations.Migration):
|
||||
default=uds.core.util.model.generateUuid, max_length=50, unique=True
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="scheduler",
|
||||
name="owner_server",
|
||||
field=models.CharField(db_index=True, default="", max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="service",
|
||||
name="token",
|
||||
@ -381,15 +450,20 @@ class Migration(migrations.Migration):
|
||||
default=uds.core.util.model.generateUuid, max_length=50, unique=True
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="tunneltoken",
|
||||
name="hostname",
|
||||
field=models.CharField(max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="tunneltoken",
|
||||
name="ip",
|
||||
field=models.CharField(max_length=128),
|
||||
field=models.CharField(max_length=45),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="tunneltoken",
|
||||
name="ip_from",
|
||||
field=models.CharField(max_length=128),
|
||||
field=models.CharField(max_length=45),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="user",
|
||||
@ -398,10 +472,15 @@ class Migration(migrations.Migration):
|
||||
default=uds.core.util.model.generateUuid, max_length=50, unique=True
|
||||
),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="userservice",
|
||||
name="src_hostname",
|
||||
field=models.CharField(default="", max_length=255),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="userservice",
|
||||
name="src_ip",
|
||||
field=models.CharField(default="", max_length=128),
|
||||
field=models.CharField(default="", max_length=45),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="userservice",
|
||||
@ -413,6 +492,9 @@ class Migration(migrations.Migration):
|
||||
migrations.DeleteModel(
|
||||
name="Proxy",
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name="UserPreference",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="userservicesession",
|
||||
name="user_service",
|
||||
|
@ -55,7 +55,6 @@ from .network import Network
|
||||
# Authenticators
|
||||
from .authenticator import Authenticator
|
||||
from .user import User
|
||||
from .user_preference import UserPreference
|
||||
from .group import Group
|
||||
|
||||
# Provisioned services
|
||||
|
@ -30,6 +30,7 @@
|
||||
'''
|
||||
from django.db import models
|
||||
|
||||
from .util import MAX_IPV6_LENGTH, MAX_DNS_NAME_LENGTH
|
||||
|
||||
class ActorToken(models.Model):
|
||||
"""
|
||||
@ -37,9 +38,11 @@ class ActorToken(models.Model):
|
||||
"""
|
||||
|
||||
username = models.CharField(max_length=128)
|
||||
ip_from = models.CharField(max_length=128)
|
||||
ip = models.CharField(max_length=128)
|
||||
hostname = models.CharField(max_length=128)
|
||||
ip_from = models.CharField(max_length=MAX_IPV6_LENGTH)
|
||||
ip = models.CharField(max_length=MAX_IPV6_LENGTH)
|
||||
ip_version = models.IntegerField(default=4) # Version of ip fields
|
||||
|
||||
hostname = models.CharField(max_length=MAX_DNS_NAME_LENGTH)
|
||||
mac = models.CharField(max_length=128, db_index=True, unique=True)
|
||||
pre_command = models.CharField(max_length=255, blank=True, default='')
|
||||
post_command = models.CharField(max_length=255, blank=True, default='')
|
||||
|
@ -224,10 +224,10 @@ class Authenticator(ManagedObjectModel, TaggingMixin):
|
||||
"""
|
||||
if self.net_filtering == Authenticator.NO_FILTERING:
|
||||
return True
|
||||
ip = net.ipToLong(ipStr)
|
||||
ip, version = net.ipToLong(ipStr)
|
||||
# Allow
|
||||
if self.net_filtering == Authenticator.ALLOW:
|
||||
return self.networks.filter(net_start__lte=ip, net_end__gte=ip).exists()
|
||||
return self.networks.filter(net_start__lte=ip, net_end__gte=ip, version=version).exists()
|
||||
# Deny, must not be in any network
|
||||
return self.networks.filter(net_start__lte=ip, net_end__gte=ip).exists() is False
|
||||
|
||||
|
@ -52,9 +52,16 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
||||
"""
|
||||
|
||||
name = models.CharField(max_length=64, unique=True)
|
||||
net_start = models.BigIntegerField(db_index=True)
|
||||
net_end = models.BigIntegerField(db_index=True)
|
||||
net_string = models.CharField(max_length=128, default='')
|
||||
|
||||
start = models.CharField(
|
||||
max_length=32, default='0', db_index=True
|
||||
) # 128 bits, for IPv6, network byte order, hex
|
||||
end = models.CharField(
|
||||
max_length=32, default='0', db_index=True
|
||||
) # 128 bits, for IPv6, network byte order, hex
|
||||
|
||||
version = models.IntegerField(default=4) # network type, ipv4 or ipv6
|
||||
net_string = models.CharField(max_length=240, default='')
|
||||
transports = models.ManyToManyField(
|
||||
Transport, related_name='networks', db_table='uds_net_trans'
|
||||
)
|
||||
@ -73,29 +80,87 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
||||
ordering = ('name',)
|
||||
app_label = 'uds'
|
||||
|
||||
@staticmethod
|
||||
def _hexlify(number: int) -> str:
|
||||
"""
|
||||
Converts a number to hex, but with 32 chars, and with leading zeros
|
||||
"""
|
||||
return '{:032x}'.format(number)
|
||||
|
||||
@staticmethod
|
||||
def _unhexlify(number: str) -> int:
|
||||
"""
|
||||
Converts a hex string to a number
|
||||
"""
|
||||
return int(number, 16)
|
||||
|
||||
@staticmethod
|
||||
def networksFor(ip: str) -> typing.Iterable['Network']:
|
||||
"""
|
||||
Returns the networks that are valid for specified ip in dotted quad (xxx.xxx.xxx.xxx)
|
||||
"""
|
||||
ipInt = net.ipToLong(ip)
|
||||
return Network.objects.filter(net_start__lte=ipInt, net_end__gte=ipInt)
|
||||
ipInt, version = net.ipToLong(ip)
|
||||
hex_value = Network._hexlify(ipInt)
|
||||
# hexlify is used to convert to hex, and then decode to convert to string
|
||||
return Network.objects.filter(
|
||||
version=version,
|
||||
start__lte=hex_value,
|
||||
end__gte=hex_value,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def create(name: str, netRange: str) -> 'Network':
|
||||
"""
|
||||
Creates an network record, with the specified net start and net end (dotted quad)
|
||||
Creates an network record, with the specified network range. Supports IPv4 and IPv6
|
||||
IPV4 has a versatile format, that can be:
|
||||
- A single IP
|
||||
- A range of IPs, in the form of "startIP - endIP"
|
||||
- A network, in the form of "network/mask"
|
||||
- A network, in the form of "network netmask mask"
|
||||
- A network, in the form of "network*'
|
||||
|
||||
Args:
|
||||
netStart: Network start
|
||||
name: Name of the network
|
||||
netRange: Network range in any supported format
|
||||
|
||||
netEnd: Network end
|
||||
"""
|
||||
nr = net.networkFromString(netRange)
|
||||
return Network.objects.create(
|
||||
name=name, net_start=nr[0], net_end=nr[1], net_string=netRange
|
||||
name=name,
|
||||
start=Network._hexlify(nr.start),
|
||||
end=Network._hexlify(nr.end),
|
||||
net_string=netRange,
|
||||
version=nr.version,
|
||||
)
|
||||
|
||||
@property
|
||||
def net_start(self) -> int:
|
||||
"""
|
||||
Returns the network start as an integer
|
||||
"""
|
||||
return Network._unhexlify(self.start)
|
||||
|
||||
@net_start.setter
|
||||
def net_start(self, value: int) -> None:
|
||||
"""
|
||||
Sets the network start
|
||||
"""
|
||||
self.start = Network._hexlify(value)
|
||||
|
||||
@property
|
||||
def net_end(self) -> int:
|
||||
"""
|
||||
Returns the network end as an integer
|
||||
"""
|
||||
return Network._unhexlify(self.end)
|
||||
|
||||
@net_end.setter
|
||||
def net_end(self, value: int) -> None:
|
||||
"""
|
||||
Sets the network end
|
||||
"""
|
||||
self.end = Network._hexlify(value)
|
||||
|
||||
@property
|
||||
def netStart(self) -> str:
|
||||
"""
|
||||
@ -120,7 +185,21 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
||||
"""
|
||||
Returns true if the specified ip is in this network
|
||||
"""
|
||||
return net.ipToLong(ip) >= self.net_start and net.ipToLong(ip) <= self.net_end
|
||||
# if net_string is '*', then we are in all networks, return true
|
||||
if self.net_string == '*':
|
||||
return True
|
||||
ipInt, version = net.ipToLong(ip)
|
||||
return self.net_start <= ipInt <= self.net_end and self.version == version
|
||||
|
||||
def save(self, *args, **kwargs) -> None:
|
||||
"""
|
||||
Overrides save to update the start, end and version fields
|
||||
"""
|
||||
rng = net.networkFromString(self.net_string)
|
||||
self.start = Network._hexlify(rng.start)
|
||||
self.end = Network._hexlify(rng.end)
|
||||
self.version = rng.version
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def update(self, name: str, netRange: str):
|
||||
"""
|
||||
@ -134,18 +213,16 @@ class Network(UUIDModel, TaggingMixin): # type: ignore
|
||||
netEnd: new Network end (quad dotted)
|
||||
"""
|
||||
self.name = name
|
||||
nr = net.networkFromString(netRange)
|
||||
self.net_start = nr[0]
|
||||
self.net_end = nr[1]
|
||||
self.net_string = netRange
|
||||
self.save()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return u'Network {} ({}) from {} to {}'.format(
|
||||
return u'Network {} ({}) from {} to {} ({})'.format(
|
||||
self.name,
|
||||
self.net_string,
|
||||
net.longToIp(self.net_start),
|
||||
net.longToIp(self.net_end),
|
||||
self.version,
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
|
@ -39,7 +39,7 @@ from uds.core.util.state import State
|
||||
from uds.core.environment import Environment
|
||||
from uds.core import jobs
|
||||
|
||||
from .util import NEVER
|
||||
from .util import NEVER, MAX_DNS_NAME_LENGTH
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -67,7 +67,7 @@ class Scheduler(models.Model):
|
||||
frecuency = models.PositiveIntegerField(default=DAY)
|
||||
last_execution = models.DateTimeField(db_index=True)
|
||||
next_execution = models.DateTimeField(default=NEVER, db_index=True)
|
||||
owner_server = models.CharField(max_length=64, db_index=True, default='')
|
||||
owner_server = models.CharField(max_length=MAX_DNS_NAME_LENGTH, db_index=True, default='')
|
||||
state = models.CharField(max_length=1, default=State.FOR_EXECUTE, db_index=True)
|
||||
|
||||
# primary key id declaration (for type checking)
|
||||
|
@ -124,10 +124,10 @@ class Transport(ManagedObjectModel, TaggingMixin):
|
||||
"""
|
||||
if self.net_filtering == Transport.NO_FILTERING:
|
||||
return True
|
||||
ip = net.ipToLong(ipStr)
|
||||
ip, version = net.ipToLong(ipStr)
|
||||
# Allow
|
||||
if self.net_filtering == Transport.ALLOW:
|
||||
return self.networks.filter(net_start__lte=ip, net_end__gte=ip).exists()
|
||||
return self.networks.filter(net_start__lte=ip, net_end__gte=ip, version=version).exists()
|
||||
# Deny, must not be in any network
|
||||
return self.networks.filter(net_start__lte=ip, net_end__gte=ip).exists() is False
|
||||
|
||||
|
@ -33,6 +33,8 @@ import typing
|
||||
from django.db import models
|
||||
from uds.core.util.request import ExtendedHttpRequest
|
||||
|
||||
from .util import MAX_DNS_NAME_LENGTH, MAX_IPV6_LENGTH
|
||||
|
||||
|
||||
class TunnelToken(models.Model):
|
||||
"""
|
||||
@ -40,9 +42,9 @@ class TunnelToken(models.Model):
|
||||
"""
|
||||
|
||||
username = models.CharField(max_length=128)
|
||||
ip_from = models.CharField(max_length=128)
|
||||
ip = models.CharField(max_length=128)
|
||||
hostname = models.CharField(max_length=128)
|
||||
ip_from = models.CharField(max_length=MAX_IPV6_LENGTH)
|
||||
ip = models.CharField(max_length=MAX_IPV6_LENGTH)
|
||||
hostname = models.CharField(max_length=MAX_DNS_NAME_LENGTH)
|
||||
|
||||
token = models.CharField(max_length=48, db_index=True, unique=True)
|
||||
stamp = models.DateTimeField() # Date creation or validation of this entry
|
||||
|
@ -1,62 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
#
|
||||
# Copyright (c) 2012-2020 Virtual Cable S.L.U.
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without modification,
|
||||
# are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
# * Neither the name of Virtual Cable S.L. nor the names of its contributors
|
||||
# may be used to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import logging
|
||||
|
||||
from django.db import models
|
||||
|
||||
from .user import User
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class UserPreference(models.Model):
|
||||
"""
|
||||
This class represents a single user preference for an user and a module
|
||||
"""
|
||||
|
||||
module = models.CharField(max_length=32, db_index=True)
|
||||
name = models.CharField(max_length=32, db_index=True)
|
||||
value = models.CharField(max_length=128, db_index=True)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='preferences')
|
||||
|
||||
# "fake" declarations for type checking
|
||||
# objects: 'models.manager.Manager[UserPreference]'
|
||||
|
||||
class Meta:
|
||||
app_label = 'uds'
|
||||
|
||||
def __str__(self) -> str:
|
||||
return '{}.{} = "{}" for user {}'.format(
|
||||
self.module, self.name, self.value, self.user
|
||||
)
|
@ -36,7 +36,6 @@ import typing
|
||||
from django.db import models
|
||||
from django.db.models import signals
|
||||
|
||||
from uds.core.managers import cryptoManager
|
||||
from uds.core.environment import Environment
|
||||
from uds.core.util import log, unique
|
||||
from uds.core.util.state import State
|
||||
@ -45,8 +44,7 @@ from uds.models.uuid_model import UUIDModel
|
||||
from uds.models.service_pool import ServicePool
|
||||
from uds.models.service_pool_publication import ServicePoolPublication
|
||||
from uds.models.user import User
|
||||
from uds.models.util import NEVER
|
||||
from uds.models.util import getSqlDatetime
|
||||
from uds.models.util import NEVER, getSqlDatetime, MAX_IPV6_LENGTH, MAX_DNS_NAME_LENGTH
|
||||
|
||||
# Not imported at runtime, just for type checking
|
||||
if typing.TYPE_CHECKING:
|
||||
@ -113,8 +111,8 @@ class UserService(UUIDModel): # pylint: disable=too-many-public-methods
|
||||
db_index=True, default=0
|
||||
) # Cache level must be 1 for L1 or 2 for L2, 0 if it is not cached service
|
||||
|
||||
src_hostname = models.CharField(max_length=64, default='')
|
||||
src_ip = models.CharField(max_length=128, default='')
|
||||
src_hostname = models.CharField(max_length=MAX_DNS_NAME_LENGTH, default='')
|
||||
src_ip = models.CharField(max_length=MAX_IPV6_LENGTH, default='') # Source IP of the user connecting to the service. Max length is 45 chars (ipv6)
|
||||
|
||||
# "fake" declarations for type checking
|
||||
# objects: 'models.manager.Manager["UserService"]'
|
||||
|
@ -31,6 +31,7 @@
|
||||
.. moduleauthor:: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import logging
|
||||
import typing
|
||||
from datetime import datetime
|
||||
from time import mktime
|
||||
|
||||
@ -38,9 +39,12 @@ from django.db import connection, models
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
NEVER = datetime(1972, 7, 1)
|
||||
NEVER_UNIX = int(mktime(NEVER.timetuple()))
|
||||
NEVER: typing.Final[datetime] = datetime(1972, 7, 1)
|
||||
NEVER_UNIX: typing.Final[int] = int(mktime(NEVER.timetuple()))
|
||||
|
||||
# Max ip v6 string length representation, allowing ipv4 mapped addresses
|
||||
MAX_IPV6_LENGTH: typing.Final = 45
|
||||
MAX_DNS_NAME_LENGTH: typing.Final = 255
|
||||
|
||||
class UnsavedForeignKey(models.ForeignKey):
|
||||
"""
|
||||
|
@ -75,15 +75,21 @@ class IPMachineDeployed(services.UserDeployment, AutoAttributes):
|
||||
# If multiple and has a ';' on IP, the values is IP;MAC
|
||||
ip = self._ip.split('~')[0].split(';')[0]
|
||||
# If ip is in fact a hostname...
|
||||
if not net.ipToLong(ip):
|
||||
if not net.ipToLong(ip).version:
|
||||
# Try to resolve name...
|
||||
try:
|
||||
# Prefer ipv4 first
|
||||
res = dns.resolver.resolve(ip)
|
||||
ip = res[0].address
|
||||
except Exception:
|
||||
self.service().parent().doLog(
|
||||
log.WARN, f'User service could not resolve Name {ip}.'
|
||||
)
|
||||
# If not found, try ipv6
|
||||
try:
|
||||
res = dns.resolver.resolve(ip, 'AAAA')
|
||||
ip = res[0].address
|
||||
except Exception as e:
|
||||
self.service().parent().doLog(
|
||||
log.WARN, f'User service could not resolve Name {ip} ({e}).'
|
||||
)
|
||||
|
||||
return ip
|
||||
|
||||
|
@ -132,17 +132,22 @@ class PhysicalMachinesProvider(services.ServiceProvider):
|
||||
return ''
|
||||
|
||||
# If ip is in fact a hostname...
|
||||
if not net.ipToLong(ip):
|
||||
if not net.ipToLong(ip).version:
|
||||
# Try to resolve name...
|
||||
try:
|
||||
# Prefer ipv4
|
||||
res = dns.resolver.resolve(ip)
|
||||
ip = res[0].address
|
||||
except Exception:
|
||||
self.doLog(log.WARN, f'Name {ip} could not be resolved')
|
||||
logger.warning('Name %s could not be resolved', ip)
|
||||
return ''
|
||||
# Try ipv6
|
||||
try:
|
||||
res = dns.resolver.resolve(ip, 'AAAA')
|
||||
ip = res[0].address
|
||||
except Exception as e:
|
||||
self.doLog(log.WARN, f'Name {ip} could not be resolved')
|
||||
logger.warning('Name %s could not be resolved (%s)', ip, e)
|
||||
return ''
|
||||
|
||||
url = ''
|
||||
try:
|
||||
config = configparser.ConfigParser()
|
||||
config.read_string(self.config.value)
|
||||
|
@ -71,13 +71,13 @@ class IPServiceBase(services.Service):
|
||||
def unassignMachine(self, ip: str) -> None:
|
||||
raise NotADirectoryError('unassignMachine')
|
||||
|
||||
def wakeup(self, ip: str, mac: typing.Optional[str]) -> None:
|
||||
def wakeup(self, ip: str, mac: typing.Optional[str], verify_ssl: bool = False) -> None:
|
||||
if mac:
|
||||
wolurl = self.parent().wolURL(ip, mac)
|
||||
if wolurl:
|
||||
logger.info('Launching WOL: %s', wolurl)
|
||||
try:
|
||||
requests.get(wolurl, verify=False)
|
||||
requests.get(wolurl, verify=verify_ssl)
|
||||
# logger.debug('Result: %s', result)
|
||||
except Exception as e:
|
||||
logger.error('Error on WOL: %s', e)
|
||||
|
@ -28,9 +28,9 @@
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
"""
|
||||
@author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
Author: Adolfo Gómez, dkmaster at dkmon dot com
|
||||
"""
|
||||
import pickle
|
||||
import pickle # nosec # Pickle use is controled by app, never by non admin user input
|
||||
import logging
|
||||
import typing
|
||||
|
||||
@ -163,7 +163,7 @@ class IPMachinesService(IPServiceBase):
|
||||
] # Allow duplicates right now
|
||||
# Current stored data, if it exists
|
||||
d = self.storage.readData('ips')
|
||||
old_ips = pickle.loads(d) if d and isinstance(d, bytes) else []
|
||||
old_ips = pickle.loads(d) if d and isinstance(d, bytes) else [] # nosec: pickle is safe here
|
||||
# dissapeared ones
|
||||
dissapeared = set(
|
||||
IPServiceBase.getIp(i.split('~')[0]) for i in old_ips
|
||||
@ -210,9 +210,9 @@ class IPMachinesService(IPServiceBase):
|
||||
values: typing.List[bytes] = data.split(b'\0')
|
||||
d = self.storage.readData('ips')
|
||||
if isinstance(d, bytes):
|
||||
self._ips = pickle.loads(d)
|
||||
self._ips = pickle.loads(d) # nosec: pickle is safe here
|
||||
elif isinstance(d, str): # "legacy" saved elements
|
||||
self._ips = pickle.loads(d.encode('utf8'))
|
||||
self._ips = pickle.loads(d.encode('utf8')) # nosec: pickle is safe here
|
||||
self.marshal() # Ensure now is bytes..
|
||||
else:
|
||||
self._ips = []
|
||||
|
Loading…
Reference in New Issue
Block a user