feat(device-edge): retire superseded VPS tailnet
This commit is contained in:
@@ -102,6 +102,10 @@ CHANNEL_CORE_CERTIFICATE = CHANNEL_TRUST_ROOT / "core-certificate.pem"
|
||||
CHANNEL_RUNTIME_CONFIG = CHANNEL_TRUST_ROOT / "runtime.json"
|
||||
CHANNEL_HEALTH_PORT = 18222
|
||||
CHANNEL_PUBLIC_PORT = 443
|
||||
CORE_CHANNEL_ACCEPTED_PATCH = "device-edge-vps-core-channel-20260812-010"
|
||||
CORE_CHANNEL_ACCEPTED_SHA256 = (
|
||||
"c8ef3c4bb45850cad32e881eba081bc4c891c2886e5500d02cb94616d82353f3"
|
||||
)
|
||||
|
||||
FOUNDATION_ENTRIES = (
|
||||
"vps/config/00-nodedc-b2-vps.conf",
|
||||
@@ -136,6 +140,9 @@ CORE_CHANNEL_ENTRIES = (
|
||||
"vps/systemd/nodedc-device-edge-channel.service",
|
||||
"deployment/device-edge-vps-core-channel-v1.json",
|
||||
)
|
||||
TAILSCALE_RETIREMENT_ENTRIES = (
|
||||
"deployment/device-edge-vps-tailscale-retirement-v1.json",
|
||||
)
|
||||
|
||||
PHASE_ENTRIES = {
|
||||
"foundation": FOUNDATION_ENTRIES,
|
||||
@@ -143,6 +150,7 @@ PHASE_ENTRIES = {
|
||||
"backhaul": BACKHAUL_ENTRIES,
|
||||
"relay": RELAY_ENTRIES,
|
||||
"core-channel": CORE_CHANNEL_ENTRIES,
|
||||
"tailscale-retirement": TAILSCALE_RETIREMENT_ENTRIES,
|
||||
}
|
||||
|
||||
SUPERSEDED_TRANSPORT_PHASES = frozenset({"backhaul", "relay"})
|
||||
@@ -206,6 +214,10 @@ PHASE_FILE_SHA256 = {
|
||||
"deployment/device-edge-vps-core-channel-v1.json":
|
||||
"ecbd66404d197f6d939736bbc1c28cc1e7091781639ffb78d2199a8db2b3ccd2",
|
||||
},
|
||||
"tailscale-retirement": {
|
||||
"deployment/device-edge-vps-tailscale-retirement-v1.json":
|
||||
"bbe11e8cf4103f44ae7888b4d3f3dde7015eeaef8ea5a7c4c00427a7b85f8e33",
|
||||
},
|
||||
}
|
||||
|
||||
# Exact immutable baselines from terminally accepted predecessor artifacts.
|
||||
@@ -622,6 +634,18 @@ def current_phase_preflight(phase: str):
|
||||
for port in (CHANNEL_HEALTH_PORT, CHANNEL_PUBLIC_PORT, 9921):
|
||||
assert_port_closed(port)
|
||||
return {"predecessor": "accepted-foundation-closed-channel"}
|
||||
if phase == "tailscale-retirement":
|
||||
core_record = applied_phase_record("core-channel")
|
||||
if (
|
||||
core_record.get("patch") != CORE_CHANNEL_ACCEPTED_PATCH
|
||||
or core_record.get("sha256") != CORE_CHANNEL_ACCEPTED_SHA256
|
||||
):
|
||||
die("VPS Tailscale retirement Core channel predecessor mismatch")
|
||||
source_file_state("core-channel")
|
||||
validate_core_channel_runtime()
|
||||
if (LIVE_ROOT / TAILSCALE_RETIREMENT_ENTRIES[0]).exists():
|
||||
die("VPS Tailscale retirement target path already exists")
|
||||
return {"predecessor": "accepted-core-channel-010-with-live-tailnet"}
|
||||
if phase == "backhaul":
|
||||
for tool in (Path("/usr/bin/ssh"), Path("/usr/bin/nc")):
|
||||
assert_executable_command_path(
|
||||
@@ -693,6 +717,13 @@ def backup_targets_for_phase(phase: str):
|
||||
return common + [BACKHAUL_UNIT, BACKHAUL_KNOWN_HOSTS]
|
||||
if phase == "core-channel":
|
||||
return common + [CHANNEL_UNIT, NFTABLES_CONFIG, CHANNEL_TRUST_ROOT]
|
||||
if phase == "tailscale-retirement":
|
||||
return common + [
|
||||
TAILSCALE_UNIT,
|
||||
TAILSCALE_STATE.parent,
|
||||
TAILSCALE_BIN.parent,
|
||||
TRUST_ROOT,
|
||||
]
|
||||
return common + [RELAY_UNIT, NFTABLES_CONFIG]
|
||||
|
||||
|
||||
@@ -1141,6 +1172,28 @@ def apply_core_channel(payload: Path):
|
||||
validate_core_channel_runtime()
|
||||
|
||||
|
||||
def apply_tailscale_retirement(_payload: Path):
|
||||
# Remote machine deletion is intentionally post-deploy: doing a logout
|
||||
# inside apply would invalidate the exact local state needed by automatic
|
||||
# rollback. The accepted local boundary is nevertheless closed completely.
|
||||
systemctl("disable", "--now", "nodedc-b2-tailscaled.service", check=False)
|
||||
for path in (
|
||||
TAILSCALE_UNIT,
|
||||
TAILSCALE_STATE.parent,
|
||||
TAILSCALE_BIN.parent,
|
||||
TRUST_ROOT,
|
||||
):
|
||||
if path.is_symlink():
|
||||
die(f"VPS Tailscale retirement target symlink rejected: {path}")
|
||||
if path.is_dir():
|
||||
shutil.rmtree(path)
|
||||
elif path.exists():
|
||||
path.unlink()
|
||||
systemctl("daemon-reload")
|
||||
systemctl("reset-failed", "nodedc-b2-tailscaled.service", check=False)
|
||||
validate_tailscale_retirement_runtime()
|
||||
|
||||
|
||||
def sshd_effective():
|
||||
return run(["/usr/sbin/sshd", "-T"]).stdout.lower()
|
||||
|
||||
@@ -1348,6 +1401,67 @@ def validate_core_channel_runtime():
|
||||
return health
|
||||
|
||||
|
||||
def validate_tailscale_retirement_runtime():
|
||||
source_file_state("foundation")
|
||||
source_file_state("core-channel")
|
||||
source_file_state("tailscale-retirement")
|
||||
core_record = applied_phase_record("core-channel")
|
||||
if (
|
||||
core_record.get("patch") != CORE_CHANNEL_ACCEPTED_PATCH
|
||||
or core_record.get("sha256") != CORE_CHANNEL_ACCEPTED_SHA256
|
||||
):
|
||||
die("retired Tailscale runtime Core channel identity mismatch")
|
||||
if run([str(NODE_BIN), "--version"]).stdout.strip() != f"v{NODE_VERSION}":
|
||||
die("retired Tailscale runtime Node version mismatch")
|
||||
if service_active("nodedc-b2-tailscaled.service"):
|
||||
die("retired Tailscale service remains active")
|
||||
if systemctl("is-enabled", "nodedc-b2-tailscaled.service", check=False).returncode == 0:
|
||||
die("retired Tailscale service remains enabled")
|
||||
for path in (
|
||||
TAILSCALE_UNIT,
|
||||
TAILSCALE_SOCKET,
|
||||
TAILSCALE_STATE.parent,
|
||||
TAILSCALE_BIN.parent,
|
||||
TRUST_ROOT,
|
||||
):
|
||||
if path.exists() or path.is_symlink():
|
||||
die(f"retired Tailscale boundary remains present: {path}")
|
||||
if run(["/usr/bin/pgrep", "-x", "tailscaled"], check=False).returncode == 0:
|
||||
die("retired Tailscale process remains running")
|
||||
assert_management_key()
|
||||
if not port_is_open(PUBLIC_IPV4, 22, timeout=5):
|
||||
die("management SSH became unavailable after Tailscale retirement")
|
||||
if port_is_open("127.0.0.1", 1055):
|
||||
die("retired Tailscale SOCKS listener remains open")
|
||||
source_file_state("core-channel")
|
||||
assert_channel_trust(require_runtime_owner=True)
|
||||
if not service_active("nodedc-device-edge-channel.service"):
|
||||
die("Core channel became inactive after Tailscale retirement")
|
||||
health = core_channel_health(require_accepted=True)
|
||||
expected = {
|
||||
"ok": True,
|
||||
"service": "nodedc-device-edge-channel",
|
||||
"channel": "accepted",
|
||||
"trackerIngress": "disabled",
|
||||
"commandTransport": "disabled",
|
||||
}
|
||||
for key, value in expected.items():
|
||||
if health.get(key) != value:
|
||||
die(f"retired Tailscale Core channel contract mismatch: {key}")
|
||||
if not port_is_open(PUBLIC_IPV4, CHANNEL_PUBLIC_PORT, timeout=5):
|
||||
die("Core channel listener became unavailable after Tailscale retirement")
|
||||
assert_port_closed(9921)
|
||||
nft = run(["/usr/sbin/nft", "list", "table", "inet", "nodedc_b2_vps"]).stdout
|
||||
if (
|
||||
"policy drop" not in nft
|
||||
or "tcp dport 22" not in nft
|
||||
or "tcp dport 443" not in nft
|
||||
or "tcp dport 9921" in nft
|
||||
):
|
||||
die("retired Tailscale firewall contract mismatch")
|
||||
return health
|
||||
|
||||
|
||||
def validate_relay_runtime():
|
||||
validate_backhaul_runtime()
|
||||
source_file_state("relay")
|
||||
@@ -1499,7 +1613,7 @@ def plan_artifact(artifact_argument: str):
|
||||
print("source_admission=public-ipv4-only")
|
||||
print("services=nodedc-b2-relay")
|
||||
print(f"relay_runtime_identity={RELAY_USER}:no-credentials")
|
||||
else:
|
||||
elif phase == "core-channel":
|
||||
print("public_core_channel=155.212.211.15:443/tcp:tls13-mtls-h2")
|
||||
print(f"health=127.0.0.1:{CHANNEL_HEALTH_PORT}")
|
||||
print("public_b2_ingress=disabled")
|
||||
@@ -1507,6 +1621,16 @@ def plan_artifact(artifact_argument: str):
|
||||
print("services=nodedc-device-edge-channel")
|
||||
print(f"channel_runtime_identity={CHANNEL_USER}:host-local-private-key")
|
||||
print("peer_trust=preprovisioned-pinned-self-signed-core-certificate+fingerprint")
|
||||
else:
|
||||
print("public_core_channel=preserved:155.212.211.15:443/tcp:tls13-mtls-h2")
|
||||
print(f"health=preserved:127.0.0.1:{CHANNEL_HEALTH_PORT}")
|
||||
print("tailscale=stop+disable+destroy-local-runtime-state")
|
||||
print("tailscale_socks_1055=removed")
|
||||
print("superseded_backhaul_private_key=removed")
|
||||
print("public_b2_ingress=disabled")
|
||||
print("tracker_tcp_9921=closed")
|
||||
print("services=preserved:nodedc-device-edge-channel")
|
||||
print("external_tailnet_machine_cleanup=required-after-deploy-ok")
|
||||
print("command_transport=disabled")
|
||||
print("gelios=untouched")
|
||||
print("dns=unchanged")
|
||||
@@ -1540,8 +1664,10 @@ def apply_artifact(artifact_argument: str):
|
||||
apply_backhaul(loaded["payload"])
|
||||
elif loaded["phase"] == "relay":
|
||||
apply_relay(loaded["payload"])
|
||||
else:
|
||||
elif loaded["phase"] == "core-channel":
|
||||
apply_core_channel(loaded["payload"])
|
||||
else:
|
||||
apply_tailscale_retirement(loaded["payload"])
|
||||
|
||||
archived = archive_artifact(loaded["artifact"], APPLIED_ROOT)
|
||||
record = {
|
||||
|
||||
Reference in New Issue
Block a user