Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
R
re6stnet
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
2
Issues
2
List
Boards
Labels
Milestones
Merge Requests
4
Merge Requests
4
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
re6stnet
Commits
d7a4d73f
Commit
d7a4d73f
authored
Jul 01, 2018
by
Julien Muchembled
1
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New option to prevent tunnelling accross borders of listed countries
parent
6b45d7ea
Changes
9
Hide whitespace changes
Inline
Side-by-side
Showing
9 changed files
with
147 additions
and
27 deletions
+147
-27
debian/control
debian/control
+1
-1
debian/init.d
debian/init.d
+3
-0
debian/rules
debian/rules
+1
-1
re6st/cache.py
re6st/cache.py
+41
-12
re6st/cli/registry.py
re6st/cli/registry.py
+3
-1
re6st/registry.py
re6st/registry.py
+6
-4
re6st/tunnel.py
re6st/tunnel.py
+88
-7
re6st/version.py
re6st/version.py
+1
-1
setup.py
setup.py
+3
-0
No files found.
debian/control
View file @
d7a4d73f
...
...
@@ -10,7 +10,7 @@ Package: re6stnet
Architecture: all
Depends: ${misc:Depends}, ${python:Depends}, python-pkg-resources, python-openssl (>= 0.13), openvpn (>= 2.3), babeld (= 1.6.2-nxd1), iproute2 | iproute, openssl
Recommends: ${python:Recommends}, logrotate
Suggests: ndisc6
Suggests:
${python:Suggests},
ndisc6
Conflicts: re6st-node
Replaces: re6st-node
Description: resilient, scalable, IPv6 network application
debian/init.d
View file @
d7a4d73f
...
...
@@ -31,6 +31,9 @@ SCRIPTNAME=/etc/init.d/$NAME
#
do_start
()
{
if
[
-
r
$
CONFDIR
/
GeoLite2
-
Country
.
mmdb
]
then
export
GEOIP2_MMDB
=$
CONFDIR
/
GeoLite2
-
Country
.
mmdb
fi
#
Return
#
0
if
daemon
has
been
started
#
1
if
daemon
was
already
running
...
...
debian/rules
View file @
d7a4d73f
...
...
@@ -15,7 +15,7 @@ include debian/common.mk
override_dh_python2
:
sed
-i
/^miniupnpc
$$
/d
`
find
$(TMP)
/usr
-name
requires.txt
`
dh_python2
--recommends
=
miniupnpc
dh_python2
--recommends
=
miniupnpc
--suggests
=
geoip2
override_dh_auto_clean
:
make clean
...
...
re6st/cache.py
View file @
d7a4d73f
import
json
,
logging
,
os
,
sqlite3
,
socket
,
subprocess
,
sys
,
time
,
zlib
from
itertools
import
chain
from
.registry
import
RegistryClient
from
.
import
utils
,
version
,
x509
class
Cache
(
object
):
crl
=
()
def
__init__
(
self
,
db_path
,
registry
,
cert
,
db_size
=
200
):
self
.
_prefix
=
cert
.
prefix
self
.
_db_size
=
db_size
...
...
@@ -25,7 +24,8 @@ class Cache(object):
peer TEXT PRIMARY KEY NOT NULL,
try INTEGER NOT NULL DEFAULT 0)"""
)
q
(
"CREATE INDEX volatile.stat_try ON stat(try)"
)
q
(
"INSERT INTO volatile.stat (peer) SELECT prefix FROM peer"
)
q
(
"INSERT INTO volatile.stat (peer)"
" SELECT prefix FROM peer WHERE prefix"
)
self
.
_db
.
commit
()
self
.
_loadConfig
(
self
.
_selectConfig
(
q
))
try
:
...
...
@@ -71,10 +71,16 @@ class Cache(object):
def
_loadConfig
(
self
,
config
):
cls
=
self
.
__class__
logging
.
debug
(
"Loading network parameters:"
)
self
.
crl
=
self
.
same_country
=
()
for
k
,
v
in
config
:
if
k
==
'crl'
:
v
=
set
(
json
.
loads
(
v
))
elif
hasattr
(
cls
,
k
):
if
k
==
'crl'
:
# BBB
k
=
'crl:json'
if
k
.
endswith
(
':json'
):
k
=
k
[:
-
5
]
v
=
json
.
loads
(
v
)
if
k
==
'crl'
:
v
=
set
(
v
)
if
hasattr
(
cls
,
k
):
continue
setattr
(
self
,
k
,
v
)
logging
.
debug
(
"- %s: %r"
,
k
,
v
)
...
...
@@ -83,13 +89,20 @@ class Cache(object):
logging
.
info
(
"Getting new network parameters from registry..."
)
try
:
# TODO: When possible, the registry should be queried via the re6st.
config
=
json
.
loads
(
zlib
.
decompress
(
x
=
json
.
loads
(
zlib
.
decompress
(
self
.
_registry
.
getNetworkConfig
(
self
.
_prefix
)))
base64
=
config
.
pop
(
''
,
())
config
=
dict
((
str
(
k
),
v
.
decode
(
'base64'
)
if
k
in
base64
else
str
(
v
)
if
type
(
v
)
is
unicode
else
v
)
for
k
,
v
in
config
.
iteritems
())
config
[
'crl'
]
=
json
.
dumps
(
config
[
'crl'
])
base64
=
x
.
pop
(
''
,
())
config
=
{}
for
k
,
v
in
x
.
iteritems
():
k
=
str
(
k
)
if
k
in
base64
:
v
=
v
.
decode
(
'base64'
)
elif
type
(
v
)
is
unicode
:
v
=
str
(
v
)
elif
isinstance
(
v
,
(
list
,
dict
)):
k
+=
':json'
v
=
json
.
dumps
(
v
)
config
[
k
]
=
v
except
socket
.
error
,
e
:
logging
.
warning
(
e
)
return
...
...
@@ -186,6 +199,22 @@ class Cache(object):
(
prefix
,)).
fetchone
()
return
r
and
r
[
0
]
@
property
def
my_address
(
self
):
for
x
,
in
self
.
_db
.
execute
(
"SELECT address FROM peer WHERE NOT prefix"
):
return
x
return
''
@
my_address
.
setter
def
my_address
(
self
,
*
args
):
with
self
.
_db
as
db
:
db
.
execute
(
"INSERT OR REPLACE INTO peer VALUES ('', ?)"
,
args
)
@
my_address
.
deleter
def
my_address
(
self
):
with
self
.
_db
as
db
:
db
.
execute
(
"DELETE FROM peer WHERE NOT prefix"
)
# Exclude our own address from results in case it is there, which may
# happen if a node change its certificate without clearing the cache.
# IOW, one should probably always put our own address there.
...
...
re6st/cli/registry.py
View file @
d7a4d73f
...
...
@@ -5,7 +5,7 @@ from SocketServer import ThreadingTCPServer
from
urlparse
import
parse_qsl
if
're6st'
not
in
sys
.
modules
:
sys
.
path
[
0
]
=
os
.
path
.
dirname
(
os
.
path
.
dirname
(
sys
.
path
[
0
]))
from
re6st
import
ctl
,
registry
,
utils
,
version
from
re6st
import
registry
,
utils
,
version
# To generate server ca and key with serial for 2001:db8:42::/48
# openssl req -nodes -new -x509 -key ca.key -set_serial 0x120010db80042 -days 3650 -out ca.crt
...
...
@@ -132,6 +132,8 @@ def main():
help
=
"Interval in seconds between two tunnel refresh: the worst"
" tunnel is closed if the number of client tunnels has reached"
" its maximum number (client-count)."
)
_
(
'--same-country'
,
action
=
'append'
,
metavar
=
"CODE"
,
help
=
"prevent tunnelling accross borders of listed countries"
)
config
=
parser
.
parse_args
()
...
...
re6st/registry.py
View file @
d7a4d73f
...
...
@@ -76,11 +76,11 @@ class RegistryServer(object):
"email TEXT NOT NULL"
,
"prefix_len INTEGER NOT NULL"
,
"date INTEGER NOT NULL"
)
if
utils
.
sqliteCreateTable
(
self
.
db
,
"cert"
,
utils
.
sqliteCreateTable
(
self
.
db
,
"cert"
,
"prefix TEXT PRIMARY KEY NOT NULL"
,
"email TEXT"
,
"cert TEXT"
)
:
self
.
db
.
execute
(
"INSERT
INTO cert VALUES ('',null,null)"
)
"cert TEXT"
)
self
.
db
.
execute
(
"INSERT OR IGNORE
INTO cert VALUES ('',null,null)"
)
utils
.
sqliteCreateTable
(
self
.
db
,
"crl"
,
"serial INTEGER PRIMARY KEY NOT NULL"
,
# Expiration date of revoked certificate.
...
...
@@ -122,6 +122,8 @@ class RegistryServer(object):
}
if
self
.
config
.
ipv4
:
kw
[
'ipv4'
],
kw
[
'ipv4_sublen'
]
=
self
.
config
.
ipv4
if
self
.
config
.
same_country
:
kw
[
'same_country'
]
=
self
.
config
.
same_country
for
x
in
(
'client_count'
,
'encrypt'
,
'hello'
,
'max_clients'
,
'min_protocol'
,
'tunnel_refresh'
):
kw
[
x
]
=
getattr
(
self
.
config
,
x
)
...
...
@@ -138,7 +140,7 @@ class RegistryServer(object):
# Example to avoid all nodes to restart at the same time:
# kw['delay_restart'] = 600 * random.random()
kw
[
'version'
]
=
self
.
version
.
encode
(
'base64'
)
self
.
network_config
=
zlib
.
compress
(
json
.
dumps
(
kw
))
self
.
network_config
=
zlib
.
compress
(
json
.
dumps
(
kw
)
,
9
)
# The 3 first bits code the number of bytes.
def
encodeVersion
(
self
,
version
):
...
...
re6st/tunnel.py
View file @
d7a4d73f
import
errno
,
json
,
logging
,
os
,
random
import
s
ocket
,
subprocess
,
struct
,
time
,
weakref
import
errno
,
json
,
logging
,
os
,
random
,
socket
import
s
ubprocess
,
struct
,
sys
,
time
,
weakref
from
collections
import
defaultdict
,
deque
from
bisect
import
bisect
,
insort
from
OpenSSL
import
crypto
...
...
@@ -7,6 +7,27 @@ from . import ctl, plib, rina, utils, version, x509
PORT
=
326
family_dict
=
{
socket
.
AF_INET
:
'IPv4'
,
socket
.
AF_INET6
:
'IPv6'
,
}
proto_dict
=
{
'tcp4'
:
(
socket
.
AF_INET
,
socket
.
SOL_TCP
),
'udp4'
:
(
socket
.
AF_INET
,
socket
.
SOL_UDP
),
'tcp6'
:
(
socket
.
AF_INET6
,
socket
.
SOL_TCP
),
'udp6'
:
(
socket
.
AF_INET6
,
socket
.
SOL_UDP
),
}
proto_dict
[
'tcp'
]
=
proto_dict
[
'tcp4'
]
proto_dict
[
'udp'
]
=
proto_dict
[
'udp4'
]
def
resolve
(
ip
,
port
,
proto
):
try
:
family
,
proto
=
proto_dict
[
proto
]
except
KeyError
:
return
None
,
()
return
family
,
(
x
[
-
1
][
0
]
for
x
in
socket
.
getaddrinfo
(
ip
,
port
,
family
,
0
,
proto
))
class
MultiGatewayManager
(
dict
):
...
...
@@ -172,6 +193,7 @@ class BaseTunnelManager(object):
NEED_RESTART
=
frozenset
((
'babel_default'
,
'encrypt'
,
'hello'
,
'ipv4'
,
'ipv4_sublen'
))
_geoiplookup
=
None
_forward
=
None
_next_rina
=
True
...
...
@@ -188,6 +210,31 @@ class BaseTunnelManager(object):
address_dict
=
defaultdict
(
list
)
for
family
,
address
in
address
:
address_dict
[
family
]
+=
address
if
any
(
address_dict
.
itervalues
()):
del
cache
.
my_address
else
:
for
address
in
utils
.
parse_address
(
cache
.
my_address
):
try
:
proto
=
proto_dict
[
address
[
2
]]
except
KeyError
:
continue
address_dict
[
proto
[
0
]].
append
(
address
)
db
=
os
.
getenv
(
'GEOIP2_MMDB'
)
if
db
:
from
geoip2
import
database
,
errors
country
=
database
.
Reader
(
db
).
country
def
geoiplookup
(
ip
):
try
:
return
country
(
ip
).
country
.
iso_code
except
errors
.
AddressNotFoundError
:
return
self
.
_geoiplookup
=
geoiplookup
self
.
_country
=
{}
for
address
in
address_dict
.
itervalues
():
self
.
_updateCountry
(
address
)
elif
cache
.
same_country
:
sys
.
exit
(
"Can not respect 'same_country' network configuration"
" (GEOIP2_MMDB not set)"
)
self
.
_address
=
dict
((
family
,
utils
.
dump_address
(
address
))
for
family
,
address
in
address_dict
.
iteritems
()
if
address
)
...
...
@@ -589,11 +636,23 @@ class BaseTunnelManager(object):
'
\
7
%s (%s)'
%
(
msg
,
os
.
uname
()[
2
]))
break
def
_updateCountry
(
self
,
address
):
for
address
in
address
:
family
,
ip
=
resolve
(
*
address
)
for
ip
in
ip
:
country
=
self
.
_geoiplookup
(
ip
)
if
country
:
if
self
.
_country
.
get
(
family
)
!=
country
:
self
.
_country
[
family
]
=
country
logging
.
info
(
'%s country: %s'
,
family_dict
[
family
],
country
)
return
class
TunnelManager
(
BaseTunnelManager
):
NEED_RESTART
=
BaseTunnelManager
.
NEED_RESTART
.
union
((
'client_count'
,
'max_clients'
,
'tunnel_refresh'
))
'client_count'
,
'max_clients'
,
'
same_country'
,
'
tunnel_refresh'
))
def
__init__
(
self
,
control_socket
,
cache
,
cert
,
openvpn_args
,
timeout
,
client_count
,
iface_list
,
address
,
ip_changed
,
...
...
@@ -780,16 +839,35 @@ class TunnelManager(BaseTunnelManager):
if
prefix
in
self
.
_served
or
prefix
in
self
.
_connection_dict
:
return
False
assert
prefix
!=
self
.
_prefix
,
self
.
__dict__
address
=
[
x
for
x
in
utils
.
parse_address
(
address
)
if
x
[
2
]
not
in
self
.
_disable_proto
]
address_list
=
[]
same_country
=
self
.
cache
.
same_country
for
x
in
utils
.
parse_address
(
address
):
if
x
[
2
]
in
self
.
_disable_proto
:
continue
if
same_country
:
family
,
ip
=
resolve
(
*
x
)
my_country
=
self
.
_country
.
get
(
family
)
if
my_country
:
for
ip
in
ip
:
country
=
self
.
_geoiplookup
(
ip
)
if
country
and
(
country
!=
my_country
if
my_country
in
same_country
else
country
in
same_country
):
logging
.
debug
(
'Do not tunnel to %s (%s -> %s)'
,
ip
,
my_country
,
country
)
else
:
address_list
.
append
((
ip
,
x
[
1
],
x
[
2
]))
continue
address_list
.
append
(
x
)
self
.
cache
.
connecting
(
prefix
,
1
)
if
not
address
:
if
not
address
_list
:
return
False
logging
.
info
(
'Establishing a connection with %u/%u'
,
int
(
prefix
,
2
),
len
(
prefix
))
with
utils
.
exit
:
iface
=
self
.
_getFreeInterface
(
prefix
)
self
.
_connection_dict
[
prefix
]
=
c
=
Connection
(
self
,
address
,
iface
,
prefix
)
self
.
_connection_dict
[
prefix
]
=
c
=
Connection
(
self
,
address_list
,
iface
,
prefix
)
if
self
.
_gateway_manager
is
not
None
:
for
ip
in
c
:
self
.
_gateway_manager
.
add
(
ip
,
True
)
...
...
@@ -917,6 +995,9 @@ class TunnelManager(BaseTunnelManager):
family
,
address
=
self
.
_ip_changed
(
ip
)
if
address
:
self
.
_address
[
family
]
=
utils
.
dump_address
(
address
)
if
self
.
_geoiplookup
:
self
.
_updateCountry
(
address
)
self
.
cache
.
my_address
=
';'
.
join
(
self
.
_address
.
itervalues
())
def
broadcastNewVersion
(
self
):
self
.
_babel_dump_new_version
()
...
...
re6st/version.py
View file @
d7a4d73f
...
...
@@ -32,7 +32,7 @@ if dirty:
# they are intended to the network admin.
# Only 'protocol' is important and it must be increased whenever they would be
# a wish to force an update of nodes.
protocol
=
3
protocol
=
4
min_protocol
=
1
if
__name__
==
"__main__"
:
...
...
setup.py
View file @
d7a4d73f
...
...
@@ -93,6 +93,9 @@ setup(
# BBB: use MANIFEST.in only so that egg_info works with very old setuptools
include_package_data
=
True
,
install_requires
=
[
'pyOpenSSL >= 0.13'
,
'miniupnpc'
],
extras_require
=
{
'geoip'
:
[
'geoip2'
],
},
#dependency_links = [
# "http://miniupnp.free.fr/files/download.php?file=miniupnpc-1.7.20120714.tar.gz#egg=miniupnpc-1.7",
# ],
...
...
Julien Muchembled
@jm
mentioned in commit
85045501
·
Jun 12, 2019
mentioned in commit
85045501
mentioned in commit 85045501add59ef13953066c5445dac5e453d1ad
Toggle commit list
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment