moved from centos 7 to debian-bookworm
includes modifications in install.py and router.py
This commit is contained in:
10
README.md
10
README.md
@ -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
11
cfg/linse-apu7_5fb688.cfg
Normal 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
|
@ -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))
|
||||
|
78
install.py
78
install.py
@ -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
2
mkusb.sh
Normal file → Executable 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
|
||||
|
||||
|
45
router.py
45
router.py
@ -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])
|
||||
|
@ -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
|
||||
|
18
to_system/etc/default/isc-dhcp-server
Normal file
18
to_system/etc/default/isc-dhcp-server
Normal 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=""
|
@ -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"
|
||||
|
@ -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]
|
||||
|
Reference in New Issue
Block a user