Showing 7 changed files with 67 additions and 21 deletions
+1 -1
.doc/database/README.md
@@ -108,7 +108,7 @@ Seed-ul curent produce:
108 108
 - workerii `dhcp-router` și `mdns-listener`
109 109
 - Work Order-ul existent pentru retragerea numelor legacy
110 110
 
111
-`config/local-hosts.tsv` rămâne manifest generat explicit din tabelele runtime.
111
+`config/local-hosts.tsv` rămâne manifest generat explicit din tabelele runtime, dar scriptul de sync citește manifestul direct din SQLite-ul runtime de pe jumper.
112 112
 
113 113
 ## Inspecție
114 114
 
+3 -3
.doc/local-hosts.md
@@ -28,8 +28,8 @@ Implementarea versionată este:
28 28
 |--------|-----|
29 29
 | `var/host-manager.sqlite` | sursa de adevăr runtime pentru registry și Work Orders |
30 30
 | `config/hosts.yaml` | seed/snapshot export pentru hosturi și FQDN-uri canonice |
31
-| `config/local-hosts.tsv` | manifest DNS generat, cu A records pentru hosturi/aliasuri de host și CNAME records pentru vhosturi |
32
-| `scripts/sync_local_hosts.sh` | generează și sincronizează `/etc/hosts`, `cloaking-rules.txt` și `/ip dns static` |
31
+| `config/local-hosts.tsv` | export DNS generat din registry-ul SQLite, cu A records pentru hosturi/aliasuri de host și CNAME records pentru vhosturi |
32
+| `scripts/sync_local_hosts.sh` | citește manifestul DNS din SQLite-ul runtime de pe jumper și sincronizează `/etc/hosts`, `cloaking-rules.txt` și `/ip dns static` |
33 33
 
34 34
 `madagascar.xdev.ro` este domeniul implicit. Pentru orice host real `*.madagascar.xdev.ro`, aliasul scurt este derivat automat. De exemplu, `autonas01.madagascar.xdev.ro` publică și `autonas01`. Vhosturile, de exemplu `pmx.baobab.madagascar.xdev.ro`, se publică drept CNAME către hostul care le servește; aliasul scurt derivat, de exemplu `pmx.baobab`, este tot CNAME. Aliasurile derivate nu se declară separat în registry.
35 35
 
@@ -40,7 +40,7 @@ Când inventarele se contrazic, ordinea de încredere este:
40 40
 1. DHCP lease/reservation pe router (`admin@192.168.2.1`) — autoritatea pentru alocarea IP-urilor pe LAN. Configurațiile statice locale nu au voie să mute un IP peste DHCP; cel mult semnalează o rezervare lipsă sau o intrare veche.
41 41
 2. `cluster/cluster-context/madagascar.json` — autoritatea pentru roluri, topologie și IP-uri de serviciu. Pentru nodurile Proxmox, DNS-ul de serviciu poate folosi interfața `thunderbridge` (`192.168.10.x`) chiar dacă management/WAN este `192.168.2.x`.
42 42
 3. `var/host-manager.sqlite` — registry-ul operațional aprobat, editat prin Madagascar Local Authority.
43
-4. `config/local-hosts.tsv` — manifestul DNS local publicat pe jumper și as01. Acesta trebuie să fie derivat sau validat din DHCP plus topologia clusterului, nu folosit ca sursă primară de alocare IP.
43
+4. `config/local-hosts.tsv` — manifestul DNS local publicat pe jumper și as01. Acesta este un export al registry-ului SQLite și nu sursa primară pentru sync; sincronizarea citește manifestul runtime de pe jumper.
44 44
 5. `hosts-local.yaml` — inventar SSH: aliasuri, utilizatori, entrypoint-uri și căi de acces. IP-urile de aici sunt utile pentru audit, dar pot fi stale dacă DHCP spune altceva.
45 45
 6. mDNS (`*.local`) — sursă observată de descoperire și validare. Confirmă prezența unui host sau propune aliasuri, dar nu creează automat intrări `madagascar.xdev.ro`.
46 46
 7. DNS public — folosit doar pentru acces extern. Local, numele interne trebuie shadow-uite exact sau lăsate nerezolvate; wildcard-ul public nu este autoritate pentru LAN.
+3 -3
README.md
@@ -18,10 +18,10 @@ The runtime instance lives on jumper and remains the local source for operationa
18 18
 
19 19
 - `var/host-manager.sqlite` - runtime source of truth for host registry and Work Orders
20 20
 - `config/hosts.yaml` - seed/snapshot export for host registry compatibility
21
-- `config/local-hosts.tsv` - DNS manifest exported for local resolvers
21
+- `config/local-hosts.tsv` - DNS manifest export derived from the runtime SQLite registry
22 22
 - `config/work-orders.yaml` - seed/snapshot export for confirmable operational changes
23 23
 - `scripts/host_manager.pl` - Perl-only web app
24
-- `scripts/sync_local_hosts.sh` - local DNS sync to jumper and as01
24
+- `scripts/sync_local_hosts.sh` - local DNS sync to jumper and as01, sourced from the runtime DB on jumper
25 25
 - `scripts/ca_manager.sh` - local OpenSSL CA helper for host certificates
26 26
 
27 27
 The public `xdev.ro` zone is maintained in the separate DNS public-zone repository.
@@ -86,7 +86,7 @@ scripts/deploy_to_jumper.sh --include-config
86 86
 
87 87
 The default internal domain is `madagascar.xdev.ro`. Short aliases are derived automatically from FQDNs, so `autonas01.madagascar.xdev.ro` also publishes `autonas01` without declaring it separately.
88 88
 
89
-Name removals with operational impact go through a Work Order. A WO records intent first; the operational checklist must be completed before confirmation can update the SQLite registry, mark the WO as confirmed, and regenerate `local-hosts.tsv`. Resolver sync remains an explicit operator step.
89
+Name removals with operational impact go through a Work Order. A WO records intent first; the operational checklist must be completed before confirmation can update the SQLite registry, mark the WO as confirmed, and regenerate `local-hosts.tsv`. Resolver sync remains an explicit operator step and reads the runtime manifest from the jumper database.
90 90
 
91 91
 The local host CA stores private material outside git under `var/ca`. Initialize it on jumper with:
92 92
 
+20 -7
config/local-hosts.tsv
@@ -1,21 +1,34 @@
1 1
 # Local DNS manifest for the madagascar network.
2
-# Generated by scripts/host_manager.pl from config/hosts.yaml.
2
+# Generated by scripts/host_manager.pl from the runtime SQLite registry.
3 3
 #
4 4
 # Format:
5 5
 # ip<TAB>name [aliases...]
6
+# CNAME<TAB>alias<TAB>target
6 7
 #
7 8
 # Priority rule:
8 9
 # - DHCP lease/reservation on 192.168.2.1 is canonical for LAN IP allocation.
9 10
 # - madagascar.json is canonical for cluster roles and service interfaces.
10 11
 # - This file publishes approved local DNS records derived from those sources.
11
-192.168.2.96	andrafiabe.madagascar.xdev.ro pbs.andrafiabe.madagascar.xdev.ro andrafiabe pbs.andrafiabe
12
-192.168.2.95	anjothibe.madagascar.xdev.ro pbs.anjothibe.madagascar.xdev.ro anjothibe pbs.anjothibe
12
+192.168.2.96	andrafiabe.madagascar.xdev.ro andrafiabe
13
+CNAME	pbs.andrafiabe.madagascar.xdev.ro	andrafiabe.madagascar.xdev.ro
14
+CNAME	pbs.andrafiabe	andrafiabe.madagascar.xdev.ro
15
+192.168.2.95	anjothibe.madagascar.xdev.ro anjothibe
16
+CNAME	pbs.anjothibe.madagascar.xdev.ro	anjothibe.madagascar.xdev.ro
17
+CNAME	pbs.anjothibe	anjothibe.madagascar.xdev.ro
13 18
 192.168.10.21	autonas01.madagascar.xdev.ro autonas01
14 19
 192.168.10.22	autonas02.madagascar.xdev.ro autonas02
15
-192.168.10.91	baobab.madagascar.xdev.ro pmx.baobab.madagascar.xdev.ro baobab pmx.baobab
16
-192.168.10.92	ebony.madagascar.xdev.ro pmx.ebony.madagascar.xdev.ro ebony pmx.ebony
17
-192.168.2.100	jumper.madagascar.xdev.ro hosts.madagascar.xdev.ro jumper hosts
20
+192.168.10.91	baobab.madagascar.xdev.ro baobab
21
+CNAME	pmx.baobab.madagascar.xdev.ro	baobab.madagascar.xdev.ro
22
+CNAME	pmx.baobab	baobab.madagascar.xdev.ro
23
+192.168.10.92	ebony.madagascar.xdev.ro ebony
24
+CNAME	pmx.ebony.madagascar.xdev.ro	ebony.madagascar.xdev.ro
25
+CNAME	pmx.ebony	ebony.madagascar.xdev.ro
26
+192.168.2.100	jumper.madagascar.xdev.ro jumper
27
+CNAME	hosts.madagascar.xdev.ro	jumper.madagascar.xdev.ro
28
+CNAME	hosts	jumper.madagascar.xdev.ro
18 29
 192.168.2.102	mazeri.madagascar.xdev.ro mazeri
19
-192.168.10.93	tapia.madagascar.xdev.ro pmx.tapia.madagascar.xdev.ro tapia pmx.tapia
30
+192.168.10.93	tapia.madagascar.xdev.ro tapia
31
+CNAME	pmx.tapia.madagascar.xdev.ro	tapia.madagascar.xdev.ro
32
+CNAME	pmx.tapia	tapia.madagascar.xdev.ro
20 33
 192.168.2.103	toltec.madagascar.xdev.ro toltec
21 34
 192.168.2.107	zabbix.madagascar.xdev.ro zabbix
+1 -1
deploy/jumper/README.md
@@ -101,4 +101,4 @@ Nu se adaugă wildcard local. Doar acest nume exact trebuie publicat.
101 101
 
102 102
 ## mDNS discovery
103 103
 
104
-`host-manager-mdns` este un listener separat care observă mDNS și scrie direct în tabelul SQLite `mdns_observations`. Listenerul nu modifică host registry-ul, `config/hosts.yaml` sau `config/local-hosts.tsv`.
104
+`host-manager-mdns` este un listener separat care observă mDNS și scrie direct în tabelul SQLite `mdns_observations`. Listenerul nu modifică host registry-ul, `config/hosts.yaml` sau `config/local-hosts.tsv`. Sync-ul resolverului citește manifestul runtime din SQLite, nu din exportul static.
+9 -0
scripts/host_manager.pl
@@ -26,6 +26,7 @@ my %opt = (
26 26
     local_hosts_tsv => $ENV{HOST_MANAGER_LOCAL_HOSTS_TSV} || "$project_dir/config/local-hosts.tsv",
27 27
     work_orders => $ENV{HOST_MANAGER_WORK_ORDERS} || "$project_dir/config/work-orders.yaml",
28 28
 );
29
+my $print_local_hosts_tsv = 0;
29 30
 
30 31
 while (@ARGV) {
31 32
     my $arg = shift @ARGV;
@@ -41,6 +42,8 @@ while (@ARGV) {
41 42
         $opt{local_hosts_tsv} = shift @ARGV;
42 43
     } elsif ($arg eq '--work-orders') {
43 44
         $opt{work_orders} = shift @ARGV;
45
+    } elsif ($arg eq '--print-local-hosts-tsv') {
46
+        $print_local_hosts_tsv = 1;
44 47
     } elsif ($arg eq '--help' || $arg eq '-h') {
45 48
         usage();
46 49
         exit 0;
@@ -49,6 +52,11 @@ while (@ARGV) {
49 52
     }
50 53
 }
51 54
 
55
+if ($print_local_hosts_tsv) {
56
+    print render_local_hosts_tsv(load_registry());
57
+    exit 0;
58
+}
59
+
52 60
 my $session_secret = $ENV{HOST_MANAGER_SESSION_SECRET} || random_hex(32);
53 61
 my %sessions;
54 62
 
@@ -87,6 +95,7 @@ Environment:
87 95
   HOST_MANAGER_DATA             Defaults to config/hosts.yaml.
88 96
   HOST_MANAGER_LOCAL_HOSTS_TSV  Defaults to config/local-hosts.tsv.
89 97
   HOST_MANAGER_WORK_ORDERS      Defaults to config/work-orders.yaml.
98
+  --print-local-hosts-tsv       Print the runtime DNS manifest and exit.
90 99
 
91 100
 SQLite is the runtime source of truth. YAML files seed a new database and remain
92 101
 download/export compatibility artifacts. The nginx vhost keeps registry, CA,
+30 -6
scripts/sync_local_hosts.sh
@@ -7,24 +7,27 @@ set -euo pipefail
7 7
 
8 8
 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
9 9
 PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
10
-CONFIG_FILE="$PROJECT_DIR/config/local-hosts.tsv"
10
+HOST_MANAGER="$PROJECT_DIR/scripts/host_manager.pl"
11 11
 JUMPER_HOST="jumper.madagascar.xdev.ro"
12 12
 AS01="admin@192.168.2.2"
13 13
 APPLY=false
14 14
 VERIFY=false
15 15
 TARGET="all"
16
+SOURCE="jumper"
16 17
 NEGATIVE_NAME="nohost.madagascar.xdev.ro"
17 18
 
18 19
 usage() {
19 20
     cat << EOF
20
-Usage: $0 [--apply] [--verify] [--target all|jumper|as01] [-c file]
21
+Usage: $0 [--apply] [--verify] [--target all|jumper|as01] [--source jumper|local]
21 22
 
22 23
 Default mode is dry-run. Use --apply to change remote resolvers.
24
+The default manifest source is the runtime SQLite database on jumper.
23 25
 
24 26
 Examples:
25 27
   $0
26 28
   $0 --apply --verify
27 29
   $0 --target as01 --apply
30
+  $0 --source local
28 31
 EOF
29 32
 }
30 33
 
@@ -42,7 +45,7 @@ while [[ $# -gt 0 ]]; do
42 45
         --apply) APPLY=true; shift ;;
43 46
         --verify) VERIFY=true; shift ;;
44 47
         --target) TARGET="${2:-}"; shift 2 ;;
45
-        -c|--config) CONFIG_FILE="${2:-}"; shift 2 ;;
48
+        --source) SOURCE="${2:-}"; shift 2 ;;
46 49
         -h|--help) usage; exit 0 ;;
47 50
         *) die "Unknown option: $1" ;;
48 51
     esac
@@ -53,16 +56,20 @@ case "$TARGET" in
53 56
     *) die "Invalid target: $TARGET" ;;
54 57
 esac
55 58
 
59
+case "$SOURCE" in
60
+    jumper|local) ;;
61
+    *) die "Invalid source: $SOURCE" ;;
62
+esac
63
+
56 64
 if [[ "$TARGET" == "is-vpn-gw" ]]; then
57 65
     log "Target is-vpn-gw is deprecated; use jumper"
58 66
     TARGET="jumper"
59 67
 fi
60 68
 
61
-[[ -f "$CONFIG_FILE" ]] || die "Missing config file: $CONFIG_FILE"
62
-
63 69
 WORK_DIR="$(mktemp -d)"
64 70
 trap 'rm -rf "$WORK_DIR"' EXIT
65 71
 
72
+MANIFEST_FILE="$WORK_DIR/local-hosts.tsv"
66 73
 HOSTS_ROWS="$WORK_DIR/hosts.rows"
67 74
 CLOAK_ROWS="$WORK_DIR/cloak.rows"
68 75
 NAMES_FILE="$WORK_DIR/names.txt"
@@ -93,6 +100,23 @@ run_jumper_payload() {
93 100
     fi
94 101
 }
95 102
 
103
+fetch_manifest() {
104
+    case "$SOURCE" in
105
+        local)
106
+            perl "$HOST_MANAGER" --print-local-hosts-tsv
107
+            ;;
108
+        jumper)
109
+            if is_local_jumper; then
110
+                perl "$HOST_MANAGER" --print-local-hosts-tsv
111
+            else
112
+                ssh "$JUMPER_HOST" "perl /usr/local/xdev-host-manager/scripts/host_manager.pl --print-local-hosts-tsv"
113
+            fi
114
+            ;;
115
+    esac > "$MANIFEST_FILE"
116
+}
117
+
118
+fetch_manifest
119
+
96 120
 while IFS= read -r line || [[ -n "$line" ]]; do
97 121
     [[ -z "${line//[[:space:]]/}" ]] && continue
98 122
     [[ "$line" =~ ^[[:space:]]*# ]] && continue
@@ -145,7 +169,7 @@ while IFS= read -r line || [[ -n "$line" ]]; do
145 169
             printf '/ip dns static add name="%s" type=A address=%s comment="xdev-local managed"\n' "$ros_name" "$ros_ip"
146 170
         } >> "$MIKROTIK_RSC"
147 171
     done
148
-done < "$CONFIG_FILE"
172
+done < "$MANIFEST_FILE"
149 173
 
150 174
 sort -u "$NAMES_FILE" -o "$NAMES_FILE"
151 175
 printf '/ip dns cache flush\n' >> "$MIKROTIK_RSC"