mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-02-04 08:13:38 +00:00
i2p: Add helper to modify the tunnel config
We will want to set the 'interface' property of certain tunnels to 0.0.0.0 and the handle the rest with the firewall. This is just prep to do so. Reviewed-by: Sunil Mohan Adapa <sunil@medhas.org>
This commit is contained in:
parent
0018e14cab
commit
eb28f47053
119
plinth/modules/i2p/helpers.py
Normal file
119
plinth/modules/i2p/helpers.py
Normal file
@ -0,0 +1,119 @@
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import os
|
||||
import re
|
||||
|
||||
import augeas
|
||||
|
||||
I2P_CONF_DIR = '/var/lib/i2p/i2p-config'
|
||||
FILE_TUNNEL_CONF = os.path.join(I2P_CONF_DIR, 'i2ptunnel.config')
|
||||
TUNNEL_IDX_REGEX = re.compile(r'tunnel.(\d+).name$')
|
||||
|
||||
|
||||
class TunnelEditor(object):
|
||||
"""
|
||||
|
||||
:type aug: augeas.Augeas
|
||||
"""
|
||||
|
||||
def __init__(self, conf_filename=None, idx=None):
|
||||
self.conf_filename = conf_filename or FILE_TUNNEL_CONF
|
||||
self.idx = idx
|
||||
self.aug = None
|
||||
|
||||
@property
|
||||
def lines(self):
|
||||
if self.aug:
|
||||
return self.aug.match('/files{}/*'.format(self.conf_filename))
|
||||
else:
|
||||
return []
|
||||
|
||||
def read_conf(self):
|
||||
"""Return an instance of Augeaus for processing APT configuration."""
|
||||
self.aug = augeas.Augeas(flags=augeas.Augeas.NO_LOAD +
|
||||
augeas.Augeas.NO_MODL_AUTOLOAD)
|
||||
self.aug.set('/augeas/load/Properties/lens', 'Properties.lns')
|
||||
self.aug.set('/augeas/load/Properties/incl[last() + 1]', self.conf_filename)
|
||||
self.aug.load()
|
||||
|
||||
return self
|
||||
|
||||
def write_conf(self):
|
||||
self.aug.save()
|
||||
return self
|
||||
|
||||
def set_tunnel_idx(self, name):
|
||||
|
||||
"""
|
||||
Finds the index of the tunnel with the given name.
|
||||
|
||||
Chainable method.
|
||||
|
||||
:type name: basestring
|
||||
"""
|
||||
|
||||
for prop in self.aug.match('/files{}/*'.format(self.conf_filename)):
|
||||
match = TUNNEL_IDX_REGEX.search(prop)
|
||||
|
||||
if match and self.aug.get(prop) == name:
|
||||
self.idx = int(match.group(1))
|
||||
return self
|
||||
raise ValueError('No tunnel with that name')
|
||||
|
||||
def calc_prop_path(self, tunnel_prop):
|
||||
"""
|
||||
Calculates the property name as found in the properties files
|
||||
:type tunnel_prop: str
|
||||
:rtype: basestring
|
||||
"""
|
||||
calced_prop_path = '/files{filepath}/tunnel.{idx}.{tunnel_prop}'.format(
|
||||
idx=self.idx, tunnel_prop=tunnel_prop,
|
||||
filepath=self.conf_filename
|
||||
)
|
||||
return calced_prop_path
|
||||
|
||||
def set_tunnel_prop(self, tunnel_prop, value):
|
||||
"""
|
||||
Updates a tunnel's property.
|
||||
|
||||
The idx has to be set and the property has to exist in the config file and belong to the tunnel's properties.
|
||||
|
||||
see calc_prop_path
|
||||
|
||||
Chainable method.
|
||||
|
||||
:param tunnel_prop:
|
||||
:type tunnel_prop: str
|
||||
:param value:
|
||||
:type value: basestring | int
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
if self.idx is None:
|
||||
raise ValueError('Please init the tunnel index before calling this method')
|
||||
|
||||
calc_prop_path = self.calc_prop_path(tunnel_prop)
|
||||
self.aug.set(calc_prop_path, value)
|
||||
return self
|
||||
|
||||
def __getitem__(self, tunnel_prop):
|
||||
ret = self.aug.get(self.calc_prop_path(tunnel_prop))
|
||||
if ret is None:
|
||||
raise KeyError('Unknown property {}'.format(tunnel_prop))
|
||||
return ret
|
||||
|
||||
def __setitem__(self, tunnel_prop, value):
|
||||
self.aug.set(self.calc_prop_path(tunnel_prop), value)
|
||||
16
plinth/modules/i2p/tests/__init__.py
Normal file
16
plinth/modules/i2p/tests/__init__.py
Normal file
@ -0,0 +1,16 @@
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
192
plinth/modules/i2p/tests/data/i2ptunnel.config
Normal file
192
plinth/modules/i2p/tests/data/i2ptunnel.config
Normal file
@ -0,0 +1,192 @@
|
||||
# NOTE: This I2P config file must use UTF-8 encoding
|
||||
tunnel.0.description=HTTP proxy for browsing eepsites and the web
|
||||
tunnel.0.interface=127.0.0.1
|
||||
tunnel.0.listenPort=4444
|
||||
tunnel.0.name=I2P HTTP Proxy
|
||||
tunnel.0.option.i2cp.closeIdleTime=1800000
|
||||
tunnel.0.option.i2cp.closeOnIdle=false
|
||||
tunnel.0.option.i2cp.delayOpen=false
|
||||
tunnel.0.option.i2cp.destination.sigType=EdDSA_SHA512_Ed25519
|
||||
tunnel.0.option.i2cp.newDestOnResume=false
|
||||
tunnel.0.option.i2cp.reduceIdleTime=900000
|
||||
tunnel.0.option.i2cp.reduceOnIdle=true
|
||||
tunnel.0.option.i2cp.reduceQuantity=1
|
||||
tunnel.0.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.0.option.i2ptunnel.httpclient.SSLOutproxies=false.i2p
|
||||
tunnel.0.option.i2ptunnel.httpclient.allowInternalSSL=true
|
||||
tunnel.0.option.i2ptunnel.httpclient.jumpServers=http://stats.i2p/cgi-bin/jump.cgi?a=,http://no.i2p/jump/,http://i2pjump.i2p/jump/
|
||||
tunnel.0.option.i2ptunnel.httpclient.sendAccept=false
|
||||
tunnel.0.option.i2ptunnel.httpclient.sendReferer=false
|
||||
tunnel.0.option.i2ptunnel.httpclient.sendUserAgent=false
|
||||
tunnel.0.option.i2ptunnel.useLocalOutproxy=false
|
||||
tunnel.0.option.inbound.backupQuantity=0
|
||||
tunnel.0.option.inbound.length=3
|
||||
tunnel.0.option.inbound.lengthVariance=0
|
||||
tunnel.0.option.inbound.nickname=shared clients
|
||||
tunnel.0.option.inbound.quantity=2
|
||||
tunnel.0.option.outbound.backupQuantity=0
|
||||
tunnel.0.option.outbound.length=3
|
||||
tunnel.0.option.outbound.lengthVariance=0
|
||||
tunnel.0.option.outbound.nickname=shared clients
|
||||
tunnel.0.option.outbound.priority=10
|
||||
tunnel.0.option.outbound.quantity=2
|
||||
tunnel.0.option.outproxyAuth=false
|
||||
tunnel.0.option.persistentClientKey=false
|
||||
tunnel.0.option.sslManuallySet=true
|
||||
tunnel.0.option.useSSL=false
|
||||
tunnel.0.proxyList=false.i2p
|
||||
tunnel.0.sharedClient=true
|
||||
tunnel.0.startOnLoad=true
|
||||
tunnel.0.type=httpclient
|
||||
tunnel.1.description=IRC tunnel to access the Irc2P network
|
||||
tunnel.1.i2cpHost=127.0.0.1
|
||||
tunnel.1.i2cpPort=7654
|
||||
tunnel.1.interface=127.0.0.1
|
||||
tunnel.1.listenPort=6668
|
||||
tunnel.1.name=Irc2P
|
||||
tunnel.1.option.crypto.lowTagThreshold=14
|
||||
tunnel.1.option.crypto.tagsToSend=20
|
||||
tunnel.1.option.i2cp.closeIdleTime=1200000
|
||||
tunnel.1.option.i2cp.closeOnIdle=true
|
||||
tunnel.1.option.i2cp.delayOpen=true
|
||||
tunnel.1.option.i2cp.destination.sigType=ECDSA_SHA256_P256
|
||||
tunnel.1.option.i2cp.newDestOnResume=false
|
||||
tunnel.1.option.i2cp.reduceIdleTime=600000
|
||||
tunnel.1.option.i2cp.reduceOnIdle=true
|
||||
tunnel.1.option.i2cp.reduceQuantity=1
|
||||
tunnel.1.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.1.option.i2p.streaming.maxWindowSize=16
|
||||
tunnel.1.option.inbound.length=3
|
||||
tunnel.1.option.inbound.lengthVariance=0
|
||||
tunnel.1.option.inbound.nickname=Irc2P
|
||||
tunnel.1.option.outbound.length=3
|
||||
tunnel.1.option.outbound.lengthVariance=0
|
||||
tunnel.1.option.outbound.nickname=Irc2P
|
||||
tunnel.1.option.outbound.priority=15
|
||||
tunnel.1.sharedClient=false
|
||||
tunnel.1.startOnLoad=true
|
||||
tunnel.1.targetDestination=irc.00.i2p:6667,irc.postman.i2p:6667,irc.echelon.i2p:6667
|
||||
tunnel.1.type=ircclient
|
||||
tunnel.2.description=I2P Monotone Server
|
||||
tunnel.2.i2cpHost=127.0.0.1
|
||||
tunnel.2.i2cpPort=7654
|
||||
tunnel.2.interface=127.0.0.1
|
||||
tunnel.2.listenPort=8998
|
||||
tunnel.2.name=mtn.i2p-projekt.i2p
|
||||
tunnel.2.option.i2cp.destination.sigType=EdDSA_SHA512_Ed25519
|
||||
tunnel.2.option.i2cp.reduceIdleTime=900000
|
||||
tunnel.2.option.i2cp.reduceOnIdle=true
|
||||
tunnel.2.option.i2cp.reduceQuantity=1
|
||||
tunnel.2.option.inbound.backupQuantity=0
|
||||
tunnel.2.option.inbound.length=3
|
||||
tunnel.2.option.inbound.lengthVariance=0
|
||||
tunnel.2.option.inbound.nickname=shared clients
|
||||
tunnel.2.option.inbound.quantity=2
|
||||
tunnel.2.option.outbound.backupQuantity=0
|
||||
tunnel.2.option.outbound.length=3
|
||||
tunnel.2.option.outbound.lengthVariance=0
|
||||
tunnel.2.option.outbound.nickname=shared clients
|
||||
tunnel.2.option.outbound.quantity=2
|
||||
tunnel.2.sharedClient=true
|
||||
tunnel.2.startOnLoad=false
|
||||
tunnel.2.targetDestination=mtn.i2p-projekt.i2p:4691
|
||||
tunnel.2.type=client
|
||||
tunnel.3.description=My eepsite
|
||||
tunnel.3.i2cpHost=127.0.0.1
|
||||
tunnel.3.i2cpPort=7654
|
||||
tunnel.3.name=I2P webserver
|
||||
tunnel.3.option.i2cp.destination.sigType=7
|
||||
tunnel.3.option.i2p.streaming.limitAction=http
|
||||
tunnel.3.option.i2p.streaming.maxConcurrentStreams=20
|
||||
tunnel.3.option.i2p.streaming.maxConnsPerDay=100
|
||||
tunnel.3.option.i2p.streaming.maxConnsPerHour=40
|
||||
tunnel.3.option.i2p.streaming.maxConnsPerMinute=10
|
||||
tunnel.3.option.i2p.streaming.maxTotalConnsPerMinute=25
|
||||
tunnel.3.option.inbound.length=3
|
||||
tunnel.3.option.inbound.lengthVariance=0
|
||||
tunnel.3.option.inbound.nickname=eepsite
|
||||
tunnel.3.option.maxPosts=3
|
||||
tunnel.3.option.maxTotalPosts=10
|
||||
tunnel.3.option.outbound.length=3
|
||||
tunnel.3.option.outbound.lengthVariance=0
|
||||
tunnel.3.option.outbound.nickname=eepsite
|
||||
tunnel.3.option.shouldBundleReplyInfo=false
|
||||
tunnel.3.privKeyFile=eepsite/eepPriv.dat
|
||||
tunnel.3.spoofedHost=mysite.i2p
|
||||
tunnel.3.startOnLoad=false
|
||||
tunnel.3.targetHost=127.0.0.1
|
||||
tunnel.3.targetPort=7658
|
||||
tunnel.3.type=httpserver
|
||||
tunnel.4.description=smtp server
|
||||
tunnel.4.i2cpHost=127.0.0.1
|
||||
tunnel.4.i2cpPort=7654
|
||||
tunnel.4.interface=127.0.0.1
|
||||
tunnel.4.listenPort=7659
|
||||
tunnel.4.name=smtp.postman.i2p
|
||||
tunnel.4.option.i2cp.destination.sigType=EdDSA_SHA512_Ed25519
|
||||
tunnel.4.option.i2cp.reduceIdleTime=900000
|
||||
tunnel.4.option.i2cp.reduceOnIdle=true
|
||||
tunnel.4.option.i2cp.reduceQuantity=1
|
||||
tunnel.4.option.inbound.backupQuantity=0
|
||||
tunnel.4.option.inbound.length=3
|
||||
tunnel.4.option.inbound.lengthVariance=0
|
||||
tunnel.4.option.inbound.nickname=shared clients
|
||||
tunnel.4.option.inbound.quantity=2
|
||||
tunnel.4.option.outbound.backupQuantity=0
|
||||
tunnel.4.option.outbound.length=3
|
||||
tunnel.4.option.outbound.lengthVariance=0
|
||||
tunnel.4.option.outbound.nickname=shared clients
|
||||
tunnel.4.option.outbound.quantity=2
|
||||
tunnel.4.sharedClient=true
|
||||
tunnel.4.startOnLoad=true
|
||||
tunnel.4.targetDestination=smtp.postman.i2p:25
|
||||
tunnel.4.type=client
|
||||
tunnel.5.description=pop3 server
|
||||
tunnel.5.i2cpHost=127.0.0.1
|
||||
tunnel.5.i2cpPort=7654
|
||||
tunnel.5.interface=127.0.0.1
|
||||
tunnel.5.listenPort=7660
|
||||
tunnel.5.name=pop3.postman.i2p
|
||||
tunnel.5.option.i2cp.destination.sigType=EdDSA_SHA512_Ed25519
|
||||
tunnel.5.option.i2cp.reduceIdleTime=900000
|
||||
tunnel.5.option.i2cp.reduceOnIdle=true
|
||||
tunnel.5.option.i2cp.reduceQuantity=1
|
||||
tunnel.5.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.5.option.inbound.backupQuantity=0
|
||||
tunnel.5.option.inbound.length=3
|
||||
tunnel.5.option.inbound.lengthVariance=0
|
||||
tunnel.5.option.inbound.nickname=shared clients
|
||||
tunnel.5.option.inbound.quantity=2
|
||||
tunnel.5.option.outbound.backupQuantity=0
|
||||
tunnel.5.option.outbound.length=3
|
||||
tunnel.5.option.outbound.lengthVariance=0
|
||||
tunnel.5.option.outbound.nickname=shared clients
|
||||
tunnel.5.option.outbound.quantity=2
|
||||
tunnel.5.sharedClient=true
|
||||
tunnel.5.startOnLoad=true
|
||||
tunnel.5.targetDestination=pop.postman.i2p:110
|
||||
tunnel.5.type=client
|
||||
tunnel.6.description=HTTPS proxy for browsing eepsites and the web
|
||||
tunnel.6.i2cpHost=127.0.0.1
|
||||
tunnel.6.i2cpPort=7654
|
||||
tunnel.6.interface=127.0.0.1
|
||||
tunnel.6.listenPort=4445
|
||||
tunnel.6.name=I2P HTTPS Proxy
|
||||
tunnel.6.option.i2cp.reduceIdleTime=900000
|
||||
tunnel.6.option.i2cp.reduceOnIdle=true
|
||||
tunnel.6.option.i2cp.reduceQuantity=1
|
||||
tunnel.6.option.i2p.streaming.connectDelay=1000
|
||||
tunnel.6.option.inbound.backupQuantity=0
|
||||
tunnel.6.option.inbound.length=3
|
||||
tunnel.6.option.inbound.lengthVariance=0
|
||||
tunnel.6.option.inbound.nickname=shared clients
|
||||
tunnel.6.option.inbound.quantity=2
|
||||
tunnel.6.option.outbound.backupQuantity=0
|
||||
tunnel.6.option.outbound.length=3
|
||||
tunnel.6.option.outbound.lengthVariance=0
|
||||
tunnel.6.option.outbound.nickname=shared clients
|
||||
tunnel.6.option.outbound.quantity=2
|
||||
tunnel.6.proxyList=outproxy-tor.meeh.i2p
|
||||
tunnel.6.sharedClient=true
|
||||
tunnel.6.startOnLoad=true
|
||||
tunnel.6.type=connectclient
|
||||
64
plinth/modules/i2p/tests/test_helpers.py
Normal file
64
plinth/modules/i2p/tests/test_helpers.py
Normal file
@ -0,0 +1,64 @@
|
||||
# This file is part of FreedomBox.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program 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 Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
import unittest
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from plinth.modules.i2p.helpers import TunnelEditor
|
||||
|
||||
DATA_DIR = Path(__file__).parent / 'data'
|
||||
TUNNEL_CONF_PATH = DATA_DIR / 'i2ptunnel.config'
|
||||
TUNNEL_HTTP_NAME = 'I2P HTTP Proxy'
|
||||
|
||||
|
||||
class TunnelEditorTests(unittest.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
self.editor = TunnelEditor(str(TUNNEL_CONF_PATH))
|
||||
|
||||
def test_reading_conf(self):
|
||||
self.editor.read_conf()
|
||||
self.assertGreater(len(self.editor.lines), 1)
|
||||
|
||||
def test_setting_idx(self):
|
||||
self.editor.read_conf()
|
||||
self.assertIsNone(self.editor.idx)
|
||||
self.editor.set_tunnel_idx(TUNNEL_HTTP_NAME)
|
||||
self.assertEqual(self.editor.idx, 0)
|
||||
|
||||
def test_setting_tunnel_props(self):
|
||||
self.editor.read_conf()
|
||||
self.editor.set_tunnel_idx('I2P HTTP Proxy')
|
||||
interface = '0.0.0.0'
|
||||
self.editor.set_tunnel_prop('interface', interface)
|
||||
self.assertEqual(self.editor['interface'], interface)
|
||||
|
||||
def test_getting_inexistent_props(self):
|
||||
self.editor.read_conf()
|
||||
self.editor.idx = 0
|
||||
self.assertRaises(KeyError, self.editor.__getitem__, 'blabla')
|
||||
|
||||
def test_setting_new_props(self):
|
||||
self.editor.read_conf()
|
||||
self.editor.idx = 0
|
||||
value = 'lol'
|
||||
prop = 'blablabla'
|
||||
self.editor[prop] = value
|
||||
self.assertEqual(self.editor[prop], value)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Loading…
x
Reference in New Issue
Block a user