Linux operatsioonisüsteemis töötab võrguühendus: erinevus redaktsioonide vahel
Resümee puudub |
|||
| (ei näidata sama kasutaja 52 vahepealset redaktsiooni) | |||
| 53. rida: | 53. rida: | ||
* teatud olukorras toimub uue objekti tekkimine: 1. saabuva võrguühenduse teenindamisega moodustatakse listen socket alusel koopia; 2. uue protsessi vajadusel olemasolev protsess moodustab endast koopia ja täidab koodi osa mälus uue tarkvara st programmi sisuga) |
* teatud olukorras toimub uue objekti tekkimine: 1. saabuva võrguühenduse teenindamisega moodustatakse listen socket alusel koopia; 2. uue protsessi vajadusel olemasolev protsess moodustab endast koopia ja täidab koodi osa mälus uue tarkvara st programmi sisuga) |
||
| − | ===Võrguühenduse vaatlemine=== |
+ | ===Võrguühenduse vaatlemine - netcat=== |
Serveri poolel on lähtepunktiks ainult nn 'listening socket', peale netcat protsessi käivitamist ütleb protsess kernelile kolm syscall'i |
Serveri poolel on lähtepunktiks ainult nn 'listening socket', peale netcat protsessi käivitamist ütleb protsess kernelile kolm syscall'i |
||
| − | * socket() |
+ | * socket() - nö üksuse loomine |
| + | * bind() - üksuse omaduste täpsustamine |
||
| − | * bind() |
||
| + | * listen() - täiendav üksuse omaduste täpsustamine |
||
| − | * listen() |
||
| + | * accept() - kliendi ootamine, selle tulemusena minnakse moodustama uut nn socket üksust, ja vana jääb ka alles |
||
| − | * accept() |
||
tulemusena paistab (reaalset grep väljundis accept4 ei ole paista) |
tulemusena paistab (reaalset grep väljundis accept4 ei ole paista) |
||
| 177. rida: | 177. rida: | ||
* šovinist võiks võrrelda võrguühenduse klienti nö poeglapsega ja serverit tütarlapsega |
* šovinist võiks võrrelda võrguühenduse klienti nö poeglapsega ja serverit tütarlapsega |
||
| + | ===Võrguühenduse variatsioonid=== |
||
| − | ===sd_listen_fds=== |
||
| + | |||
| + | Väited |
||
| + | |||
| + | * võrguühendusega tegeleb kõige lihtsamal juhtumil üks ja sama protsess (non-threaded, netcat juhtum) |
||
| + | * võrguühendusega tegeleb üks threaded protsess |
||
| + | * võrguühendusega tegeleb protsesside komplekt (nt reuseport, sd_listen_fds) |
||
| + | |||
| + | ===Võrguühenduse vaatlemine - python skript=== |
||
| + | |||
| + | TODO |
||
| + | |||
| + | ===sd_listen_fds - sshd=== |
||
sd_listen_fds (sd nagu systemd) nähtus võimaldab nt sellist asjakorraldust, mis toimub Ubuntu v. 24.04 operatsioonisüsteemis vaikimisi sshd serveriga |
sd_listen_fds (sd nagu systemd) nähtus võimaldab nt sellist asjakorraldust, mis toimub Ubuntu v. 24.04 operatsioonisüsteemis vaikimisi sshd serveriga |
||
| − | * kui keegi pole sshd abil süsteemi sisse loginud, siis port 22 peal kuulab systemd komplekti |
+ | * kui keegi pole sshd abil süsteemi sisse loginud, siis port 22 peal kuulab systemd komplekti kuuluva programm 'init' (analoogne kunagise xinitd vms nn internet superserveriga) - ssh.socket unit juhtimisel |
* kui keegi pole sshd kaudu süsteemi sisse loginud, siis sshd protsesse süsteemis ei töötagi veel (arvutusressusi kokkuhoiu eesmärgil) |
* kui keegi pole sshd kaudu süsteemi sisse loginud, siis sshd protsesse süsteemis ei töötagi veel (arvutusressusi kokkuhoiu eesmärgil) |
||
| − | * kui toimub esimene pöördumine, siis systemd käivitab ssh.service unit abil sshd protsessi kusjuures andes üle listen tegevusega seotud |
+ | * kui toimub esimene pöördumine, siis systemd käivitab ssh.socket unit abil teise system unit'i ssh.service unit abil sshd protsessi enda kusjuures andes üle listen tegevusega seotud failideskriptori (see 9999 port nähtus ei puutu otseselt asjasse, see on üks nö trikk mille abil saab süsteemi eemalt sisse logida minnes mööda tavalisest, st vastasel korral ei saaks süsteemi hästi vaadelda) |
<pre> |
<pre> |
||
| 232. rida: | 244. rida: | ||
* sisselogitud oleks on seotud protsessiga "sshd: root@pts/0" |
* sisselogitud oleks on seotud protsessiga "sshd: root@pts/0" |
||
* kui toimub veel sisselogimisi, siis moodustub "sshd: root@pts/0" kõrvale nt "sshd: root@pts/1" |
* kui toimub veel sisselogimisi, siis moodustub "sshd: root@pts/0" kõrvale nt "sshd: root@pts/1" |
||
| + | * netstat väljund selles mõttes petab, et ta ütleb, et tema kuulab port 22, aga selleks ajaks pigem tegeleb võrgus kuulamisega juhba 'sshd -D ...' protsess |
||
seejuures |
seejuures |
||
| 357. rida: | 370. rida: | ||
* ssh.socket on enabletud, ssh.service iseensest ei ole enabletud - seda triggerdab ssh.socket |
* ssh.socket on enabletud, ssh.service iseensest ei ole enabletud - seda triggerdab ssh.socket |
||
* selleks, et selline nähtus toimuks, peab nägema omajagu vaeva systemd osakond, ja sshd tarkvara peab olema ka omalt poolt ettevalmistatud sd_listen_fds abil tegevuse üleandmiseks |
* selleks, et selline nähtus toimuks, peab nägema omajagu vaeva systemd osakond, ja sshd tarkvara peab olema ka omalt poolt ettevalmistatud sd_listen_fds abil tegevuse üleandmiseks |
||
| + | * tundub, et kui ssh.socket ja ssh.service on mõlemad enabletud, siis midagi hulle sellest ei juhtu, st süsteem on algkäivitumise järgselt sshd protsesside koosseisu seisukohalt samasuguses olekus nagu oleks esimene kasutaja sisse logimise järel; st init on näiliselt seotud port :22 osakonnaga ja 'sshd -D ...' sisuliselt |
||
===Reuseport=== |
===Reuseport=== |
||
| 564. rida: | 578. rida: | ||
* nö load balancing toimub kerneli poolt tehtud hash'ing vms abil (seda võiks saada üle kirjutada eBPF programmiga tõenäoliselt) |
* nö load balancing toimub kerneli poolt tehtud hash'ing vms abil (seda võiks saada üle kirjutada eBPF programmiga tõenäoliselt) |
||
| + | |||
| + | ====Turvalisus==== |
||
| + | |||
| + | Väidetavalt ei ole reuseport realistlik ründevektor kuigi tal võiks teoreetiliselt olla potentsiaali |
||
| + | |||
| + | * ta on populaarne interneti ühendatud infosüsteemi komponendi puhul (nginx, haproxy, apache, nsd, unbound) |
||
| + | * ta võimaldab nö ausa teenust pakkuva pordi külge sokutada midagi, ja teenuse ees olev tulemüür laseb liikluse ilmselt läbi |
||
| + | |||
| + | ===Ruuter=== |
||
| + | |||
| + | Väited |
||
| + | |||
| + | * ruuter puhul ei ole tegevused seotud arvutis töötava protsessiga |
||
| + | * ruuteris võrgus liikuvad andmed ei lahku kernel-space'ist |
||
| + | |||
| + | ===af_packet kasutamise näide=== |
||
| + | |||
| + | af_packet kihis töötamine on kõige madalam nö ausal viisil võrgu kasutamine st user-space'ist (edasi tuleks otse kernelis toimetada, nt kerneli mooduli abil, ebpf programmiga vms). Looduses esinevad näited |
||
| + | |||
| + | * dhcp |
||
| + | * synology assistant |
||
| + | * mikrotik winbox ilma ip aadressita kliendiga st wifi router vms seadmega |
||
| + | |||
| + | Väited |
||
| + | |||
| + | * töötab madalamas kihis kui nö tavalised paketifiltri tulemüürid (iptables, nft), st nendega ei saa takistada af_packet viisil koheldavat liiklust (ei kliendi ega serveri poolel) |
||
| + | |||
| + | Server |
||
| + | |||
| + | <pre> |
||
| + | root@ph-minio-01:~# cat packet_server.py |
||
| + | import socket |
||
| + | import struct |
||
| + | |||
| + | def run_server(): |
||
| + | # 0x0003 tells the kernel to capture ALL incoming protocol frames |
||
| + | ETH_P_ALL = 0x0003 |
||
| + | |||
| + | # Create the Layer 2 AF_PACKET socket |
||
| + | # socket.htons converts the protocol integer to network byte order |
||
| + | server_sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL)) |
||
| + | |||
| + | # Bind the socket strictly to the loopback interface |
||
| + | server_sock.bind(("lo", 0)) |
||
| + | |||
| + | print("Layer 2 AF_PACKET Server listening on interface 'lo'...") |
||
| + | |||
| + | while True: |
||
| + | # Receive the raw Ethernet frame (buffer size 65535 bytes) |
||
| + | raw_frame, addr = server_sock.recvfrom(65535) |
||
| + | |||
| + | # An Ethernet header is exactly 14 bytes: |
||
| + | # Destination MAC (6 bytes) + Source MAC (6 bytes) + EtherType (2 bytes) |
||
| + | eth_header = raw_frame[:14] |
||
| + | payload = raw_frame[14:] |
||
| + | |||
| + | # Unpack the 14-byte header using the struct module: |
||
| + | # !6s6sH means: Network order, 6 bytes, 6 bytes, unsigned short (2 bytes) |
||
| + | dest_mac, src_mac, eth_type = struct.unpack("!6s6sH", eth_header) |
||
| + | |||
| + | # Custom EtherType check (We'll use 0x7A7A for our custom test app) |
||
| + | if eth_type == 0x7A7A: |
||
| + | print("\n--- [New Raw Frame Captured] ---") |
||
| + | # Convert MAC address bytes to human-readable hex strings |
||
| + | print(f"Destination MAC : {dest_mac.hex(':')}") |
||
| + | print(f"Source MAC : {src_mac.hex(':')}") |
||
| + | print(f"EtherType (Hex) : {hex(eth_type)}") |
||
| + | try: |
||
| + | print(f"Payload Data : {payload.decode('utf-8')}") |
||
| + | except UnicodeDecodeError: |
||
| + | print(f"Payload Data : {payload}") |
||
| + | |||
| + | if __name__ == "__main__": |
||
| + | run_server() |
||
| + | </pre> |
||
| + | |||
| + | klient |
||
| + | |||
| + | <pre> |
||
| + | root@ph-minio-01:~# cat packet_client.py |
||
| + | import socket |
||
| + | import struct |
||
| + | |||
| + | def run_client(): |
||
| + | # Create the Layer 2 AF_PACKET socket |
||
| + | client_sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW) |
||
| + | |||
| + | # Bind to the loopback interface |
||
| + | client_sock.bind(("lo", 0)) |
||
| + | |||
| + | # Define our raw addresses |
||
| + | # Because it's loopback ('lo'), the MAC addresses are always 6 bytes of 0x00 |
||
| + | LOOPBACK_MAC = b'\x00\x00\x00\x00\x00\x00' |
||
| + | |||
| + | dest_mac = LOOPBACK_MAC |
||
| + | src_mac = LOOPBACK_MAC |
||
| + | |||
| + | # Define a custom EtherType protocol ID (0x7A7A). |
||
| + | # Standard values are things like 0x0800 (IPv4) or 0x86DD (IPv6). |
||
| + | # Using a custom ID guarantees the Linux kernel's internal TCP/IP stack |
||
| + | # will ignore our packet, leaving it entirely for our server script to read! |
||
| + | eth_type = 0x7A7A |
||
| + | |||
| + | # Craft the payload text |
||
| + | message = "Hello from the raw AF_PACKET layer!" |
||
| + | payload = message.encode('utf-8') |
||
| + | |||
| + | # Manually assemble the 14-byte Ethernet Header |
||
| + | # !6s6sH = Network byte order, 6-byte string, 6-byte string, unsigned short integer |
||
| + | eth_header = struct.pack("!6s6sH", dest_mac, src_mac, eth_type) |
||
| + | |||
| + | # Combine the header and payload into one single raw binary frame |
||
| + | full_frame = eth_header + payload |
||
| + | |||
| + | print(f"Sending raw frame ({len(full_frame)} bytes) directly onto 'lo'...") |
||
| + | |||
| + | # Inject the frame onto the wire |
||
| + | client_sock.send(full_frame) |
||
| + | print("Frame injected successfully.") |
||
| + | |||
| + | if __name__ == "__main__": |
||
| + | run_client() |
||
| + | </pre> |
||
| + | |||
| + | Kasutamine |
||
| + | |||
| + | <pre> |
||
| + | root@ph-minio-01:~# python3 packet_client.py |
||
| + | Sending raw frame (49 bytes) directly onto 'lo'... |
||
| + | Frame injected successfully. |
||
| + | root@ph-minio-01:~# |
||
| + | |||
| + | root@ph-minio-01:~# tcpdump -ni lo |
||
| + | tcpdump: verbose output suppressed, use -v[v]... for full protocol decode |
||
| + | listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes |
||
| + | 18:08:12.071780 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype Unknown (0x7a7a), length 49: |
||
| + | 0x0000: 4865 6c6c 6f20 6672 6f6d 2074 6865 2072 Hello.from.the.r |
||
| + | 0x0010: 6177 2041 465f 5041 434b 4554 206c 6179 aw.AF_PACKET.lay |
||
| + | 0x0020: 6572 21 er! |
||
| + | </pre> |
||
| + | |||
| + | Süsteemist sellisel viisil töötavad server programmi on keeruline avastada, nt tavaline 'netstat -lnp' ei näita, aga nt ss programm näitab |
||
| + | |||
| + | <pre> |
||
| + | Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process |
||
| + | p_raw UNCONN 0 0 *:lo * users:(("python3",pid=2283,fd=3)) |
||
| + | </pre> |
||
| + | |||
| + | ning |
||
| + | |||
| + | <pre> |
||
| + | root@ph-minio-01:~# lsof -n -P -p 2283 |
||
| + | COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME |
||
| + | python3 2283 root cwd DIR 253,1 4096 130524 /root |
||
| + | python3 2283 root rtd DIR 253,1 4096 2 / |
||
| + | python3 2283 root txt REG 253,1 6828688 21266 /usr/bin/python3.13 |
||
| + | python3 2283 root mem REG 253,1 3063024 8431 /usr/lib/locale/locale-archive |
||
| + | python3 2283 root mem REG 253,1 27028 33448 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache |
||
| + | python3 2283 root mem REG 253,1 1995216 33459 /usr/lib/x86_64-linux-gnu/libc.so.6 |
||
| + | python3 2283 root mem REG 253,1 178272 20782 /usr/lib/x86_64-linux-gnu/libexpat.so.1.10.2 |
||
| + | python3 2283 root mem REG 253,1 125376 2298 /usr/lib/x86_64-linux-gnu/libz.so.1.3.1 |
||
| + | python3 2283 root mem REG 253,1 977112 33462 /usr/lib/x86_64-linux-gnu/libm.so.6 |
||
| + | python3 2283 root mem REG 253,1 225672 33456 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 |
||
| + | python3 2283 root 0u CHR 136,1 0t0 4 /dev/pts/1 |
||
| + | python3 2283 root 1u CHR 136,1 0t0 4 /dev/pts/1 |
||
| + | python3 2283 root 2u CHR 136,1 0t0 4 /dev/pts/1 |
||
| + | python3 2283 root 3u pack 72032 0t0 ALL type=SOCK_RAW |
||
| + | </pre> |
||
| + | |||
| + | ===systemd kasutamine network-proxy rollis=== |
||
| + | |||
| + | ====Tööpõhimõte==== |
||
| + | |||
| + | * kasutatakse ühte võrgu-vaba python skripti, mis ei tea ise võrgust mitte midagi, tema töötab read-write abstraktsiooni tasemel |
||
| + | * võrk tuuakse kohale inet, unix ja vsock socket abil - võrguklient ühendatakse kokku skripti read-write omadustega stdin, stdout abil |
||
| + | |||
| + | Tulemusena on sama python skripti väljund kolme erineva address family kaudu ligipääsetav |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl --type=socket | grep raw-w |
||
| + | raw-webserver-tcp.socket loaded active listening TCP/IP Socket for Border Web Server |
||
| + | raw-webserver-unix.socket loaded active listening Unix Domain Socket for Border Web Server |
||
| + | raw-webserver-vsock.socket loaded active listening VSOCK Memory Socket for Border Web Server |
||
| + | </pre> |
||
| + | |||
| + | ja |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl list-sockets | grep raw-w |
||
| + | [::]:1044 raw-webserver-tcp.socket - |
||
| + | vsock::1044 raw-webserver-vsock.socket - |
||
| + | /run/raw_webserver.sock raw-webserver-unix.socket - |
||
| + | </pre> |
||
| + | |||
| + | ====Ettevalmistused - vsock==== |
||
| + | |||
| + | vsock jaoks sobib kasutada nt Proxmox PVE v. 9.2 lahendust, virtuaalsele arvutile tuleb käsitsi lisada vsock teemaline pci seade |
||
| + | |||
| + | <pre> |
||
| + | root@pve-svc-02:~# grep arg /etc/pve/qemu-server/50194.conf |
||
| + | args: -device vhost-vsock-pci,guest-cid=50194 |
||
| + | </pre> |
||
| + | |||
| + | mis paistab virtuaalses arvutis töötavale operatsioonisüsteemile selliselt |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# lspci | grep -i socket |
||
| + | 00:02.0 Communication controller: Red Hat, Inc. Virtio 1.0 socket (rev 01) |
||
| + | </pre> |
||
| + | |||
| + | vsock sarnane nähtus esineb nt vmware puhul nö ammusest ajast, on tuntud nimega 'VMCI Sockets'. |
||
| + | |||
| + | ====Võrgu-vaba python skript==== |
||
| + | |||
| + | Kasutada on üks python nö võrgu-vaba skript /usr/local/bin/raw_webserver.py, mis teeb read ja write tegevusi, tavalisel viisil |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# cat /usr/local/bin/raw_webserver.py |
||
| + | #!/usr/bin/env python3 |
||
| + | import sys |
||
| + | import time |
||
| + | |||
| + | def main(): |
||
| + | # Keep an active buffer array for incoming chunks |
||
| + | request_bytes = b"" |
||
| + | |||
| + | # Read the stream line-by-line from stdin |
||
| + | while True: |
||
| + | line = sys.stdin.buffer.readline() |
||
| + | if not line: |
||
| + | break # Client disconnected |
||
| + | |||
| + | request_bytes += line |
||
| + | |||
| + | # An empty HTTP line (\r\n or \n) marks the absolute end of HTTP headers. |
||
| + | # The moment we see this, we MUST break out of reading, or we deadlock! |
||
| + | if line == b"\r\n" or line == b"\n": |
||
| + | break |
||
| + | |||
| + | if not request_bytes: |
||
| + | sys.exit(0) |
||
| + | |||
| + | # Log the first line to systemd journal for debugging |
||
| + | request_text = request_bytes.decode('utf-8', errors='ignore') |
||
| + | lines = request_text.split('\r\n') |
||
| + | if lines: |
||
| + | print(f"Received Request: {lines[0]}", file=sys.stderr) |
||
| + | |||
| + | # Manually build the HTML response payload |
||
| + | html_content = ( |
||
| + | "<html><body>" |
||
| + | "<h1>Hello from the Strict Read-Write Border!</h1>" |
||
| + | "<p>Systemd Socket Activation is fully online and functioning.</p>" |
||
| + | "</body></html>" |
||
| + | ) |
||
| + | |||
| + | http_response = ( |
||
| + | "HTTP/1.1 200 OK\r\n" |
||
| + | "Content-Type: text/html; charset=utf-8\r\n" |
||
| + | f"Content-Length: {len(html_content)}\r\n" |
||
| + | "Connection: close\r\n" |
||
| + | "\r\n" |
||
| + | f"{html_content}" |
||
| + | ) |
||
| + | |||
| + | # Write back to stdout (the socket) and flush the buffers out to the wire |
||
| + | time.sleep(8) |
||
| + | sys.stdout.buffer.write(http_response.encode('utf-8')) |
||
| + | sys.stdout.buffer.flush() |
||
| + | |||
| + | if __name__ == "__main__": |
||
| + | main() |
||
| + | </pre> |
||
| + | |||
| + | kus |
||
| + | |||
| + | * rida 'time.sleep(8)' abil saab sündmusi aeglustada, st jõuab ühes aknas päringu teha ja teises aknas vaadata, kuidas võrguühendus edeneb vms |
||
| + | |||
| + | ning lokaalselt käivitades |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# echo "sisend" | /usr/local/bin/raw_webserver.py |
||
| + | Received Request: sisend |
||
| + | |||
| + | HTTP/1.1 200 OK |
||
| + | Content-Type: text/html; charset=utf-8 |
||
| + | Content-Length: 140 |
||
| + | Connection: close |
||
| + | |||
| + | <html><body><h1>Hello from the Strict Read-Write Border!</h1><p>Systemd Socket Activation is fully online and functioning.</p></body></html> |
||
| + | root@zabbix-pub-01:~# |
||
| + | </pre> |
||
| + | |||
| + | ====The Shared Systemd Service Unit==== |
||
| + | |||
| + | Shared systemd service unit põhimõtteliselt võiks olla kõigi variatsioonide peale üks st ühe universaalse nimega, millegipärast sedasi ei õnnestunud praktiliselt |
||
| + | |||
| + | * /etc/systemd/system/raw-webserver-tcp@.service |
||
| + | * /etc/systemd/system/raw-webserver-unix@.service |
||
| + | * /etc/systemd/system/raw-webserver-vsock@.service |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# cat /etc/systemd/system/raw-webserver-tcp@.service |
||
| + | [Unit] |
||
| + | Description=Protocol-Blind Web Server Instance (Per-Connection) |
||
| + | After=network.target |
||
| + | |||
| + | [Service] |
||
| + | ExecStart=/usr/local/bin/raw_webserver.py |
||
| + | StandardInput=socket |
||
| + | StandardOutput=socket |
||
| + | StandardError=journal |
||
| + | # Automatically terminate the process when the read/write loop completes |
||
| + | Type=oneshot |
||
| + | </pre> |
||
| + | |||
| + | ====raw-webserver-tcp==== |
||
| + | |||
| + | Väited |
||
| + | |||
| + | * kasutada saab eemalt võrgust |
||
| + | |||
| + | Esitatud võrgu-vaba üldine pyhton skript ühendatakse systemd |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# cat /etc/systemd/system/raw-webserver-tcp.socket |
||
| + | [Unit] |
||
| + | Description=TCP/IP Socket for Border Web Server |
||
| + | |||
| + | [Socket] |
||
| + | # Bind to port 1044 on all available network cards (IPv4 and IPv6) |
||
| + | ListenStream=1044 |
||
| + | # Accept=yes spawns a unique instance of our @.service file per network connection |
||
| + | Accept=yes |
||
| + | |||
| + | [Install] |
||
| + | WantedBy=sockets.target |
||
| + | </pre> |
||
| + | |||
| + | kus |
||
| + | |||
| + | * raw-webserver-tcp nime alusel teab systemd kasutada raw-webserver-tcp@.service unitit |
||
| + | |||
| + | Aktiveerimine |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl enable --now raw-webserver-tcp.socket |
||
| + | </pre> |
||
| + | |||
| + | ja kasutamine |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl | grep raw-webser |
||
| + | raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service loaded activating start start Protocol-Blind Web Server Instance (Per-Connection) (192.168.10.194:36804) |
||
| + | system-raw\x2dwebserver.slice loaded active active Slice /system/raw-webserver |
||
| + | system-raw\x2dwebserver\x2dtcp.slice loaded active active Slice /system/raw-webserver-tcp |
||
| + | raw-webserver-tcp.socket loaded active listening TCP/IP Socket for Border Web Server |
||
| + | |||
| + | root@zabbix-pub-01:~# systemctl status raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service |
||
| + | ● raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service - Protocol-Blind Web Server Instance (Per-Connection) (192.168.10.194:36804) |
||
| + | Loaded: loaded (/etc/systemd/system/raw-webserver-tcp@.service; static) |
||
| + | Active: activating (start) since Sun 2026-05-31 19:46:42 EEST; 15s ago |
||
| + | TriggeredBy: ● raw-webserver-tcp.socket |
||
| + | Main PID: 13900 (python3) |
||
| + | Tasks: 1 (limit: 2115) |
||
| + | Memory: 4.2M (peak: 4.4M) |
||
| + | CPU: 17ms |
||
| + | CGroup: /system.slice/system-raw\x2dwebserver\x2dtcp.slice/raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service |
||
| + | └─13900 python3 /usr/local/bin/raw_webserver.py |
||
| + | </pre> |
||
| + | |||
| + | Klient pöördub |
||
| + | |||
| + | <pre> |
||
| + | # curl -v http://192.168.10.193:1044/ |
||
| + | </pre> |
||
| + | |||
| + | Statistika |
||
| + | |||
| + | <pre> |
||
| + | May 31 19:46:42 zabbix-pub-01 systemd[1]: Starting raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service - Protocol-Blind Web Server Instance (Per-Connection) (192.168.10.194:36804)... |
||
| + | May 31 19:46:42 zabbix-pub-01 raw_webserver.py[13900]: Received Request: GET / HTTP/1.1 |
||
| + | |||
| + | root@zabbix-pub-01:~# netstat -anpt | grep 1044 |
||
| + | tcp6 0 0 :::1044 :::* LISTEN 1/init |
||
| + | tcp6 0 0 192.168.10.193:1044 192.168.10.194:36804 ESTABLISHED 1/init |
||
| + | |||
| + | root@zabbix-pub-01:~# lsof -n -P -p 1 | grep :1044 |
||
| + | systemd 1 root 72u IPv6 1678876 0t0 TCP 192.168.10.193:1044->192.168.10.194:36804 (ESTABLISHED) |
||
| + | systemd 1 root 80u IPv6 1668028 0t0 TCP *:1044 (LISTEN) |
||
| + | </pre> |
||
| + | |||
| + | kus |
||
| + | |||
| + | * kasutaja pöördumisel systemd tekitab 'listening socket' eeskujul 'active connection socket'-i ('establised') |
||
| + | * kui mitu klienti pöörduvad, siis tekib süsteemi mitu .py skripti töötavas olekus |
||
| + | |||
| + | ====raw-webserver-unix==== |
||
| + | |||
| + | Väited |
||
| + | |||
| + | * kasutada saab kohalikust arvutist |
||
| + | |||
| + | Tekitada universaalne service shared fail (sama /etc/systemd/system/raw-webserver-tcp.socket sisuga) |
||
| + | |||
| + | /etc/systemd/system/raw-webserver-unix@.service |
||
| + | |||
| + | ning |
||
| + | |||
| + | <pre> |
||
| + | # cat /etc/systemd/system/raw-webserver-unix.socket |
||
| + | [Unit] |
||
| + | Description=Unix Domain Socket for Border Web Server |
||
| + | |||
| + | [Socket] |
||
| + | # ListenStream with an absolute path instructs systemd to create an AF_UNIX socket file |
||
| + | ListenStream=/run/raw_webserver.sock |
||
| + | # Accept=yes spawns a unique instance of our @.service file per local file connection |
||
| + | Accept=yes |
||
| + | |||
| + | [Install] |
||
| + | WantedBy=sockets.target |
||
| + | </pre> |
||
| + | |||
| + | Aktiveerimiseks |
||
| + | |||
| + | <pre> |
||
| + | # systemctl daemon-reload |
||
| + | # systemctl enable --now raw-webserver-unix.socket |
||
| + | </pre> |
||
| + | |||
| + | ja pöördumiseks samas arvutis |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# time curl --unix-socket /run/raw_webserver.sock http://localhost/ |
||
| + | <html><body><h1>Hello from the Strict Read-Write Border!</h1><p>Systemd Socket Activation is fully online and functioning.</p></body></html> |
||
| + | </pre> |
||
| + | |||
| + | Soket paistab nii, eelmise variandi oma on samuti nimekirjas |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl list-sockets | grep raw |
||
| + | [::]:1044 raw-webserver-tcp.socket - |
||
| + | /run/raw_webserver.sock raw-webserver-unix.socket - |
||
| + | </pre> |
||
| + | |||
| + | Päringu tegemise ajal |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# ss -a -p -f unix | grep raw |
||
| + | u_str LISTEN 0 4096 /run/raw_webserver.sock 1727463 * 0 users:(("systemd",pid=1,fd=79)) |
||
| + | u_str ESTAB 0 0 /run/raw_webserver.sock 1736013 * 1679109 users:(("python3",pid=14454,fd=1),("python3",pid=14454,fd=0),("systemd",pid=1,fd=10)) |
||
| + | </pre> |
||
| + | |||
| + | kus |
||
| + | |||
| + | * unix soketil on ka listen ja established |
||
| + | |||
| + | ====raw-webserver-vsock==== |
||
| + | |||
| + | Väited |
||
| + | |||
| + | * kasutada saab kõnealuse virtuaalse arvuti pve hostist |
||
| + | |||
| + | Tekitada universaalne service shared fail (sama /etc/systemd/system/raw-webserver-tcp.socket sisuga) |
||
| + | |||
| + | /etc/systemd/system/raw-webserver-vsock@.service |
||
| + | |||
| + | ning |
||
| + | |||
| + | <pre> |
||
| + | # cat /etc/systemd/system/raw-webserver-vsock.socket |
||
| + | [Unit] |
||
| + | Description=VSOCK Memory Socket for Border Web Server |
||
| + | |||
| + | [Socket] |
||
| + | # Bind strictly to the AF_VSOCK address family on port 1044 |
||
| + | ListenStream=vsock::1044 |
||
| + | Accept=yes |
||
| + | |||
| + | [Install] |
||
| + | WantedBy=sockets.target |
||
| + | </pre> |
||
| + | |||
| + | Aktiveerimiseks |
||
| + | |||
| + | <pre> |
||
| + | # systemctl daemon-reload |
||
| + | # systemctl enable --now raw-webserver-vsock.socket |
||
| + | </pre> |
||
| + | |||
| + | Kliendina pöördumiseks sobib nt socat programmi kasutada, küllap curl saab ka |
||
| + | |||
| + | <pre> |
||
| + | root@pve-svc-02:~# echo "GET /" | socat - VSOCK-CONNECT:50194:1044 |
||
| + | HTTP/1.1 200 OK |
||
| + | Content-Type: text/html; charset=utf-8 |
||
| + | Content-Length: 140 |
||
| + | Connection: close |
||
| + | |||
| + | <html><body><h1>Hello from the Strict Read-Write Border!</h1><p>Systemd Socket Activation is fully online and functioning.</p></body></html> |
||
| + | </pre> |
||
| + | |||
| + | Paistab |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl --type=socket | grep raw-w |
||
| + | raw-webserver-tcp.socket loaded active listening TCP/IP Socket for Border Web Server |
||
| + | raw-webserver-unix.socket loaded active listening Unix Domain Socket for Border Web Server |
||
| + | raw-webserver-vsock.socket loaded active listening VSOCK Memory Socket for Border Web Server |
||
| + | |||
| + | root@zabbix-pub-01:~# ss -a -p | grep 1044 |
||
| + | .. |
||
| + | v_str LISTEN 0 0 *:1044 *:* users:(("systemd",pid=1,fd=94)) |
||
| + | v_str ESTAB 0 0 50194:1044 2:3887152104 users:(("python3",pid=1604,fd=1),("python3",pid=1604,fd=0),("systemd",pid=1,fd=10)) |
||
| + | |||
| + | root@zabbix-pub-01:~# lsof -n -P -p 1684 |
||
| + | COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME |
||
| + | python3 1684 root cwd DIR 252,2 4096 2 / |
||
| + | python3 1684 root rtd DIR 252,2 4096 2 / |
||
| + | python3 1684 root txt REG 252,2 8020928 1406 /usr/bin/python3.12 |
||
| + | python3 1684 root mem REG 252,2 3055776 128 /usr/lib/locale/locale-archive |
||
| + | python3 1684 root mem REG 252,2 2125328 7621 /usr/lib/x86_64-linux-gnu/libc.so.6 |
||
| + | python3 1684 root mem REG 252,2 174336 7817 /usr/lib/x86_64-linux-gnu/libexpat.so.1.9.1 |
||
| + | python3 1684 root mem REG 252,2 113000 15564 /usr/lib/x86_64-linux-gnu/libz.so.1.3 |
||
| + | python3 1684 root mem REG 252,2 952616 7624 /usr/lib/x86_64-linux-gnu/libm.so.6 |
||
| + | python3 1684 root mem REG 252,2 27028 7610 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache |
||
| + | python3 1684 root mem REG 252,2 236616 7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 |
||
| + | python3 1684 root 0u sock 0,9 0t0 16838 protocol: AF_VSOCK |
||
| + | python3 1684 root 1u sock 0,9 0t0 16838 protocol: AF_VSOCK |
||
| + | python3 1684 root 2u unix 0xffff8a1a4527ec00 0t0 30427 type=STREAM (CONNECTED) |
||
| + | </pre> |
||
| + | |||
| + | kus |
||
| + | |||
| + | * python skript puhul on iseloomulikut avatud 0u ja 1u märkega af_vsock (st kuskil teises otsas on vsock, mitte et see oleks pythonile oluline, tema kasutab ikka read ja write syscallisid, aga kernel teab) |
||
| + | |||
| + | ====Misc==== |
||
| + | |||
| + | Ebaõnnestumiste eemaldamine |
||
| + | |||
| + | <pre> |
||
| + | root@zabbix-pub-01:~# systemctl | grep raw |
||
| + | ● raw-webserver-vsock@0-50194:1044-2:3887152079.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152079) |
||
| + | ● raw-webserver-vsock@1-50194:1044-2:3887152080.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152080) |
||
| + | ● raw-webserver-vsock@2-50194:1044-2:3887152081.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152081) |
||
| + | ● raw-webserver-vsock@3-50194:1044-2:3887152082.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152082) |
||
| + | |||
| + | root@zabbix-pub-01:~# systemctl reset-failed 'raw-webserver-vsock@*' |
||
| + | </pre> |
||
===Kasulikud lisamaterjalid=== |
===Kasulikud lisamaterjalid=== |
||
Viimane redaktsioon: 31. mai 2026, kell 23:49
Sissejuhatus
Käesolev tekst
- ei juhenda arvuti ip aadressi ja ruutingu seadistamist
- mõtiskleb, mis asi on võrguühendus
- mõtiskleb, kuidas on arvutis töötav protsess seotud võrguühendusega (ja vastupidi)
Mõisted
- berkeley sockets api - käesoleva teksti peamini sisu
- streams api - berkeley sockets api peamini alternatiiv - solaris ja co kasutavad
Tööpõhimõte
Teenuse pakkumise poolelt vaadates toimub seoses võrguühenduse teenindamisega selline sündmuste käik (välja on kirjutatud nn syscall'id)
SOCKET() -> BIND() -> LISTEN() -> ACCEPT() -> READ/WRITE() -> CLOSE()
kus erinevad tarkvaralised lahendused saavad jagada seda järgnevust erinevalt lõikudeks, nt
- üks netcat protsess tegeleb kõigi teemadega ise
- postgres isand protsess tegeleb kuni accept juurde, edasi moodustatakse uus protsess ning see töötab lõpuks 'active connected socket' olukorraga
- sshd/init (ubuntu 24.04) - esialgu kuulab võrgus init protsess port 22/tcp, st on listen olekus, kui saabub esimene pöördumine tuuakse juurde sshd protsess, listen antakse üle (sd_listen_fds tehnika abil)
- võrguühenduse töötamine on seotud tavaliselt user-space protsessi töötamisega
Kliendi poolt vaadates
SOCKET() -> CONNECT() -> READ/WRITE() -> CLOSE()
Väited
- toodud nähtusi saab omavahel kombineerida (nt nginx-reuseport režiimis kasutab lisaks 'sshd/init' lähenemise elemente)
- kui toimub võrguühendus, siis serveri poolelt osaleb kaks erinevat socketit: 1. listening socket, 2. active connected socket
- socket esineb kerneli mälu struktuuri üksusena (sinna on üles kirjutatud metaandmed, nt port number, aga ka andmetega seotud queue'd)
Selleks, et protsess saaks kasutada võrku ületab ta nö user-space -> kernel-space horisondi syscallide abil, ja neid ei ole palju, nt (mõni on ühine nii kliendile kui serverile, nt 'socket()', mõni iseloomulik serverile (nt listen()), mõni iseloomulik kliendile (nt connect())
- socket()
- bind()
- listen()
- accept()
- connect()
- read()
- write()
- epoll()
- poll()
- close()
Listening-socket ja active-connected-socket objektidega toimuv on sarnane protsessiga toimuvale forkimise (clone()) olukorras
- pidavalt on süsteemis olemas nö parent nähtus: vastavalt 1. listen socket, 2. parent protsess
- teatud olukorras toimub uue objekti tekkimine: 1. saabuva võrguühenduse teenindamisega moodustatakse listen socket alusel koopia; 2. uue protsessi vajadusel olemasolev protsess moodustab endast koopia ja täidab koodi osa mälus uue tarkvara st programmi sisuga)
Võrguühenduse vaatlemine - netcat
Serveri poolel on lähtepunktiks ainult nn 'listening socket', peale netcat protsessi käivitamist ütleb protsess kernelile kolm syscall'i
- socket() - nö üksuse loomine
- bind() - üksuse omaduste täpsustamine
- listen() - täiendav üksuse omaduste täpsustamine
- accept() - kliendi ootamine, selle tulemusena minnakse moodustama uut nn socket üksust, ja vana jääb ka alles
tulemusena paistab (reaalset grep väljundis accept4 ei ole paista)
terminal 1 - server-01# strace netcat -l 6001 2>&1 | egrep "socket|setsockopt|bind|listen|accept"
..
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
bind(3, {sa_family=AF_INET, sin_port=htons(6001), sin_addr=inet_addr("0.0.0.0")}, 16) = 0
listen(3, 1) = 0
accept4(...
...
terminal 2 - server-01# netstat -anpt | grep netcat
tcp 0 0 0.0.0.0:6001 0.0.0.0:* LISTEN 21473/netcat
terminal 2 - server-01# lsof -n -P -p 21473
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 21473 root cwd DIR 252,2 4096 8193 /root
netcat 21473 root rtd DIR 252,2 4096 2 /
netcat 21473 root txt REG 252,2 39560 841 /usr/bin/nc.openbsd
netcat 21473 root mem REG 252,2 2125328 7621 /usr/lib/x86_64-linux-gnu/libc.so.6
netcat 21473 root mem REG 252,2 55536 15411 /usr/lib/x86_64-linux-gnu/libmd.so.0.1.0
netcat 21473 root mem REG 252,2 68104 8515 /usr/lib/x86_64-linux-gnu/libresolv.so.2
netcat 21473 root mem REG 252,2 80888 15290 /usr/lib/x86_64-linux-gnu/libbsd.so.0.12.1
netcat 21473 root mem REG 252,2 236616 7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
netcat 21473 root 0u CHR 136,7 0t0 10 /dev/pts/7
netcat 21473 root 1u CHR 136,7 0t0 10 /dev/pts/7
netcat 21473 root 2u CHR 136,7 0t0 10 /dev/pts/7
netcat 21473 root 3u IPv4 1514112 0t0 TCP *:6001 (LISTEN)
kus
- lsof käsk esitab userspace protsessiga seotud avatud failide nimekirja (muu hulgas kuulub nimekirja võrgusoketiga seotud võrguühenduse inode 1514112
- 1514112 on operatsioonisüsteemis unikaalne globaalne võrgusoketi inode number (ino)
- fd 0u, 1u ja 2u on standardse tähendusega, vastavalt - stdin, stdout ja stderr
- 3u on protsessi kontekstis unikaalne file descriptor väärus (3r või 4w oleksid põhimõtteliselt read ja write, u on universal st mõlemad)
- tegu on listening soketiga, talle vastab mälueraldis jms
- soket asub ise kernel space'is, aga userspace'i protsessil on temaga kontakt fd abil (selle suhtes teeb protess nt read() ja write() syscall'isid
- accept() syscall on pooleldi täidetud, st ta ootab võrgust connect() syscall'ile vastavat pöördumist
Peale kliendi ühenduse alustamist st kui ühendus toimub, paistab kliendi arvutis syscallid
- socket
- setsockopt
- connect
terminal 1 - klient-01# # strace netcat 192.168.10.193 6001 2>&1 | egrep "socket|setsockopt|connect"
..
socket(AF_INET, SOCK_STREAM, IPPROTO_TCP) = 3
setsockopt(3, SOL_SOCKET, SO_REUSEADDR, [1], 4) = 0
setsockopt(3, SOL_SOCKET, SO_REUSEPORT, [1], 4) = 0
connect(3, {sa_family=AF_INET, sin_port=htons(6001), sin_addr=inet_addr("192.168.10.193")}, 16) = 0
...
terminal 2 - klient-01# netstat -anpt | grep netcat
tcp 0 0 192.168.10.196:58384 192.168.10.193:6001 ESTABLISHED 345883/netcat
terminal 2 - klient-01# lsof -n -P -p 345883
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
netcat 345883 root cwd DIR 253,2 4096 14 /root
netcat 345883 root rtd DIR 253,2 4096 2 /
netcat 345883 root txt REG 253,2 35032 419036 /usr/bin/nc.traditional
netcat 345883 root mem REG 253,2 1999312 403434 /usr/lib/x86_64-linux-gnu/libc.so.6
netcat 345883 root mem REG 253,2 225600 403431 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
netcat 345883 root 0u CHR 136,2 0t0 5 /dev/pts/2
netcat 345883 root 1u CHR 136,2 0t0 5 /dev/pts/2
netcat 345883 root 2u CHR 136,2 0t0 5 /dev/pts/2
netcat 345883 root 3u IPv4 29390854 0t0 TCP 192.168.10.196:58384->192.168.10.193:6001 (ESTABLISHED)
kus
- üks active connected socket oma mälueraldisega kernelis
- 29390854 on operatsioonisüsteemi jaoks globaalselt unikaalne inode väärtus (ino)
ja serveris kaks võrgusoketit nüüd, accept syscall sai valmis (tema tegevuse tulemusena ilmus file descriptor 4u
terminal 2 - server-01# netstat -anpt | grep netcat tcp 0 0 0.0.0.0:6001 0.0.0.0:* LISTEN 21473/netcat tcp 0 0 192.168.10.193:6001 192.168.10.196:58384 ESTABLISHED 21473/netcat terminal 2 - server-01# lsof -n -P -p 21473 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME netcat 21473 root cwd DIR 252,2 4096 8193 /root netcat 21473 root rtd DIR 252,2 4096 2 / netcat 21473 root txt REG 252,2 39560 841 /usr/bin/nc.openbsd netcat 21473 root mem REG 252,2 2125328 7621 /usr/lib/x86_64-linux-gnu/libc.so.6 netcat 21473 root mem REG 252,2 55536 15411 /usr/lib/x86_64-linux-gnu/libmd.so.0.1.0 netcat 21473 root mem REG 252,2 68104 8515 /usr/lib/x86_64-linux-gnu/libresolv.so.2 netcat 21473 root mem REG 252,2 80888 15290 /usr/lib/x86_64-linux-gnu/libbsd.so.0.12.1 netcat 21473 root mem REG 252,2 236616 7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 netcat 21473 root 0u CHR 136,7 0t0 10 /dev/pts/7 netcat 21473 root 1u CHR 136,7 0t0 10 /dev/pts/7 netcat 21473 root 2u CHR 136,7 0t0 10 /dev/pts/7 netcat 21473 root 3u IPv4 1514112 0t0 TCP *:6001 (LISTEN) netcat 21473 root 4u IPv4 1514113 0t0 TCP 192.168.10.193:6001->192.168.10.196:58384 (ESTABLISHED)
- listening socket oma mälueraldisega jms
- active connected socket oma mälueraldisega
- võrgusoketi inode numbrid on soketitel erinevad - 1514112 ja 1514113
- lisaks tekkinud file descriptor 4u on read() ja write() syscallide poolt kasutatav
- asjaolu, et võrguühendusega on seotud fd nii nagu tavalise failisüsteemi faili puhul kinnitab asjaolu, et in-unix-everything-is-a-file
Väited
- serveri poolel olev listen-socket ülesseadmine on valmisoleku tekitamine sisenevate võrguühenduste teenindamiseks
- kui accept syscall lõpetab, siis listen-socket on nagu ta ikka on edasi, lisaks on tekkinud juurde teine ühendusega tegelev soket
- listen-socket ja active-connection-soket suhe on võrreldab protsessidega operatsioonisüsteemis: parent ja child kuid sellise täpsustusega, et parent sünnitab ainult nö poegi st kes edasi ise ei suuda sünnitada; parent protsessi poolt tekitatud child protsess reeglina saab omakorda tekitada child protsessi, st protsess tekitab tütreid
- šovinist võiks võrrelda võrguühenduse klienti nö poeglapsega ja serverit tütarlapsega
Võrguühenduse variatsioonid
Väited
- võrguühendusega tegeleb kõige lihtsamal juhtumil üks ja sama protsess (non-threaded, netcat juhtum)
- võrguühendusega tegeleb üks threaded protsess
- võrguühendusega tegeleb protsesside komplekt (nt reuseport, sd_listen_fds)
Võrguühenduse vaatlemine - python skript
TODO
sd_listen_fds - sshd
sd_listen_fds (sd nagu systemd) nähtus võimaldab nt sellist asjakorraldust, mis toimub Ubuntu v. 24.04 operatsioonisüsteemis vaikimisi sshd serveriga
- kui keegi pole sshd abil süsteemi sisse loginud, siis port 22 peal kuulab systemd komplekti kuuluva programm 'init' (analoogne kunagise xinitd vms nn internet superserveriga) - ssh.socket unit juhtimisel
- kui keegi pole sshd kaudu süsteemi sisse loginud, siis sshd protsesse süsteemis ei töötagi veel (arvutusressusi kokkuhoiu eesmärgil)
- kui toimub esimene pöördumine, siis systemd käivitab ssh.socket unit abil teise system unit'i ssh.service unit abil sshd protsessi enda kusjuures andes üle listen tegevusega seotud failideskriptori (see 9999 port nähtus ei puutu otseselt asjasse, see on üks nö trikk mille abil saab süsteemi eemalt sisse logida minnes mööda tavalisest, st vastasel korral ei saaks süsteemi hästi vaadelda)
root@zabbix-pub-01:/# netstat -anpt Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name .. tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1/init tcp6 0 0 :::22 :::* LISTEN 1/init ... tcp6 0 0 192.168.10.193:9999 192.168.10.156:59522 ESTABLISHED 1/init root@zabbix-pub-01:/# ps -ef | egrep "init|ssh" UID PID PPID C STIME TTY TIME CMD root 1 0 8 17:06 ? 00:00:01 /sbin/init
kus
- nn bootshell viguriga (vt TODO) on nö kõrvalt neutraalselt sisse logitud
Peale sisselogimist
root@zabbix-pub-01:/# netstat -anpt Active Internet connections (servers and established) Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN 1/init tcp 0 0 192.168.10.193:22 192.168.10.156:60079 ESTABLISHED 1037/sshd: root@pts tcp6 0 0 :::22 :::* LISTEN 1/init tcp6 0 0 :::9999 :::* LISTEN 1/init tcp6 0 0 192.168.10.193:9999 192.168.10.156:59522 ESTABLISHED 1/init root@zabbix-pub-01:/# ps -ef | egrep "init|ssh" UID PID PPID C STIME TTY TIME CMD root 1 0 0 17:06 ? 00:00:01 /sbin/init root 1035 1 0 17:08 ? 00:00:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups root 1037 1035 0 17:08 ? 00:00:00 sshd: root@pts/0
kus
- init protsess on "sshd -D ..." parent
- "sshd -D ..." on "sshd: root@pts/0" parent
- sisselogitud oleks on seotud protsessiga "sshd: root@pts/0"
- kui toimub veel sisselogimisi, siis moodustub "sshd: root@pts/0" kõrvale nt "sshd: root@pts/1"
- netstat väljund selles mõttes petab, et ta ütleb, et tema kuulab port 22, aga selleks ajaks pigem tegeleb võrgus kuulamisega juhba 'sshd -D ...' protsess
seejuures
root@zabbix-pub-01:~# lsof -n -P -p 1 | grep "TCP \*:22" | grep LISTE COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME systemd 1 root 94u IPv4 6904 0t0 TCP *:22 (LISTEN) systemd 1 root 95u IPv6 4076 0t0 TCP *:22 (LISTEN) root@zabbix-pub-01:~# lsof -n -P -p 1035 | grep TCP COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME sshd 1035 root 3u IPv4 6904 0t0 TCP *:22 (LISTEN) sshd 1035 root 4u IPv6 4076 0t0 TCP *:22 (LISTEN)
st
- init protsess ja "sshd -D .." protsess jagavad omavahel port 22 osas võrgusoketi globaalset inode'i - inet -> "sshd -D .." suunal toimus sd_listen_fds
systemd unitite poolelt töötavad selle nähtusega kaks unitit
- ssh.socket
- ssh.service
kus
root@zabbix-pub-01:~# systemctl status ssh.socket
● ssh.socket - OpenBSD Secure Shell server socket
Loaded: loaded (/usr/lib/systemd/system/ssh.socket; enabled; preset: enabled)
Drop-In: /etc/systemd/system/ssh.socket.d
└─override.conf
Active: active (running) since Thu 2026-05-28 17:06:29 EEST; 10min ago
Triggers: ● ssh.service
Listen: 0.0.0.0:22 (Stream)
[::]:22 (Stream)
Tasks: 0 (limit: 2115)
Memory: 12.0K (peak: 512.0K)
CPU: 1ms
CGroup: /system.slice/ssh.socket
May 28 17:06:29 zabbix-pub-01 systemd[1]: Listening on ssh.socket - OpenBSD Secure Shell server socket.
root@zabbix-pub-01:~# cat /lib/systemd/system/ssh.socket
[Unit]
Description=OpenBSD Secure Shell server socket
Before=sockets.target ssh.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Socket]
ListenStream=0.0.0.0:22
ListenStream=[::]:22
BindIPv6Only=ipv6-only
Accept=no
FreeBind=yes
[Install]
WantedBy=sockets.target
RequiredBy=ssh.service
root@zabbix-pub-01:~#
kus
- BindIPv6Only=ipv6-only - port 22 jaoks on eraldatud kaks listen soketit st tehtud kaks mälueraldist: 1. ipv4 jaoks, 2. ipv6 jaoks
ning
root@zabbix-pub-01:~# systemctl status ssh.service
● ssh.service - OpenBSD Secure Shell server
Loaded: loaded (/usr/lib/systemd/system/ssh.service; disabled; preset: enabled)
Drop-In: /etc/systemd/system/ssh.service.d
└─override.conf
Active: active (running) since Thu 2026-05-28 17:08:45 EEST; 9min ago
TriggeredBy: ● ssh.socket
Docs: man:sshd(8)
man:sshd_config(5)
Process: 1034 ExecStartPre=/usr/sbin/sshd -t (code=exited, status=0/SUCCESS)
Main PID: 1035 (sshd)
Tasks: 1 (limit: 2115)
Memory: 5.3M (peak: 5.9M)
CPU: 39ms
CGroup: /system.slice/ssh.service
└─1035 "sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups"
May 28 17:08:45 zabbix-pub-01 systemd[1]: Starting ssh.service - OpenBSD Secure Shell server...
May 28 17:08:45 zabbix-pub-01 sshd[1035]: Server listening on 0.0.0.0 port 22.
May 28 17:08:45 zabbix-pub-01 sshd[1035]: Server listening on :: port 22.
May 28 17:08:45 zabbix-pub-01 systemd[1]: Started ssh.service - OpenBSD Secure Shell server.
May 28 17:08:45 zabbix-pub-01 sshd[1037]: Accepted publickey for root from 192.168.10.156 port 60079 ssh2: ED25519 SHA256:3cj7QCk4leNOSQJlfeUeHr6Y>
May 28 17:08:45 zabbix-pub-01 sshd[1037]: pam_unix(sshd:session): session opened for user root(uid=0) by root(uid=0)
root@zabbix-pub-01:~# cat /lib/systemd/system/ssh.service
[Unit]
Description=OpenBSD Secure Shell server
Documentation=man:sshd(8) man:sshd_config(5)
After=network.target auditd.service
ConditionPathExists=!/etc/ssh/sshd_not_to_be_run
[Service]
EnvironmentFile=-/etc/default/ssh
ExecStartPre=/usr/sbin/sshd -t
ExecStart=/usr/sbin/sshd -D $SSHD_OPTS
ExecReload=/usr/sbin/sshd -t
ExecReload=/bin/kill -HUP $MAINPID
KillMode=process
Restart=on-failure
RestartPreventExitStatus=255
Type=notify
RuntimeDirectory=sshd
RuntimeDirectoryMode=0755
[Install]
WantedBy=multi-user.target
Alias=sshd.service
Väited
- ssh.socket sisaldab rida 'Accept=no' - accept syscall juures annab systemd tegevused üle init protsessilt "sshd -D ..." protsessile
- üleandmine toimub systemd poolt vastava xxx.service abil, st antud juhul ssh.service käivitamisega
- ssh.socket on enabletud, ssh.service iseensest ei ole enabletud - seda triggerdab ssh.socket
- selleks, et selline nähtus toimuks, peab nägema omajagu vaeva systemd osakond, ja sshd tarkvara peab olema ka omalt poolt ettevalmistatud sd_listen_fds abil tegevuse üleandmiseks
- tundub, et kui ssh.socket ja ssh.service on mõlemad enabletud, siis midagi hulle sellest ei juhtu, st süsteem on algkäivitumise järgselt sshd protsesside koosseisu seisukohalt samasuguses olekus nagu oleks esimene kasutaja sisse logimise järel; st init on näiliselt seotud port :22 osakonnaga ja 'sshd -D ...' sisuliselt
Reuseport
Osutub, et tcp/ip network stack Linux, Windows, BSD jt implementatsioonid võimaldavad teatud tingimustel mitmel protsessil korraga sama porti kasutada. Seda võimalust kontrollib 'so_reuseport' socket option ('so_').
Väited
- haproxy - kasutatakse vaikimisi
- nginx - vaikimisi ei kasutata, aga soovitatakse kasutada jõudluse suurendamise eesmärgil
- zabbix-agent2 - ei ole võimalik kasutada
Python skript
Illustratsiooniks reuseport omadust kasutav python skript
# cat socket-01-server.py
import socket
import os
import sys
PORT = 8083
# Fetch the Process ID so we can easily tell which instance answers the client
PID = os.getpid()
# 1. Create a standard TCP IPv4 Socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
# 2. THE MAGIC OPTIONS: Enable Reuse Address and Reuse Port
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
server_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEPORT, 1)
# 3. Bind and Listen (Standard blocking mode)
server_socket.bind(('0.0.0.0', PORT))
server_socket.listen(10)
print(f"[Worker PID: {PID}] Multi-core listening gate opened on port {PORT}...")
try:
while True:
# This call BLOCKS completely. The script sleeps right here in the kernel
# until the Linux 4-tuple hash decides to route a client to THIS specific socket instance.
client_socket, client_address = server_socket.accept()
# Read the raw incoming HTTP request payload (up to 4KB)
request = client_socket.recv(4096)
if request:
# Construct a basic HTTP response containing the answering process ID
body = f"Hello from Worker Process ID: {PID}\n"
response = (
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/plain\r\n"
f"Content-Length: {len(body)}\r\n"
"Connection: close\r\n"
"\r\n"
f"{body}"
).encode('utf-8')
# Fire the response back to the client
client_socket.sendall(response)
# Close the socket immediately to return the client line
client_socket.close()
except KeyboardInterrupt:
print(f"\n[Worker {PID}] Shutting down down gracefully.")
finally:
server_socket.close()
kus
- TODO
Väited
- sellist skripti saab käivitada mitu eksemplari ühes arvutis - kõik kuulavad ja teenindavad samal pordil
Nginx ja haproxy
reuseport nähtust saab illustreerida käivitades samal pordil nginx ja haproxy protsessid, haproxy seadistus
root@zabbix-pub-01:~# cat /etc/haproxy/haproxy.cfg
global
log /dev/log local0
chroot /var/lib/haproxy
user haproxy
group haproxy
daemon
# no-reuseport
nbthread 1
# Modern HAProxy thread configuration:
# This automatically spawns workers matching your CPU count.
# To strictly witness the kernel routing traffic across different internal
# buckets, HAProxy pairs this natively with SO_REUSEPORT by default.
stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
defaults
log global
mode http
option httplog
option dontlognull
timeout connect 5000
timeout client 50000
timeout server 50000
# =====================================================================
# THE EXPERIMENTAL REUSEPORT FRONTEND
# =====================================================================
frontend reuseport_experiment
bind 0.0.0.0:80
# We will use HAProxy's internal sample fetches to append the
# handling Thread ID directly into a custom HTTP response header!
http-after-response set-header X-Handling-Thread "%[thread]"
default_backend echo_back
backend echo_back
# A simple trick: We use HAProxy's internal stats app to act as an
# instant mock HTTP responder, so you don't need a backend server running.
http-request return status 200 content-type "text/plain" string "Hello from HAProxy! Connection processed successfully.\n"
ning nginx seadistus
root@zabbix-pub-01:~# cat /etc/nginx/sites-enabled/default
##
# You should look at the following URL's in order to grasp a solid understanding
# of Nginx configuration files in order to fully unleash the power of Nginx.
# https://www.nginx.com/resources/wiki/start/
# https://www.nginx.com/resources/wiki/start/topics/tutorials/config_pitfalls/
# https://wiki.debian.org/Nginx/DirectoryStructure
#
# In most cases, administrators will remove this file from sites-enabled/ and
# leave it as reference inside of sites-available where it will continue to be
# updated by the nginx packaging team.
#
# This file will automatically load configuration files provided by other
# applications, such as Drupal or Wordpress. These applications will be made
# available underneath a path with that package name, such as /drupal8.
#
# Please see /usr/share/doc/nginx-doc/examples/ for more detailed examples.
##
# Default server configuration
#
server {
listen 80 reuseport;
# listen [::]:8082 reuseport;
root /var/www/html;
# Add index.php to the list if you are using PHP
index index.html index.htm index.nginx-debian.html;
server_name _;
location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
}
tulemus paistab serveris välja
root@zabbix-pub-01:~# netstat -lnpt | grep :80 tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1520/nginx: master tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 1513/haproxy root@zabbix-pub-01:~# lsof -n -P -p 1513 | grep TCP haproxy 1513 haproxy 9u IPv4 12976 0t0 TCP *:80 (LISTEN) root@zabbix-pub-01:~# lsof -n -P -p 1520 | grep TCP nginx 1520 root 5u IPv4 16719 0t0 TCP *:80 (LISTEN)
- erinevad listen socket inode numbrid
- ei ole üksteisest põlvnevat suhet
ja kliendi poolt vaadates
root@zabbix-01:~# curl http://192.168.10.193/ Hello from HAProxy! Connection processed successfully. root@zabbix-01:~# curl http://192.168.10.193/ Hello from HAProxy! Connection processed successfully. root@zabbix-01:~# curl http://192.168.10.193/ Welcome to nginx! root@zabbix-01:~# curl http://192.168.10.193/ Hello from HAProxy! Connection processed successfully. root@zabbix-01:~# curl http://192.168.10.193/ Welcome to nginx! root@zabbix-01:~# curl http://192.168.10.193/ Welcome to nginx!
Väited
- nö load balancing toimub kerneli poolt tehtud hash'ing vms abil (seda võiks saada üle kirjutada eBPF programmiga tõenäoliselt)
Turvalisus
Väidetavalt ei ole reuseport realistlik ründevektor kuigi tal võiks teoreetiliselt olla potentsiaali
- ta on populaarne interneti ühendatud infosüsteemi komponendi puhul (nginx, haproxy, apache, nsd, unbound)
- ta võimaldab nö ausa teenust pakkuva pordi külge sokutada midagi, ja teenuse ees olev tulemüür laseb liikluse ilmselt läbi
Ruuter
Väited
- ruuter puhul ei ole tegevused seotud arvutis töötava protsessiga
- ruuteris võrgus liikuvad andmed ei lahku kernel-space'ist
af_packet kasutamise näide
af_packet kihis töötamine on kõige madalam nö ausal viisil võrgu kasutamine st user-space'ist (edasi tuleks otse kernelis toimetada, nt kerneli mooduli abil, ebpf programmiga vms). Looduses esinevad näited
- dhcp
- synology assistant
- mikrotik winbox ilma ip aadressita kliendiga st wifi router vms seadmega
Väited
- töötab madalamas kihis kui nö tavalised paketifiltri tulemüürid (iptables, nft), st nendega ei saa takistada af_packet viisil koheldavat liiklust (ei kliendi ega serveri poolel)
Server
root@ph-minio-01:~# cat packet_server.py
import socket
import struct
def run_server():
# 0x0003 tells the kernel to capture ALL incoming protocol frames
ETH_P_ALL = 0x0003
# Create the Layer 2 AF_PACKET socket
# socket.htons converts the protocol integer to network byte order
server_sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW, socket.htons(ETH_P_ALL))
# Bind the socket strictly to the loopback interface
server_sock.bind(("lo", 0))
print("Layer 2 AF_PACKET Server listening on interface 'lo'...")
while True:
# Receive the raw Ethernet frame (buffer size 65535 bytes)
raw_frame, addr = server_sock.recvfrom(65535)
# An Ethernet header is exactly 14 bytes:
# Destination MAC (6 bytes) + Source MAC (6 bytes) + EtherType (2 bytes)
eth_header = raw_frame[:14]
payload = raw_frame[14:]
# Unpack the 14-byte header using the struct module:
# !6s6sH means: Network order, 6 bytes, 6 bytes, unsigned short (2 bytes)
dest_mac, src_mac, eth_type = struct.unpack("!6s6sH", eth_header)
# Custom EtherType check (We'll use 0x7A7A for our custom test app)
if eth_type == 0x7A7A:
print("\n--- [New Raw Frame Captured] ---")
# Convert MAC address bytes to human-readable hex strings
print(f"Destination MAC : {dest_mac.hex(':')}")
print(f"Source MAC : {src_mac.hex(':')}")
print(f"EtherType (Hex) : {hex(eth_type)}")
try:
print(f"Payload Data : {payload.decode('utf-8')}")
except UnicodeDecodeError:
print(f"Payload Data : {payload}")
if __name__ == "__main__":
run_server()
klient
root@ph-minio-01:~# cat packet_client.py
import socket
import struct
def run_client():
# Create the Layer 2 AF_PACKET socket
client_sock = socket.socket(socket.AF_PACKET, socket.SOCK_RAW)
# Bind to the loopback interface
client_sock.bind(("lo", 0))
# Define our raw addresses
# Because it's loopback ('lo'), the MAC addresses are always 6 bytes of 0x00
LOOPBACK_MAC = b'\x00\x00\x00\x00\x00\x00'
dest_mac = LOOPBACK_MAC
src_mac = LOOPBACK_MAC
# Define a custom EtherType protocol ID (0x7A7A).
# Standard values are things like 0x0800 (IPv4) or 0x86DD (IPv6).
# Using a custom ID guarantees the Linux kernel's internal TCP/IP stack
# will ignore our packet, leaving it entirely for our server script to read!
eth_type = 0x7A7A
# Craft the payload text
message = "Hello from the raw AF_PACKET layer!"
payload = message.encode('utf-8')
# Manually assemble the 14-byte Ethernet Header
# !6s6sH = Network byte order, 6-byte string, 6-byte string, unsigned short integer
eth_header = struct.pack("!6s6sH", dest_mac, src_mac, eth_type)
# Combine the header and payload into one single raw binary frame
full_frame = eth_header + payload
print(f"Sending raw frame ({len(full_frame)} bytes) directly onto 'lo'...")
# Inject the frame onto the wire
client_sock.send(full_frame)
print("Frame injected successfully.")
if __name__ == "__main__":
run_client()
Kasutamine
root@ph-minio-01:~# python3 packet_client.py Sending raw frame (49 bytes) directly onto 'lo'... Frame injected successfully. root@ph-minio-01:~# root@ph-minio-01:~# tcpdump -ni lo tcpdump: verbose output suppressed, use -v[v]... for full protocol decode listening on lo, link-type EN10MB (Ethernet), snapshot length 262144 bytes 18:08:12.071780 00:00:00:00:00:00 > 00:00:00:00:00:00, ethertype Unknown (0x7a7a), length 49: 0x0000: 4865 6c6c 6f20 6672 6f6d 2074 6865 2072 Hello.from.the.r 0x0010: 6177 2041 465f 5041 434b 4554 206c 6179 aw.AF_PACKET.lay 0x0020: 6572 21 er!
Süsteemist sellisel viisil töötavad server programmi on keeruline avastada, nt tavaline 'netstat -lnp' ei näita, aga nt ss programm näitab
Netid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
p_raw UNCONN 0 0 *:lo * users:(("python3",pid=2283,fd=3))
ning
root@ph-minio-01:~# lsof -n -P -p 2283 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME python3 2283 root cwd DIR 253,1 4096 130524 /root python3 2283 root rtd DIR 253,1 4096 2 / python3 2283 root txt REG 253,1 6828688 21266 /usr/bin/python3.13 python3 2283 root mem REG 253,1 3063024 8431 /usr/lib/locale/locale-archive python3 2283 root mem REG 253,1 27028 33448 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache python3 2283 root mem REG 253,1 1995216 33459 /usr/lib/x86_64-linux-gnu/libc.so.6 python3 2283 root mem REG 253,1 178272 20782 /usr/lib/x86_64-linux-gnu/libexpat.so.1.10.2 python3 2283 root mem REG 253,1 125376 2298 /usr/lib/x86_64-linux-gnu/libz.so.1.3.1 python3 2283 root mem REG 253,1 977112 33462 /usr/lib/x86_64-linux-gnu/libm.so.6 python3 2283 root mem REG 253,1 225672 33456 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 python3 2283 root 0u CHR 136,1 0t0 4 /dev/pts/1 python3 2283 root 1u CHR 136,1 0t0 4 /dev/pts/1 python3 2283 root 2u CHR 136,1 0t0 4 /dev/pts/1 python3 2283 root 3u pack 72032 0t0 ALL type=SOCK_RAW
systemd kasutamine network-proxy rollis
Tööpõhimõte
- kasutatakse ühte võrgu-vaba python skripti, mis ei tea ise võrgust mitte midagi, tema töötab read-write abstraktsiooni tasemel
- võrk tuuakse kohale inet, unix ja vsock socket abil - võrguklient ühendatakse kokku skripti read-write omadustega stdin, stdout abil
Tulemusena on sama python skripti väljund kolme erineva address family kaudu ligipääsetav
root@zabbix-pub-01:~# systemctl --type=socket | grep raw-w raw-webserver-tcp.socket loaded active listening TCP/IP Socket for Border Web Server raw-webserver-unix.socket loaded active listening Unix Domain Socket for Border Web Server raw-webserver-vsock.socket loaded active listening VSOCK Memory Socket for Border Web Server
ja
root@zabbix-pub-01:~# systemctl list-sockets | grep raw-w [::]:1044 raw-webserver-tcp.socket - vsock::1044 raw-webserver-vsock.socket - /run/raw_webserver.sock raw-webserver-unix.socket -
Ettevalmistused - vsock
vsock jaoks sobib kasutada nt Proxmox PVE v. 9.2 lahendust, virtuaalsele arvutile tuleb käsitsi lisada vsock teemaline pci seade
root@pve-svc-02:~# grep arg /etc/pve/qemu-server/50194.conf args: -device vhost-vsock-pci,guest-cid=50194
mis paistab virtuaalses arvutis töötavale operatsioonisüsteemile selliselt
root@zabbix-pub-01:~# lspci | grep -i socket 00:02.0 Communication controller: Red Hat, Inc. Virtio 1.0 socket (rev 01)
vsock sarnane nähtus esineb nt vmware puhul nö ammusest ajast, on tuntud nimega 'VMCI Sockets'.
Võrgu-vaba python skript
Kasutada on üks python nö võrgu-vaba skript /usr/local/bin/raw_webserver.py, mis teeb read ja write tegevusi, tavalisel viisil
root@zabbix-pub-01:~# cat /usr/local/bin/raw_webserver.py
#!/usr/bin/env python3
import sys
import time
def main():
# Keep an active buffer array for incoming chunks
request_bytes = b""
# Read the stream line-by-line from stdin
while True:
line = sys.stdin.buffer.readline()
if not line:
break # Client disconnected
request_bytes += line
# An empty HTTP line (\r\n or \n) marks the absolute end of HTTP headers.
# The moment we see this, we MUST break out of reading, or we deadlock!
if line == b"\r\n" or line == b"\n":
break
if not request_bytes:
sys.exit(0)
# Log the first line to systemd journal for debugging
request_text = request_bytes.decode('utf-8', errors='ignore')
lines = request_text.split('\r\n')
if lines:
print(f"Received Request: {lines[0]}", file=sys.stderr)
# Manually build the HTML response payload
html_content = (
"<html><body>"
"<h1>Hello from the Strict Read-Write Border!</h1>"
"<p>Systemd Socket Activation is fully online and functioning.</p>"
"</body></html>"
)
http_response = (
"HTTP/1.1 200 OK\r\n"
"Content-Type: text/html; charset=utf-8\r\n"
f"Content-Length: {len(html_content)}\r\n"
"Connection: close\r\n"
"\r\n"
f"{html_content}"
)
# Write back to stdout (the socket) and flush the buffers out to the wire
time.sleep(8)
sys.stdout.buffer.write(http_response.encode('utf-8'))
sys.stdout.buffer.flush()
if __name__ == "__main__":
main()
kus
- rida 'time.sleep(8)' abil saab sündmusi aeglustada, st jõuab ühes aknas päringu teha ja teises aknas vaadata, kuidas võrguühendus edeneb vms
ning lokaalselt käivitades
root@zabbix-pub-01:~# echo "sisend" | /usr/local/bin/raw_webserver.py Received Request: sisend HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 140 Connection: close <html><body><h1>Hello from the Strict Read-Write Border!</h1><p>Systemd Socket Activation is fully online and functioning.</p></body></html> root@zabbix-pub-01:~#
Shared systemd service unit põhimõtteliselt võiks olla kõigi variatsioonide peale üks st ühe universaalse nimega, millegipärast sedasi ei õnnestunud praktiliselt
- /etc/systemd/system/raw-webserver-tcp@.service
- /etc/systemd/system/raw-webserver-unix@.service
- /etc/systemd/system/raw-webserver-vsock@.service
root@zabbix-pub-01:~# cat /etc/systemd/system/raw-webserver-tcp@.service [Unit] Description=Protocol-Blind Web Server Instance (Per-Connection) After=network.target [Service] ExecStart=/usr/local/bin/raw_webserver.py StandardInput=socket StandardOutput=socket StandardError=journal # Automatically terminate the process when the read/write loop completes Type=oneshot
raw-webserver-tcp
Väited
- kasutada saab eemalt võrgust
Esitatud võrgu-vaba üldine pyhton skript ühendatakse systemd
root@zabbix-pub-01:~# cat /etc/systemd/system/raw-webserver-tcp.socket [Unit] Description=TCP/IP Socket for Border Web Server [Socket] # Bind to port 1044 on all available network cards (IPv4 and IPv6) ListenStream=1044 # Accept=yes spawns a unique instance of our @.service file per network connection Accept=yes [Install] WantedBy=sockets.target
kus
- raw-webserver-tcp nime alusel teab systemd kasutada raw-webserver-tcp@.service unitit
Aktiveerimine
root@zabbix-pub-01:~# systemctl enable --now raw-webserver-tcp.socket
ja kasutamine
root@zabbix-pub-01:~# systemctl | grep raw-webser
raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service loaded activating start start Protocol-Blind Web Server Instance (Per-Connection) (192.168.10.194:36804)
system-raw\x2dwebserver.slice loaded active active Slice /system/raw-webserver
system-raw\x2dwebserver\x2dtcp.slice loaded active active Slice /system/raw-webserver-tcp
raw-webserver-tcp.socket loaded active listening TCP/IP Socket for Border Web Server
root@zabbix-pub-01:~# systemctl status raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service
● raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service - Protocol-Blind Web Server Instance (Per-Connection) (192.168.10.194:36804)
Loaded: loaded (/etc/systemd/system/raw-webserver-tcp@.service; static)
Active: activating (start) since Sun 2026-05-31 19:46:42 EEST; 15s ago
TriggeredBy: ● raw-webserver-tcp.socket
Main PID: 13900 (python3)
Tasks: 1 (limit: 2115)
Memory: 4.2M (peak: 4.4M)
CPU: 17ms
CGroup: /system.slice/system-raw\x2dwebserver\x2dtcp.slice/raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service
└─13900 python3 /usr/local/bin/raw_webserver.py
Klient pöördub
# curl -v http://192.168.10.193:1044/
Statistika
May 31 19:46:42 zabbix-pub-01 systemd[1]: Starting raw-webserver-tcp@6-192.168.10.193:1044-192.168.10.194:36804.service - Protocol-Blind Web Server Instance (Per-Connection) (192.168.10.194:36804)... May 31 19:46:42 zabbix-pub-01 raw_webserver.py[13900]: Received Request: GET / HTTP/1.1 root@zabbix-pub-01:~# netstat -anpt | grep 1044 tcp6 0 0 :::1044 :::* LISTEN 1/init tcp6 0 0 192.168.10.193:1044 192.168.10.194:36804 ESTABLISHED 1/init root@zabbix-pub-01:~# lsof -n -P -p 1 | grep :1044 systemd 1 root 72u IPv6 1678876 0t0 TCP 192.168.10.193:1044->192.168.10.194:36804 (ESTABLISHED) systemd 1 root 80u IPv6 1668028 0t0 TCP *:1044 (LISTEN)
kus
- kasutaja pöördumisel systemd tekitab 'listening socket' eeskujul 'active connection socket'-i ('establised')
- kui mitu klienti pöörduvad, siis tekib süsteemi mitu .py skripti töötavas olekus
raw-webserver-unix
Väited
- kasutada saab kohalikust arvutist
Tekitada universaalne service shared fail (sama /etc/systemd/system/raw-webserver-tcp.socket sisuga)
/etc/systemd/system/raw-webserver-unix@.service
ning
# cat /etc/systemd/system/raw-webserver-unix.socket [Unit] Description=Unix Domain Socket for Border Web Server [Socket] # ListenStream with an absolute path instructs systemd to create an AF_UNIX socket file ListenStream=/run/raw_webserver.sock # Accept=yes spawns a unique instance of our @.service file per local file connection Accept=yes [Install] WantedBy=sockets.target
Aktiveerimiseks
# systemctl daemon-reload # systemctl enable --now raw-webserver-unix.socket
ja pöördumiseks samas arvutis
root@zabbix-pub-01:~# time curl --unix-socket /run/raw_webserver.sock http://localhost/ <html><body><h1>Hello from the Strict Read-Write Border!</h1><p>Systemd Socket Activation is fully online and functioning.</p></body></html>
Soket paistab nii, eelmise variandi oma on samuti nimekirjas
root@zabbix-pub-01:~# systemctl list-sockets | grep raw [::]:1044 raw-webserver-tcp.socket - /run/raw_webserver.sock raw-webserver-unix.socket -
Päringu tegemise ajal
root@zabbix-pub-01:~# ss -a -p -f unix | grep raw
u_str LISTEN 0 4096 /run/raw_webserver.sock 1727463 * 0 users:(("systemd",pid=1,fd=79))
u_str ESTAB 0 0 /run/raw_webserver.sock 1736013 * 1679109 users:(("python3",pid=14454,fd=1),("python3",pid=14454,fd=0),("systemd",pid=1,fd=10))
kus
- unix soketil on ka listen ja established
raw-webserver-vsock
Väited
- kasutada saab kõnealuse virtuaalse arvuti pve hostist
Tekitada universaalne service shared fail (sama /etc/systemd/system/raw-webserver-tcp.socket sisuga)
/etc/systemd/system/raw-webserver-vsock@.service
ning
# cat /etc/systemd/system/raw-webserver-vsock.socket [Unit] Description=VSOCK Memory Socket for Border Web Server [Socket] # Bind strictly to the AF_VSOCK address family on port 1044 ListenStream=vsock::1044 Accept=yes [Install] WantedBy=sockets.target
Aktiveerimiseks
# systemctl daemon-reload # systemctl enable --now raw-webserver-vsock.socket
Kliendina pöördumiseks sobib nt socat programmi kasutada, küllap curl saab ka
root@pve-svc-02:~# echo "GET /" | socat - VSOCK-CONNECT:50194:1044 HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 140 Connection: close <html><body><h1>Hello from the Strict Read-Write Border!</h1><p>Systemd Socket Activation is fully online and functioning.</p></body></html>
Paistab
root@zabbix-pub-01:~# systemctl --type=socket | grep raw-w
raw-webserver-tcp.socket loaded active listening TCP/IP Socket for Border Web Server
raw-webserver-unix.socket loaded active listening Unix Domain Socket for Border Web Server
raw-webserver-vsock.socket loaded active listening VSOCK Memory Socket for Border Web Server
root@zabbix-pub-01:~# ss -a -p | grep 1044
..
v_str LISTEN 0 0 *:1044 *:* users:(("systemd",pid=1,fd=94))
v_str ESTAB 0 0 50194:1044 2:3887152104 users:(("python3",pid=1604,fd=1),("python3",pid=1604,fd=0),("systemd",pid=1,fd=10))
root@zabbix-pub-01:~# lsof -n -P -p 1684
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python3 1684 root cwd DIR 252,2 4096 2 /
python3 1684 root rtd DIR 252,2 4096 2 /
python3 1684 root txt REG 252,2 8020928 1406 /usr/bin/python3.12
python3 1684 root mem REG 252,2 3055776 128 /usr/lib/locale/locale-archive
python3 1684 root mem REG 252,2 2125328 7621 /usr/lib/x86_64-linux-gnu/libc.so.6
python3 1684 root mem REG 252,2 174336 7817 /usr/lib/x86_64-linux-gnu/libexpat.so.1.9.1
python3 1684 root mem REG 252,2 113000 15564 /usr/lib/x86_64-linux-gnu/libz.so.1.3
python3 1684 root mem REG 252,2 952616 7624 /usr/lib/x86_64-linux-gnu/libm.so.6
python3 1684 root mem REG 252,2 27028 7610 /usr/lib/x86_64-linux-gnu/gconv/gconv-modules.cache
python3 1684 root mem REG 252,2 236616 7618 /usr/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
python3 1684 root 0u sock 0,9 0t0 16838 protocol: AF_VSOCK
python3 1684 root 1u sock 0,9 0t0 16838 protocol: AF_VSOCK
python3 1684 root 2u unix 0xffff8a1a4527ec00 0t0 30427 type=STREAM (CONNECTED)
kus
- python skript puhul on iseloomulikut avatud 0u ja 1u märkega af_vsock (st kuskil teises otsas on vsock, mitte et see oleks pythonile oluline, tema kasutab ikka read ja write syscallisid, aga kernel teab)
Misc
Ebaõnnestumiste eemaldamine
root@zabbix-pub-01:~# systemctl | grep raw ● raw-webserver-vsock@0-50194:1044-2:3887152079.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152079) ● raw-webserver-vsock@1-50194:1044-2:3887152080.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152080) ● raw-webserver-vsock@2-50194:1044-2:3887152081.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152081) ● raw-webserver-vsock@3-50194:1044-2:3887152082.service loaded failed failed Protocol-Blind Web Server Instance (Per-Connection) (vsock:2:3887152082) root@zabbix-pub-01:~# systemctl reset-failed 'raw-webserver-vsock@*'
Kasulikud lisamaterjalid
- https://en.wikipedia.org/wiki/Berkeley_sockets
- käesoleva teksti moodustamisel on kasutatud ohtralt AI abil (gemini google)