7.3 KiB
Поведение Amnezia limits (old bash)
Этот документ фиксирует фактический behavior старой bash-системы. Он нужен как migration contract: Rust-версия должна воспроизвести не только видимый лимит 70 Mbit/s, но и всю сетевую topology, lifecycle-логику и диагностику.
1. Файлы старой системы
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. tcfromiproute2-6.15.0.- Docker
26.1.5+dfsg1. - Amnezia container name in snapshot:
amnezia-awg2. - Interface in snapshot:
awg0. - Systemd units
amnezia-tc.serviceandamnezia-tc-watcher.serviceenabled. - Scripts installed in
/opt/amnezia/and executable by root.
3. Container discovery
limit_peers.sh searches running containers in this order:
amnezia-awg2
amnezia-awg
check-vpn-limits uses the opposite order in its array:
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:
wg[0-9]+ | wg | awg[0-9]* | amnezia[0-9]* | tun[0-9]+
check-vpn-limits detects:
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:
wg show all allowed-ips
The script extracts the third field and keeps IPv4 addresses matching:
10.8.1.[0-9]
Fallback source:
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:
<iface> root qdisc: handle 1: htb
<iface> root class: 1:1 rate 1000mbit ceil 1000mbit
per peer class: 1:<class_id> rate LIMIT ceil LIMIT
per peer qdisc: fq_codel under 1:<class_id>
per peer filter: match ip dst <peer_ip> flowid 1:<class_id>
Ingress is implemented through IFB:
modprobe ifb
ip link add name ifb0 type ifb
ip link set dev ifb0 up
<iface> ingress qdisc: handle ffff:
<iface> 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:<class_id> rate LIMIT ceil LIMIT
per peer qdisc: fq_codel under 2:<class_id>
per peer filter: match ip src <peer_ip> flowid 2:<class_id>
Default limit:
LIMIT=70mbit
Default root rate:
1000mbit
Default first class id:
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:
tc qdisc del dev <iface> root || true
tc qdisc del dev <iface> 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:
tc filter show dev <iface> 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 <iface>. 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:
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:
ip link show awg0
Then it runs:
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 70Mbiton the AWG/WG interface; - filter statistics from
tc -s filter show dev <iface> 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 showoutput format; - container loses
tcoriproute2tools.
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;
ifb0exists and is up when ingress shaping is enabled;- class ids are deterministic, start from 10 and use lexicographic bash
sort -upeer 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.