# Поведение `Amnezia limits (old bash)` Этот документ фиксирует фактический behavior старой bash-системы. Он нужен как migration contract: Rust-версия должна воспроизвести не только видимый лимит 70 Mbit/s, но и всю сетевую topology, lifecycle-логику и диагностику. ## 1. Файлы старой системы ```text Amnezia limits (old bash)/ ├── limit_peers.sh.txt # основной apply/reconcile traffic-control rules ├── docker_watcher.sh.txt # watcher Docker container start events ├── check-vpn-limits.txt # status/diagnostic CLI ├── amnezia-tc.service.txt # systemd oneshot apply ├── amnezia-tc-watcher.service.txt # systemd watcher ├── traffic_control-was.txt # production snapshot of tc qdisc/classes/filters ├── wg-peers.txt # production snapshot of wg peers ├── iproute2_a_core.txt # kernel/iproute2/docker/container facts ├── permissions.txt # installed paths and enabled units └── Нюансы.txt # operational notes and known risks ``` ## 2. Runtime environment from snapshots Зафиксированное окружение: - Debian 13 cloud amd64 kernel line: `6.12.57+deb13-cloud-amd64`. - `tc` from `iproute2-6.15.0`. - Docker `26.1.5+dfsg1`. - Amnezia container name in snapshot: `amnezia-awg2`. - Interface in snapshot: `awg0`. - Systemd units `amnezia-tc.service` and `amnezia-tc-watcher.service` enabled. - Scripts installed in `/opt/amnezia/` and executable by root. ## 3. Container discovery `limit_peers.sh` searches running containers in this order: ```text amnezia-awg2 amnezia-awg ``` `check-vpn-limits` uses the opposite order in its array: ```text amnezia-awg amnezia-awg2 ``` Rust should normalize this into one configured ordered list. Recommended default: `amnezia-awg2`, then `amnezia-awg`, because `amnezia-awg2` is the current AWG 2.0 production name in the snapshot. ## 4. Interface discovery `limit_peers.sh` starts from `WG_IF`, default `wg0`. If this interface is absent inside the container, it auto-detects: ```text wg[0-9]+ | wg | awg[0-9]* | amnezia[0-9]* | tun[0-9]+ ``` `check-vpn-limits` detects: ```text wg[0-9]+ | wg | amnezia[0-9]* | tun[0-9]+ | veth[0-9]+ ``` Operational note says the important risk is future Amnezia interface rename between `awg0`, `wg0`, `amnezia0`. Rust must use a single detector shared by apply, daemon, watcher and status. ## 5. Peer discovery Primary source: ```bash wg show all allowed-ips ``` The script extracts the third field and keeps IPv4 addresses matching: ```text 10.8.1.[0-9] ``` Fallback source: ```bash wg show ``` Then regex extracts the same `10.8.1.x` addresses. The production peer snapshot contains 23 peers in `10.8.1.0/24`, with a mix of online peers and peers without endpoint/handshake. Rust must shape all peers with allowed IPs, not only currently online peers. ## 6. Traffic-control topology The old script owns both egress and ingress shaping. Egress on the AWG/WG interface: ```text root qdisc: handle 1: htb root class: 1:1 rate 1000mbit ceil 1000mbit per peer class: 1: rate LIMIT ceil LIMIT per peer qdisc: fq_codel under 1: per peer filter: match ip dst flowid 1: ``` Ingress is implemented through IFB: ```text modprobe ifb ip link add name ifb0 type ifb ip link set dev ifb0 up ingress qdisc: handle ffff: ingress filter: action mirred egress redirect dev ifb0 ifb0 root qdisc: handle 2: htb ifb0 root class: 2:1 rate 1000mbit ceil 1000mbit per peer class: 2: rate LIMIT ceil LIMIT per peer qdisc: fq_codel under 2: per peer filter: match ip src flowid 2: ``` Default limit: ```text LIMIT=70mbit ``` Default root rate: ```text 1000mbit ``` Default first class id: ```text 10 ``` The snapshot `traffic_control-was.txt` shows classes `1:10` through `1:32`, which corresponds to 23 peers. Class assignment follows bash `sort -u`, so it is lexicographic string order, not numeric IPv4 order. For example, `10.8.1.10` sorts before `10.8.1.2`. Rust v1 must preserve this if strict parity with production class IDs is required. ## 7. Rebuild behavior Before applying new rules, the script deletes old topology: ```bash tc qdisc del dev root || true tc qdisc del dev ingress || true ip link del ifb0 || true ``` Then it recreates everything. This is important: the production model is full rebuild on detected mismatch. Rust v1 should keep this behavior unless a well-tested incremental diff is implemented later. ## 8. Drift detection in bash The script reads existing filters: ```bash tc filter show dev parent 1: ``` It extracts hex IP matches and compares count/content with expected peer IPs. If count differs or any peer IP is missing, it rebuilds rules. Weakness: this only strongly verifies egress filters on ``. It does not fully verify `ifb0`, ingress redirect, qdisc leaves or per-peer ingress filters. Rust must improve this and treat any partial state as drift. ## 9. Docker watcher behavior `docker_watcher.sh` listens to: ```bash docker events --filter event=start --format '{{.Actor.Attributes.name}}' ``` If the started container is `amnezia-awg2` or `amnezia-awg`, it waits up to 20 seconds for: ```bash ip link show awg0 ``` Then it runs: ```bash WG_IF=awg0 /opt/amnezia/limit_peers.sh ``` Weakness: hardcoded `awg0`. Rust watcher must reuse the same interface detector as `apply-limits`. ## 10. Status behavior `check-vpn-limits` reports: - status of `amnezia-tc.service`; - status of `amnezia-tc.timer`, although timer file is not present in the archive; - running container and Docker status string; - connected clients from `wg show`; - TC classes with `rate 70Mbit` on the AWG/WG interface; - filter statistics from `tc -s filter show dev parent 1:`; - last 3 events from `/var/log/amnezia-tc.log`. Rust `status` must be at least as useful, but should not hardcode 70 Mbit/s; it should use config and display desired vs actual. ## 11. Operational guarantees stated in old notes The old notes claim the system currently survives: - host reboot; - Docker restart; - container restart; - Amnezia update; - user addition; - user removal. They also identify two breakage scenarios: - Amnezia fully changes `wg show` output format; - container loses `tc` or `iproute2` tools. In the Rust architecture, the second risk should be reduced by host namespace control: `tc/ip` should preferably come from the host while operating in the container netns, rather than requiring them inside the container. ## 12. Migration contract Rust migration is not accepted until all of the following are true: - same peers are discovered as in old bash; - same default limit is applied: 70 Mbit/s per peer; - both egress and ingress shaping exist; - `ifb0` exists and is up when ingress shaping is enabled; - class ids are deterministic, start from 10 and use lexicographic bash `sort -u` peer ordering; - full rebuild removes stale peer rules after peer deletion; - repeated apply is safe; - daemon recovers after Docker/container lifecycle events; - status explains actual drift rather than printing only a success banner.