moved from centos 7 to debian-bookworm

includes modifications in install.py and router.py
This commit is contained in:
l_samenv
2024-03-05 08:03:00 +01:00
parent 8680e5b2a9
commit e4e37eb049
17 changed files with 110 additions and 64 deletions

View File

@ -1,14 +1,14 @@
# APU server
# Linux Box installtion tools
The APU is a Linux box to be used as a control box at LIN sample environment
`servercfg/` contains the configuration files for all boxes
`cfg/` contains the configuration files for all boxes
`to_system/` contains the files to be installed in the system
## config files
filename: servercfg/(hostname)_(hexdigits).cfg
filename: cfg/(hostname)_(hexdigits).cfg
* (hostname) the hostname to be given on startup
* (hexdigits) the last six digits of the ethernet adress of the first interface on the APU (leftmost plug)
@ -96,7 +96,7 @@ If you do not have one, you may create it from another box
log in as root/FrappyLinse to an apu box
```
apu> cd aputools
apu> cd boxtools
apu> bash mkusb.sh
```
You are asked to give the device name from a list (typically sdb)
@ -150,7 +150,7 @@ which image?
login with root/FrappyLinse
```
> cd aputools
> cd boxtools
> git pull
> python3 install.py
...

11
cfg/linse-apu7_5fb688.cfg Normal file
View File

@ -0,0 +1,11 @@
[NETWORK]
; please refer to README.md for help
enp1s0=wan,192.168.1.0/24
enp2s0=192.168.2.2
enp3s0=192.168.3.3
enp4s0=192.168.127.254
[ROUTER]
; please refer to README.md for help
3001=192.168.127.254:3001
8080=192.168.127.254:80

View File

@ -7,7 +7,7 @@ import threading
# display tty device
tty = '/dev/ttyS1'
STARTUP_TEXT = '/root/aputools/startup_display.txt'
STARTUP_TEXT = '/home/l_samenv/installtools/startup_display.txt'
ESC = 0x1b
ESCESC = bytes((ESC, ESC))

View File

@ -2,7 +2,7 @@
"""install.py
- copy files from to_system into system directories
- set host name / network settings from aputools/servercfg file
- set host name / network settings from boxtools/cfg file
"""
if bytes == str:
@ -21,12 +21,17 @@ from ipaddress import IPv4Interface
from configparser import ConfigParser
from os.path import join, getmtime, exists, basename
if os.geteuid() != 0:
exit("You need to have root privileges to run this script.\nPlease try again, this time using 'sudo'. Exiting.")
TOOLS = '/home/l_samenv/boxtools'
more_info = False
os.chdir('/root/aputools/to_system')
os.chdir(f'{TOOLS}/to_system')
DEL = '__to_delete__'
CFGPATH = '/root/aputools/servercfg/%s_%6.6x.cfg'
STARTUP_TEXT = '/root/aputools/startup_display.txt'
CFGPATH = f'{TOOLS}/cfg/%s_%6.6x.cfg'
STARTUP_TEXT = f'{TOOLS}/startup_display.txt'
COMMENT = "; please refer to README.md for help"
@ -62,12 +67,12 @@ max-lease-time 7200;
authoritative;
"""
ROUTER_TEMPLATE = """[Unit]
ROUTER_TEMPLATE = f"""[Unit]
Description = Routing to locally connected hardware
After = network.target
[Service]
ExecStart = /usr/bin/python3 /root/aputools/router.py
ExecStart = /usr/bin/python3 {TOOLS}/router.py
[Install]
WantedBy = multi-user.target
@ -79,19 +84,19 @@ After = network.target
[Service]
User = l_samenv
ExecStart = /usr/bin/python3 /home/l_samenv/frappy/bin/secop-server %s
ExecStart = /usr/bin/python3 /home/l_samenv/frappy/bin/frappy-server %s
[Install]
WantedBy = multi-user.target
"""
DISPLAY_TEMPLATE = """[Unit]
DISPLAY_TEMPLATE = f"""[Unit]
Description = status display
After = network.target
[Service]
User = root
ExecStart = /usr/bin/python3 /root/aputools/display.py -d
ExecStart = /usr/bin/python3 {TOOLS}/display.py -d
[Install]
WantedBy = multi-user.target
@ -105,8 +110,8 @@ ifname_mapping = {
}
pip_requirements = {
'root': {},
'l_samenv': {},
'root': {}
}
@ -115,7 +120,6 @@ dhcp_server_cfg = [] # configuration for dhcp
main_info = { # info to be determined depending on cfg
'ifname': '', # name of interface of wan (dhcp) port
'addr': '', # addr of wan port
'current': None,
'hostname': socket.gethostname() # effective or given host name
}
@ -145,8 +149,8 @@ def router(**opts):
if not opts:
return None
try:
with open('/root/aputools/requirements.txt') as f:
pip_requirements['root']['aputools'] = f.read()
with open(f'{TOOLS}/requirements.txt') as f:
pip_requirements['root']['tools'] = f.read()
except FileNotFoundError:
pass
return ROUTER_TEMPLATE
@ -225,7 +229,7 @@ def write_when_new(filename, content, ignore_reduction=False):
old_set = set(v for v in (old or '').split('\n') if not v.startswith('#'))
content_set -= old_set
if content_set:
print('missing packages: %s' % (', '.join(content_set - old_set)))
print(f"missing in {filename}: {(', '.join(content_set - old_set))}")
else:
return False
if doit:
@ -264,24 +268,13 @@ def write_when_new(filename, content, ignore_reduction=False):
def create_if(name, cfg):
result = dict(
TYPE='Ethernet',
NAME=name,
DEVICE=name,
BOOTPROTO='none',
ONBOOT='yes',
PROXY_METHOD='none',
BROWSER_ONLY='no',
IPV4_FAILURE_FATAL='yes',
IPV6INIT='no')
if cfg == 'off':
result['ONBOOT'] = 'no'
result = None
elif cfg.startswith('wan') or cfg == 'dhcp':
if main_info.get('mainif', name) != name:
raise ValueError('can not have more than one WAN/DHCP port')
main_info['mainif'] = name
main_info['addr'] = net_addr[name]
result['BOOTPROTO'] = 'dhcp'
# default: all <= 192.0.0.0
# others have to be added explicitly
dhcp_server_cfg.append(('0.0.0.0/128.0.0.0', []))
@ -289,6 +282,7 @@ def create_if(name, cfg):
for nw in cfg.split(',')[1:]:
nw = IPv4Interface(nw).network
dhcp_server_cfg.append((nw.with_netmask, []))
result = f"allow-hotplug {name}\niface {name} inet dhcp"
else:
cfgip = IPv4Interface(cfg)
network = cfgip.network
@ -300,12 +294,12 @@ def create_if(name, cfg):
else:
cfgip = network.network_address + 1
dhcp_server_cfg.append((network.with_netmask, [(str(otherip.ip), str(otherip.ip))]))
result['IPADDR'] = str(cfgip)
addr = str(cfgip)
else: # subnet with multiple adresses -> static adresses only. dhcp range not yet implemented
result['IPADDR'] = str(cfgip.ip)
result['NETMASK'] = network.netmask
result['PREFIX'] = str(network.prefixlen)
return result
addr = str(cfgip.ip)
# result['NETMASK'] = network.netmask
result = f"allow-hotplug {name}\niface {name} inet static\n address {addr}/{network.prefixlen}"
return result + '\n'
def walk(action):
@ -416,12 +410,12 @@ def handle_config():
display_available = disp.read(8)[0:4] == b'\x1b\x1b\x05\xf3'
if display_available:
# leftmost interface labelled 'eth0'
main['mainif'] = sorted_if[0]
# main_info['mainif'] = sorted_if[0]
typ = 'controlbox'
template = CONTROLBOX_TEMPLATE
else:
# rightmost interface
main['mainif'] = sorted_if[-1]
# main_info['mainif'] = sorted_if[-1]
typ = 'bare apu'
template = CONFIG_TEMPLATE
print('no cfg file found for this', typ, f'with id {apuid:%6.6x} (hostname={hostname})')
@ -443,7 +437,7 @@ def handle_config():
os.system('sh sethostname.sh')
else:
if cfgfile:
print('replace host name %r by %r' % (hostname, newhostname))
print('replace host name %r by %r' % (main_info['hostname'], newhostname))
show.dirty = True
main_info['hostname'] = newhostname
if cfgfile is None:
@ -456,8 +450,8 @@ def handle_config():
network = {ifname_mapping.get(k, k): v for k, v in parser['NETWORK'].items()}
for ifname in net_addr:
content = create_if(ifname, network.pop(ifname, 'off'))
content = '\n'.join('%s=%s' % kv for kv in content.items())
todo = write_when_new('/etc/sysconfig/network-scripts/ifcfg-%s' % ifname, content)
# content = '\n'.join('%s=%s' % kv for kv in content.items())
todo = write_when_new(f'/etc/network/interfaces.d/{ifname}', content)
if todo and not more_info:
print('change', ifname)
show.dirty = True
@ -479,11 +473,11 @@ def handle_config():
todo = write_when_new('/etc/dhcp/dhcpd.conf', content)
if todo:
print('change dhcpd.conf')
to_start['dhcpd'] = 'restart'
to_start['isc-dhcp-server'] = 'restart'
show.dirty = True
elif doit:
unix_cmd('systemctl stop dhcpd')
unix_cmd('systemctl disable dhcpd')
unix_cmd('systemctl stop isc-dhcp-server')
unix_cmd('systemctl disable isc-dhcp-server')
content = []
dirty = False
for section in parser.sections():
@ -502,7 +496,7 @@ def handle_config():
with open(cfgfile, 'w') as f:
f.write('\n'.join(content))
except Exception as e:
print('ERROR: can not handle %s %s: %r' % (hostname, ifname, e))
print('ERROR: can not handle %s %s: %r' % (main_info['hostname'], ifname, e))
raise
return False
@ -546,6 +540,8 @@ def handle_config():
show.dirty = True
if action == 'if_restart':
unix_cmd('ifdown %s' % service)
for service, action in to_start.items():
if action == 'if_restart':
unix_cmd('ifup %s' % service)
else:
if action == 'restart':
@ -579,7 +575,7 @@ else:
doit = True
handle_config()
walk(Do())
with open('/root/aputools/current', 'w') as f:
with open(f'{TOOLS}/current', 'w') as f:
f.write(result)
elif 'more'.startswith(answer.lower()):
more_info = True

2
mkusb.sh Normal file → Executable file
View File

@ -1,3 +1,5 @@
#!/bin/bash
# create an USB stick with TinyLinux on it
# name of the USB stick to be created
NAME=BOOT_TINY

View File

@ -11,6 +11,7 @@ from serial import serial_for_url
from subprocess import Popen, PIPE, check_output, call, DEVNULL
from configparser import ConfigParser
FIREWALL_CONF = '/etc/nftables.conf'
class Log:
DEBUG = 0
@ -187,7 +188,7 @@ class Router(IoHandler):
pass
except Exception as e:
msg = f'error in reply: {e!r}'
service.failures[service.port] = msg
self.service.failures[self.service.port] = msg
self.close()
def close(self):
@ -344,6 +345,7 @@ class Service:
pending_connects = {}
routes = {}
failures = {}
firewall_ports = set()
tmo = 5
def __init__(self, port, addr, iocls, maxcount=None, handler_args=()):
@ -358,6 +360,7 @@ class Service:
self.handler_args = handler_args
self.readers[s.fileno()] = self.accept
self.port = port
self.firewall_ports.add(port)
if addr:
self.routes[port] = self # not for InfoHandler
if maxcount is None:
@ -399,23 +402,38 @@ class Service:
self.handlers[handler.fno] = handler
@classmethod
def run(cls, routes, restrict=None):
if restrict is not None:
lines = BASIC % dict(accept='DROP' if restrict else 'ACCEPT')
unix_cmd('iptables -F')
for line in lines.split('\n'):
if line.strip():
unix_cmd(line)
if restrict:
unix_cmd(FILTER % 22)
def change_firewall(cls):
pattern = re.compile('(tcp dport ({.*}) ct state new accept)', re.MULTILINE | re.DOTALL)
with open(FIREWALL_CONF) as f:
content = f.read()
try:
((prevline, prevports),) = pattern.findall(content)
except TypeError:
raise ValueError(f'{FIREWALL_CONF} does not contain expected pattern for open ports')
# parse previous port set
prevportset = {int(p) for p in prevports[1:-1].split(',')}
# keep port numbers below 1024
ports = {p for p in prevportset if p < 1024} | set(cls.firewall_ports)
line = prevline.replace(prevports, '{ %s }' % ', '.join((str(p) for p in sorted(ports))))
if prevportset != ports:
if os.geteuid() == 0:
with open('f{FIREWALL_CONF}.tmp', 'w') as f:
f.write(content.replace(prevline, line))
os.rename('f{FIREWALL_CONF}.tmp', FIREWALL_CONF)
unix_cmd('systemctl enable --now nftables')
else:
print('need sudo rights to modify firewall')
@classmethod
def run(cls, routes):
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,))
for port, dest in routes.items():
port = int(port)
if restrict:
unix_cmd(FILTER % port)
if '/' in dest:
Service(port, dest, SerialHandler)
else:
@ -425,6 +443,7 @@ class Service:
else:
remoteport = port
Service(port, (host, remoteport), TcpHandler)
cls.change_firewall()
while True:
try:
# log.debug('select %r', list(cls.readers))
@ -458,7 +477,7 @@ class Service:
if __name__ == '__main__':
parser = ConfigParser()
cfgfiles = glob('/root/aputools/servercfg/%s_*.cfg' % socket.gethostname())
cfgfiles = glob('/home/l_samenv/boxtools/cfg/%s_*.cfg' % socket.gethostname())
if len(cfgfiles) != 1:
raise ValueError('there must be one and only one single cfgfile %r' % cfgfiles)
parser.read(cfgfiles[0])

View File

@ -1,7 +1,7 @@
ETHNAME=$(cat /sys/class/net/enp1s0/address)
ETHNAME=${ETHNAME//:/}
APUID=${ETHNAME: -6}
FOUND="$(shopt -s nullglob; echo /root/aputools/servercfg/*_$APUID.cfg)"
FOUND="$(shopt -s nullglob; echo /home/l_samenv/boxtools/cfg/*_$APUID.cfg)"
if [ -z "$FOUND" ]; then
HOSTNAME=apu$APUID
else

View File

@ -0,0 +1,18 @@
# Defaults for isc-dhcp-server (sourced by /etc/init.d/isc-dhcp-server)
# Path to dhcpd's config file (default: /etc/dhcp/dhcpd.conf).
#DHCPDv4_CONF=/etc/dhcp/dhcpd.conf
#DHCPDv6_CONF=/etc/dhcp/dhcpd6.conf
# Path to dhcpd's PID file (default: /var/run/dhcpd.pid).
#DHCPDv4_PID=/var/run/dhcpd.pid
#DHCPDv6_PID=/var/run/dhcpd6.pid
# Additional options to start dhcpd with.
# Don't use options -cf or -pf here; use DHCPD_CONF/ DHCPD_PID instead
#OPTIONS=""
# On what interfaces should the DHCP server (dhcpd) serve DHCP requests?
# Separate multiple interfaces with spaces, e.g. "eth0 eth1".
INTERFACESv4="enp2s0 enp3s0 enp4s0"
INTERFACESv6=""

View File

@ -7,6 +7,6 @@ function service_status () {
echo ${name} $(systemctl is-active ${name}) $enabled; \
done | column -t | grep --color=always '\(disabled\|inactive\|$\)' | nl -bn
}
alias current='cat /root/aputools/current'
alias current='cat /home/l_samenv/boxtools/current'
service_status router frappy display
echo "> current # show currently installed state"

View File

@ -5,7 +5,7 @@ Before=network.pre-target
[Service]
Type=oneshot
ExecStart = /usr/bin/bash /root/aputools/sethostname.sh
ExecStart = /usr/bin/bash /home/l_samenv/boxtools/sethostname.sh
RemainAfterExit=yes
[Install]