Files
AWG2_harness/docs/OLD_BASH_BEHAVIOR.md
T

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.
  • 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:

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 70Mbit on 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 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.