firewall and routing of port 80 to 8080
- create nftables in install.py - routing of port 80 to 8080 if boxweb is using 8080 - modified change_firewall in utils.py used also by router.py + automatically start desktop and web browser when boxweb is configured
This commit is contained in:
12
desktop/boxweb-browser.service
Normal file
12
desktop/boxweb-browser.service
Normal file
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description = Running Web Browser for boxweb
|
||||
Requires=labwc.service
|
||||
Requires=boxweb.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
ExecStart = /home/l_samenv/boxweb/start_browser.py
|
||||
|
||||
[Install]
|
||||
WantedBy = default.target
|
||||
|
||||
11
desktop/labwc.service
Normal file
11
desktop/labwc.service
Normal file
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=LabWC Wayland compositor
|
||||
|
||||
[Service]
|
||||
User=l_samenv
|
||||
Type=simple
|
||||
ExecStart=/usr/bin/labwc
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=default.target
|
||||
60
install.py
60
install.py
@@ -47,6 +47,9 @@ except ImportError:
|
||||
if serial is None:
|
||||
exit()
|
||||
|
||||
main_ports = {22} # ssh
|
||||
|
||||
router_ports = set()
|
||||
|
||||
TOOLS = BoxInfo.TOOLS
|
||||
BOX_TYPES = {'ionopi', 'ionopimax', 'control-box', 'bare-apu', 'dual-eth-rpi'}
|
||||
@@ -198,6 +201,18 @@ def do_cmd(*command, sudo=True):
|
||||
show.dirty = True
|
||||
|
||||
|
||||
def do_copy(src, dst):
|
||||
"""copy or delete (src=None)"""
|
||||
as_root = not dst.is_relative_to('/home/l_samenv')
|
||||
if src is None:
|
||||
if dst.exists():
|
||||
do_cmd('rm', dst, sudo=as_root)
|
||||
return
|
||||
if dst.exists() and getmtime(src) <= getmtime(dst):
|
||||
return
|
||||
do_cmd('cp', src, dst, sudo=as_root)
|
||||
|
||||
|
||||
def frappy(cfg=None, port=None, requirements='', **kwds):
|
||||
if not cfg:
|
||||
return False, None
|
||||
@@ -209,27 +224,18 @@ def frappy(cfg=None, port=None, requirements='', **kwds):
|
||||
return False, FRAPPY_SERVICE
|
||||
|
||||
|
||||
def router(firewall=False, **opts):
|
||||
if firewall:
|
||||
active, enabled = check_service('nftables')
|
||||
ports = {22}
|
||||
if opts:
|
||||
for port in firewall.split(','):
|
||||
ports.add(int(port))
|
||||
for key in opts:
|
||||
try:
|
||||
ports.add(int(key))
|
||||
except ValueError:
|
||||
pass
|
||||
ports.update((1110, 1111, 1112))
|
||||
if change_firewall(True, ports, doit):
|
||||
show.dirty = True
|
||||
elif change_firewall(False, set(), doit):
|
||||
show.dirty = True
|
||||
def router(**opts):
|
||||
active, enabled = check_service('nftables')
|
||||
if opts:
|
||||
for key in opts:
|
||||
try:
|
||||
router_ports.add(int(key))
|
||||
except ValueError:
|
||||
pass
|
||||
router_ports.update((1110, 1111, 1112))
|
||||
if not opts:
|
||||
return True, None
|
||||
try:
|
||||
os.remove(TO_SYSTEM[0] / 'etc/nftables.conf')
|
||||
with open(TOOLS / 'requirements.txt') as f:
|
||||
pip_requirements['root']['tools'] = f.read()
|
||||
except FileNotFoundError:
|
||||
@@ -281,8 +287,21 @@ def pip():
|
||||
|
||||
|
||||
def boxweb(port=8080, page=None):
|
||||
port = int(port)
|
||||
servicecfg = None if page is None else BOXWEB_SERVICE.format(port=port, page=page)
|
||||
if page is None:
|
||||
servicecfg = None
|
||||
else:
|
||||
port = int(port)
|
||||
main_ports.add(port)
|
||||
if port == 8080:
|
||||
main_ports.add(80)
|
||||
servicecfg = BOXWEB_SERVICE.format(port=port, page=page)
|
||||
for file, dest in (('labwc.service', '/etc/systemd/system'),
|
||||
('boxweb-browser.service', '/home/l_samenv/.config/systemd/user')):
|
||||
src = TOOLS / 'desktop' / file
|
||||
dst = Path(dest) / file
|
||||
if not page:
|
||||
src = None # -> remove
|
||||
do_copy(src, dst)
|
||||
return port <= 1024, servicecfg
|
||||
|
||||
|
||||
@@ -697,6 +716,7 @@ def handle_config():
|
||||
if servicecfg and to_start.get('service') is None:
|
||||
to_start[service] = 'restart', as_root
|
||||
|
||||
change_firewall(router_ports, main_ports, doit)
|
||||
if 'dialout' not in unix_cmd('id l_samenv'):
|
||||
do_cmd('usermod -a -G dialout l_samenv')
|
||||
pip()
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0;
|
||||
|
||||
# accept any localhost traffic
|
||||
iif lo accept
|
||||
|
||||
# accept traffic originated from us
|
||||
ct state established,related accept
|
||||
|
||||
# activate the following line to accept common local services
|
||||
tcp dport { 22 } ct state new accept
|
||||
|
||||
# ICMPv6 packets which must not be dropped, see https://tools.ietf.org/html/rfc4890#section-4.4.1
|
||||
meta nfproto ipv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-reply, echo-request, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, 148, 149 } accept
|
||||
ip6 saddr fe80::/10 icmpv6 type { 130, 131, 132, 143, 151, 152, 153 } accept
|
||||
|
||||
# count and drop any other traffic
|
||||
counter drop
|
||||
}
|
||||
}
|
||||
@@ -421,10 +421,6 @@ class Service:
|
||||
|
||||
@classmethod
|
||||
def run(cls, routes):
|
||||
firewall = routes.pop('firewall', None)
|
||||
firewall_on = firewall is not None
|
||||
if firewall_on:
|
||||
cls.firewall_ports = set(int(r) for r in firewall.split(',') if r.strip())
|
||||
Service(1110, None, InfoHandler, 5, handler_args=(log.DEBUG,))
|
||||
Service(1111, None, InfoHandler, 5, handler_args=(log.INFO,))
|
||||
Service(1112, None, InfoHandler, 5, handler_args=(log.WARN,))
|
||||
@@ -439,7 +435,7 @@ class Service:
|
||||
else:
|
||||
remoteport = port
|
||||
Service(port, (host, remoteport), TcpHandler)
|
||||
change_firewall(firewall_on, cls.firewall_ports)
|
||||
change_firewall(cls.firewall_ports, None)
|
||||
while True:
|
||||
try:
|
||||
# log.debug('select %r', list(cls.readers))
|
||||
@@ -476,3 +472,5 @@ if __name__ == '__main__':
|
||||
routercfg = BoxInfo().read_config('ROUTER')
|
||||
if routercfg:
|
||||
Service.run(routercfg)
|
||||
else:
|
||||
print('router is not configured')
|
||||
|
||||
109
utils.py
109
utils.py
@@ -2,6 +2,7 @@ import os
|
||||
import socket
|
||||
import threading
|
||||
import re
|
||||
import tempfile
|
||||
from glob import glob
|
||||
from pathlib import Path
|
||||
from configparser import ConfigParser
|
||||
@@ -10,6 +11,48 @@ from subprocess import Popen, PIPE
|
||||
|
||||
|
||||
FIREWALL_CONF = '/etc/nftables.conf'
|
||||
FIREWALL_MAIN = Path('~/.config/firewall').expanduser()
|
||||
|
||||
NFTABLES_CONF = """#!/usr/sbin/nft -f
|
||||
|
||||
flush ruleset
|
||||
|
||||
table inet filter {
|
||||
chain input {
|
||||
type filter hook input priority 0;
|
||||
|
||||
# accept any localhost traffic
|
||||
iif lo accept
|
||||
|
||||
# accept traffic originated from us
|
||||
ct state established,related accept
|
||||
|
||||
# activate the following line to accept common local services
|
||||
tcp dport { %s } ct state new accept
|
||||
|
||||
# ICMPv6 packets which must not be dropped, see https://tools.ietf.org/html/rfc4890#section-4.4.1
|
||||
meta nfproto ipv6 icmpv6 type { destination-unreachable, packet-too-big, time-exceeded, parameter-problem, echo-reply, echo-request, nd-router-solicit, nd-router-advert, nd-neighbor-solicit, nd-neighbor-advert, 148, 149 } accept
|
||||
ip6 saddr fe80::/10 icmpv6 type { 130, 131, 132, 143, 151, 152, 153 } accept
|
||||
|
||||
# count and drop any other traffic
|
||||
counter drop
|
||||
}
|
||||
}
|
||||
%s
|
||||
"""
|
||||
|
||||
ROUTE80 = """
|
||||
table ip nat {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority 0; policy accept;
|
||||
tcp dport 80 redirect to 8080
|
||||
}
|
||||
|
||||
chain postrouting {
|
||||
type nat hook postrouting priority 0; policy accept;
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
if os.geteuid():
|
||||
@@ -255,45 +298,49 @@ def check_service(service, set_on=None, execute=None, as_root=True):
|
||||
return active, enabled
|
||||
|
||||
|
||||
def change_firewall(set_on, ports, execute=None):
|
||||
ports.add(22) # always add ssh
|
||||
active, enabled = check_service('nftables')
|
||||
if not set_on:
|
||||
check_service('nftables', False, execute)
|
||||
return active or enabled
|
||||
|
||||
pattern = re.compile('(tcp dport ({.*}) ct state new accept)', re.MULTILINE | re.DOTALL)
|
||||
|
||||
for filename in FIREWALL_CONF, BoxInfo.TOOLS / 'nftables.conf':
|
||||
with open(filename) as f:
|
||||
content = f.read()
|
||||
def change_firewall(router_ports, main_ports, execute=None):
|
||||
"""configure the firewall
|
||||
|
||||
router_ports: the ports needed for the router
|
||||
main_ports: other ports needed to be open. when None, read it from FIREWALL_MAIN
|
||||
execute: None: do it quietly, True: do it, False: display only
|
||||
"""
|
||||
if main_ports is None:
|
||||
try:
|
||||
((prevline, prevports),) = pattern.findall(content)
|
||||
break
|
||||
except (TypeError, ValueError):
|
||||
pass
|
||||
with open(FIREWALL_MAIN) as fil:
|
||||
main_ports = set(int(p) for p in f.read().split())
|
||||
except FileNotFoundError:
|
||||
main_ports = {22}
|
||||
else:
|
||||
print(f'{FIREWALL_CONF} does not contain expected pattern for open ports - firewall off?')
|
||||
return False
|
||||
main_ports.add(22) # always add ssh
|
||||
if execute:
|
||||
with open(FIREWALL_MAIN, 'w') as fil:
|
||||
fil.write(' '.join(str(p) for p in sorted(main_ports)))
|
||||
fil.write('\n')
|
||||
|
||||
# parse previous port set
|
||||
prevportset = {int(p) for p in prevports[1:-1].split(',')}
|
||||
line = prevline.replace(prevports, '{ %s }' % ', '.join((str(p) for p in sorted(ports))))
|
||||
if prevportset == ports:
|
||||
ports = main_ports | router_ports
|
||||
active, enabled = check_service('nftables')
|
||||
|
||||
with open(FIREWALL_CONF) as f:
|
||||
oldcontent = f.read()
|
||||
|
||||
portstr = ', '.join(str(p) for p in sorted(ports))
|
||||
content = NFTABLES_CONF % (portstr, ROUTE80 if {80, 8080} <= ports else '')
|
||||
|
||||
if content == oldcontent:
|
||||
if active and enabled:
|
||||
return False
|
||||
check_service('nftables', True, execute)
|
||||
return True
|
||||
if os.geteuid() == 0:
|
||||
if execute is not None:
|
||||
print(f'change firewall ports to {ports}')
|
||||
if execute != 0:
|
||||
with open('f{FIREWALL_CONF}.tmp', 'w') as f:
|
||||
f.write(content.replace(prevline, line))
|
||||
os.rename('f{FIREWALL_CONF}.tmp', FIREWALL_CONF)
|
||||
if execute is not None:
|
||||
print(f'set firewall ports to {portstr}')
|
||||
if execute != 0:
|
||||
with tempfile.NamedTemporaryFile('w') as fil:
|
||||
fil.write(content)
|
||||
fil.flush()
|
||||
unix_cmd('cp', fil.name, FIREWALL_CONF, execute=execute)
|
||||
unix_cmd('chmod 0644', FIREWALL_CONF, execute=execute)
|
||||
|
||||
unix_cmd('systemctl restart nftables', execute=execute)
|
||||
unix_cmd('systemctl enable nftables', execute=execute)
|
||||
elif ports - prevportset:
|
||||
print('need sudo rights to modify firewall')
|
||||
return True
|
||||
|
||||
Reference in New Issue
Block a user