feat(recovery): verify scoped delivery and create first full offsite bundle
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a13eab-1e6a-7b33-9c37-372e90ada3a2
This commit is contained in:
parent
8857d9ff6f
commit
17a8fa1495
15 changed files with 435 additions and 12 deletions
|
|
@ -420,3 +420,40 @@ checks pass after this correction. A fresh metadata capture plan confirms zero
|
|||
allow policies matching helper Pods. The next owner login timed out before
|
||||
handoff; no corrected provider probe or KV/ESO publication ran in that attempt.
|
||||
The installed runtime's timers remain disabled while attended admission resumes.
|
||||
|
||||
|
||||
## First full bundle created — 2026-10-16
|
||||
|
||||
Both exact roles passed native login, effective single-policy/900-second lifetime,
|
||||
metadata/sibling/old-escrow/token-create denials, wrong-SA/namespace/audience
|
||||
rejection, ESO value equality and temporary-reader revocation. Both ExternalSecrets
|
||||
are Ready and KV versions are1. The operator session self-revoked. CCR-0048/0049
|
||||
are applied with actual bound-claim evidence; the provider-only request5/9
|
||||
assumption has been superseded by separately approved21/25.
|
||||
|
||||
ESO adds known no-transformation defaults (`conversionStrategy: Default`,
|
||||
`decodingStrategy: None`, `metadataPolicy: None`) to its spec. The delivery guard
|
||||
now accepts only those defaults while rejecting extra sources or altered
|
||||
conversion/property/version/reader bindings. The disabled node units were
|
||||
replaced with exact previous-packet/content guards, timers verified disabled and
|
||||
inactive, and preserved rollback bytes; source release
|
||||
`758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf`
|
||||
is installed.56 focused checks passed, including native synthetic full-archive
|
||||
PostgreSQL recovery. The task-only disabled-upgrade source and packet are in the
|
||||
live evidence folder; this is not permission for an unchecked active-job upgrade.
|
||||
|
||||
Actual first capture completed under the approved node owner, with all required
|
||||
source identity guards and native data snapshots. Both temporary helpers and
|
||||
private plaintext were cleaned. The exact new encrypted artifact is:
|
||||
|
||||
- Name: `net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age`
|
||||
- Folder: Storage Share `Backup/railiance-backups/net-kingdom-identity/`
|
||||
- Bytes:109738344
|
||||
- Ciphertext SHA256:`7117b3fe1e4840c86b301c94c85e6b16709e5626bb0884bd0580bde630e30e30`
|
||||
- Status: unique publication and fresh downloaded readback verified; safe index published last.
|
||||
|
||||
Private node receipts live under `/var/lib/net-kingdom-backup/runs/` for that
|
||||
artifact. No age private key was used in capture/upload. The first independent
|
||||
owner retrieval and human hidden-key archive proof follow; both timers remain
|
||||
disabled until the actual proof is reviewed. Full directory/issuer/MFA/token and
|
||||
audit-continuity acceptance remains distinct from archive recovery.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
{
|
||||
"started_at": "2026-10-16T06:32:01.718185+00:00",
|
||||
"status": "encrypted_bundle_created",
|
||||
"decision_ref": "40bfae40-6108-4e51-8613-073339f5345f",
|
||||
"plan_run_id": "5882771aa2ce",
|
||||
"created_resources": [
|
||||
{
|
||||
"namespace": "sso",
|
||||
"kind": "NetworkPolicy",
|
||||
"name": "nk49-capture-5882771aa2ce",
|
||||
"uid": "39853a14-4875-4320-b0f5-fda26d4d04eb"
|
||||
},
|
||||
{
|
||||
"namespace": "sso",
|
||||
"kind": "Pod",
|
||||
"name": "nk49-capture-5882771aa2ce",
|
||||
"uid": "6cb00afb-05b5-4196-929b-fe5c142a9d66"
|
||||
},
|
||||
{
|
||||
"namespace": "mfa",
|
||||
"kind": "NetworkPolicy",
|
||||
"name": "nk49-capture-5882771aa2ce",
|
||||
"uid": "d9f9e2fe-56ee-4c59-950d-09d33d9c95da"
|
||||
},
|
||||
{
|
||||
"namespace": "mfa",
|
||||
"kind": "Pod",
|
||||
"name": "nk49-capture-5882771aa2ce",
|
||||
"uid": "f3328766-1a74-4272-b5b1-d9d07a4aff7a"
|
||||
}
|
||||
],
|
||||
"unverified_create_outcomes": [],
|
||||
"helper_cleanup_verified": true,
|
||||
"temporary_plaintext_cleanup_verified": true,
|
||||
"temporary_plaintext_directory": "/tmp/nk49-identity-qcsy2iyi",
|
||||
"schema": "netkingdom.identity-capture.v1",
|
||||
"ciphertext_sha256": "7117b3fe1e4840c86b301c94c85e6b16709e5626bb0884bd0580bde630e30e30",
|
||||
"ciphertext_bytes": 109738344,
|
||||
"ciphertext_path": "/var/lib/net-kingdom-backup/runs/net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age/net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age",
|
||||
"profile_sha256": "40f9e5b5bb957e5b23e03b31701702b850502d16a92a0fa0ebf92618cc3f18a1",
|
||||
"file_count": 36,
|
||||
"checks": {
|
||||
"sqlite_integrity_passed": true,
|
||||
"privacyidea_key_format_and_pair_passed": true,
|
||||
"keycape_signing_key_format_passed": true,
|
||||
"policy_json_present": true,
|
||||
"database_custom_format_present": true,
|
||||
"native_identity_recovery_proven": false
|
||||
},
|
||||
"offsite_recovery_proven": false,
|
||||
"secret_values_exported": false,
|
||||
"finished_at": "2026-10-16T06:32:35.934734+00:00"
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"schema": "netkingdom.identity-offsite.v1",
|
||||
"status": "readback_verified",
|
||||
"observed_at": "2026-10-16T06:32:39.402297+00:00",
|
||||
"provider": "Storage Share",
|
||||
"folder": "Backup/railiance-backups/net-kingdom-identity",
|
||||
"artifact": "net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age",
|
||||
"ciphertext_sha256": "7117b3fe1e4840c86b301c94c85e6b16709e5626bb0884bd0580bde630e30e30",
|
||||
"ciphertext_bytes": 109738344,
|
||||
"fetched_path": "/var/lib/net-kingdom-backup/runs/net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age/fetched.tar.age",
|
||||
"etag": "\"28ec1f7014e492de4c40e1ae75502f08\"",
|
||||
"quota_available_before": 3643309771,
|
||||
"new_object_created": true,
|
||||
"downloaded_digest_verified": true,
|
||||
"verified_index_published": true,
|
||||
"index_name": "net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age.receipt.json",
|
||||
"age_private_key_used": false,
|
||||
"secret_values_exported": false,
|
||||
"retained_objects_deleted": false,
|
||||
"native_identity_recovery_proven": false
|
||||
}
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
{
|
||||
"schema": "netkingdom.identity-runtime-install.v1",
|
||||
"status": "prepared_not_installed",
|
||||
"decision_ref": "40bfae40-6108-4e51-8613-073339f5345f",
|
||||
"release_id": "758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf",
|
||||
"release_directory": "/opt/net-kingdom-backup/releases/758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf",
|
||||
"runtime_files": [
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/capture-identity.py",
|
||||
"sha256": "47dca7c4dc96fa7eb9fb02c81fca9ca8dc5281aac918979eb7367148168ba705"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/identity-files.py",
|
||||
"sha256": "d0063a9eedd3610e3d45bd2d834aae83cff74e4b3504d138b3548f427e89331d"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/identity-bundle.py",
|
||||
"sha256": "524a03c065db4307fcde83d33af39503f3de19cb6ef3a8ca72c991d4a8c08ee6"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/identity-runtime.py",
|
||||
"sha256": "a2c0acf567c204705b90be64d561d0fda2285e0fc71a962d6e1b149f571e1940"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/identity-offsite.py",
|
||||
"sha256": "e7c31fa9ee081961af873348ee3b4829a645d40c7dc7f1e22c564aa9ed01bc49"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/identity-retention.py",
|
||||
"sha256": "a41ea4759f69c35548149fc8d0d5ace547f16cf9fe4c9dac10b119a4d0becdec"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/identity-backup-profile.json",
|
||||
"sha256": "40f9e5b5bb957e5b23e03b31701702b850502d16a92a0fa0ebf92618cc3f18a1"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/prepare-identity-coverage.py",
|
||||
"sha256": "cd5f3067d28b4c7cc93ed08b8cf3869218bdecf7c54b5dffed22d697de71dbb1"
|
||||
},
|
||||
{
|
||||
"path": "tools/bootstrap-recovery/install-identity-backup.py",
|
||||
"sha256": "5c612a2a51c9b59aad8cc01d418c2f563cbd92c62dc1fa8be994e5fb59df90f1"
|
||||
},
|
||||
{
|
||||
"path": "sso-mfa/k8s/privacyidea/key-custody-remote.py",
|
||||
"sha256": "27b2b885c81923d85054e3999c5613e104fe627779b2b50398ed9bed5088f47e"
|
||||
}
|
||||
],
|
||||
"units": {
|
||||
"net-kingdom-identity-backup.service": "[Unit]\nDescription=Encrypted full identity backup and verified Storage Share readback\nAfter=network-online.target k3s.service\nConditionPathExists=/var/lib/net-kingdom-backup/accepted-recovery.json\n[Service]\nType=oneshot\nUser=root\nGroup=root\nUMask=0077\nStateDirectory=net-kingdom-backup\nStateDirectoryMode=0700\nNoNewPrivileges=true\nPrivateTmp=true\nPrivateDevices=true\nProtectSystem=strict\nProtectHome=true\nReadWritePaths=/var/lib/net-kingdom-backup\nCapabilityBoundingSet=\nRestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\nProtectKernelTunables=true\nProtectKernelModules=true\nProtectControlGroups=true\nMemoryMax=3G\nCPUQuota=200%\nTasksMax=128\nTimeoutStartSec=45min\nTimeoutStopSec=5min\nExecStartPre=/usr/bin/python3 -I /opt/net-kingdom-backup/releases/758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf/tools/bootstrap-recovery/identity-runtime.py retention --kubeconfig /etc/rancher/k3s/k3s.yaml --state /var/lib/net-kingdom-backup\nExecStart=/usr/bin/python3 -I /opt/net-kingdom-backup/releases/758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf/tools/bootstrap-recovery/identity-runtime.py scheduled --kubeconfig /etc/rancher/k3s/k3s.yaml --state /var/lib/net-kingdom-backup --decision-id 40bfae40-6108-4e51-8613-073339f5345f\nExecStartPost=/usr/bin/python3 -I /opt/net-kingdom-backup/releases/758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf/tools/bootstrap-recovery/identity-runtime.py retention --kubeconfig /etc/rancher/k3s/k3s.yaml --state /var/lib/net-kingdom-backup\n",
|
||||
"net-kingdom-identity-backup.timer": "[Unit]\nDescription=Daily full identity recovery bundle\n[Timer]\nOnCalendar=*-*-* 03:45:00 UTC\nPersistent=true\nRandomizedDelaySec=5min\nUnit=net-kingdom-identity-backup.service\n[Install]\nWantedBy=timers.target\n",
|
||||
"net-kingdom-identity-backup-health.service": "[Unit]\nDescription=Identity backup age check with durable failure reporting\nAfter=network-online.target k3s.service\n[Service]\nType=oneshot\nUser=root\nGroup=root\nUMask=0077\nStateDirectory=net-kingdom-backup\nStateDirectoryMode=0700\nNoNewPrivileges=true\nPrivateTmp=true\nPrivateDevices=true\nProtectSystem=strict\nProtectHome=true\nReadWritePaths=/var/lib/net-kingdom-backup\nCapabilityBoundingSet=\nRestrictAddressFamilies=AF_UNIX AF_INET AF_INET6\nProtectKernelTunables=true\nProtectKernelModules=true\nProtectControlGroups=true\nMemoryMax=3G\nCPUQuota=200%\nTasksMax=128\nTimeoutStartSec=45min\nTimeoutStopSec=5min\nExecStart=/usr/bin/python3 -I /opt/net-kingdom-backup/releases/758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf/tools/bootstrap-recovery/identity-runtime.py status --kubeconfig /etc/rancher/k3s/k3s.yaml --state /var/lib/net-kingdom-backup\n",
|
||||
"net-kingdom-identity-backup-health.timer": "[Unit]\nDescription=Hourly identity backup freshness check\n[Timer]\nOnCalendar=hourly\nPersistent=true\nUnit=net-kingdom-identity-backup-health.service\n[Install]\nWantedBy=timers.target\n"
|
||||
},
|
||||
"timers_automatically_enabled": false,
|
||||
"first_capture_command": "/usr/bin/python3 -I /opt/net-kingdom-backup/releases/758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf/tools/bootstrap-recovery/identity-runtime.py first-capture --kubeconfig /etc/rancher/k3s/k3s.yaml --state /var/lib/net-kingdom-backup --decision-id 40bfae40-6108-4e51-8613-073339f5345f",
|
||||
"activation_requires_independent_recovery": true,
|
||||
"source_contains_credentials": false
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"status": "reader_and_delivery_verified",
|
||||
"phase": "negative_reader_logins",
|
||||
"native_reader_metadata": {
|
||||
"token_policies": [
|
||||
"net-kingdom-backup-publisher-eso"
|
||||
],
|
||||
"identity_policies": null,
|
||||
"policies": [
|
||||
"net-kingdom-backup-publisher-eso"
|
||||
],
|
||||
"lease_duration": 900
|
||||
},
|
||||
"capabilities_checked": {
|
||||
"auth/token/create": [
|
||||
"deny"
|
||||
],
|
||||
"platform/data/workloads/net-kingdom/identity-backup/publisher": [
|
||||
"read"
|
||||
],
|
||||
"platform/data/workloads/net-kingdom/identity-backup/publisher-sibling": [
|
||||
"deny"
|
||||
],
|
||||
"platform/data/workloads/railiance/backup/offsite-lane": [
|
||||
"deny"
|
||||
],
|
||||
"platform/metadata/workloads/net-kingdom/identity-backup/publisher": [
|
||||
"deny"
|
||||
]
|
||||
},
|
||||
"kv_version": 1,
|
||||
"wrong_namespace_probe_attempted": {
|
||||
"name": "net-kingdom-backup-publisher-eso",
|
||||
"namespace": "default"
|
||||
},
|
||||
"wrong_namespace_probe_cleanup_verified": true,
|
||||
"exact_namespace_binding_verified": true,
|
||||
"exact_policy_and_ttl_verified": true,
|
||||
"wrong_service_account_denied": true,
|
||||
"wrong_audience_denied": true,
|
||||
"wrong_namespace_denied": true,
|
||||
"metadata_sibling_escrow_and_token_create_denied": true,
|
||||
"kv_write_denied": true,
|
||||
"eso_delivery_equality_verified": true,
|
||||
"temporary_reader_revoked": true,
|
||||
"secret_values_exported": false,
|
||||
"source_ccr": "CCR-2026-0048",
|
||||
"ccr_sha256": "a7d5c356ce0c4df9de439a58ad3d36e1578f27cda327c9387e97cfc2e9dc7cdc"
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
{
|
||||
"status": "provider_and_reader_prepared",
|
||||
"action": "publisher",
|
||||
"caller_entity_id": "4a1f663c-0621-0483-edd0-f852274f4b6b",
|
||||
"caller_auth_path": "auth/netkingdom/oidc/callback",
|
||||
"quota_before": {
|
||||
"quota_bytes": 10737418240,
|
||||
"used_bytes": 7094108469,
|
||||
"available_bytes": 3643309771
|
||||
},
|
||||
"folder_created": false,
|
||||
"share_created": false,
|
||||
"share_id": "26",
|
||||
"share_permissions": 21,
|
||||
"folder": "/railiance-backups/net-kingdom-identity",
|
||||
"provider_test_artifacts": [
|
||||
"nk49-boundary-2d01d6795bb8422282c9af18c965df49.age",
|
||||
"nk49-boundary-7d6dd29f0d5a495cab2ee6e51bed1843.age"
|
||||
],
|
||||
"boundary_fixture_cleanup_verified": true,
|
||||
"boundary_phase": "deny_other_account_folder",
|
||||
"parent_probe_status": 400,
|
||||
"parent_path_rejected": true,
|
||||
"other_account_folder_status": 401,
|
||||
"provider_boundary_verified": true,
|
||||
"owner_fixture_cleanup_verified": true,
|
||||
"publisher_quota_available": 3643309771,
|
||||
"coding_agent_fence_verified_before_publication": true,
|
||||
"kv_version": 1,
|
||||
"coding_agents_denied": true,
|
||||
"escrow_read": false,
|
||||
"operator_password_delivered": false,
|
||||
"retained_backups_deleted": false,
|
||||
"source_ccr": "CCR-2026-0048",
|
||||
"ccr_sha256": "a7d5c356ce0c4df9de439a58ad3d36e1578f27cda327c9387e97cfc2e9dc7cdc",
|
||||
"eso_delivery_and_native_login_verification_pending": true
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"status": "reader_and_delivery_verified",
|
||||
"phase": "negative_reader_logins",
|
||||
"native_reader_metadata": {
|
||||
"token_policies": [
|
||||
"net-kingdom-backup-retention-eso"
|
||||
],
|
||||
"identity_policies": null,
|
||||
"policies": [
|
||||
"net-kingdom-backup-retention-eso"
|
||||
],
|
||||
"lease_duration": 900
|
||||
},
|
||||
"capabilities_checked": {
|
||||
"auth/token/create": [
|
||||
"deny"
|
||||
],
|
||||
"platform/data/workloads/net-kingdom/identity-backup/retention": [
|
||||
"read"
|
||||
],
|
||||
"platform/data/workloads/net-kingdom/identity-backup/retention-sibling": [
|
||||
"deny"
|
||||
],
|
||||
"platform/data/workloads/railiance/backup/offsite-lane": [
|
||||
"deny"
|
||||
],
|
||||
"platform/metadata/workloads/net-kingdom/identity-backup/retention": [
|
||||
"deny"
|
||||
]
|
||||
},
|
||||
"kv_version": 1,
|
||||
"wrong_namespace_probe_attempted": {
|
||||
"name": "net-kingdom-backup-retention-eso",
|
||||
"namespace": "default"
|
||||
},
|
||||
"wrong_namespace_probe_cleanup_verified": true,
|
||||
"exact_namespace_binding_verified": true,
|
||||
"exact_policy_and_ttl_verified": true,
|
||||
"wrong_service_account_denied": true,
|
||||
"wrong_audience_denied": true,
|
||||
"wrong_namespace_denied": true,
|
||||
"metadata_sibling_escrow_and_token_create_denied": true,
|
||||
"kv_write_denied": true,
|
||||
"eso_delivery_equality_verified": true,
|
||||
"temporary_reader_revoked": true,
|
||||
"secret_values_exported": false,
|
||||
"source_ccr": "CCR-2026-0049",
|
||||
"ccr_sha256": "d391018abbda0fbc1acec4b5a22c0f99ce65525de940431df376a7552b3746cf"
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"status": "provider_and_reader_prepared",
|
||||
"action": "retention",
|
||||
"caller_entity_id": "4a1f663c-0621-0483-edd0-f852274f4b6b",
|
||||
"caller_auth_path": "auth/netkingdom/oidc/callback",
|
||||
"quota_before": {
|
||||
"quota_bytes": 10737418240,
|
||||
"used_bytes": 7094108469,
|
||||
"available_bytes": 3643309771
|
||||
},
|
||||
"folder_created": false,
|
||||
"share_created": true,
|
||||
"share_id": "29",
|
||||
"share_permissions": 25,
|
||||
"folder": "/railiance-backups/net-kingdom-identity",
|
||||
"provider_test_artifacts": [
|
||||
"nk49-boundary-6f5413c177894fdabdfb7828ef76f823.age",
|
||||
"nk49-absent-abb34d9dafdc46e2babb5922b2d341d7.age"
|
||||
],
|
||||
"boundary_fixture_cleanup_verified": true,
|
||||
"boundary_phase": "deny_other_account_folder",
|
||||
"parent_probe_status": 400,
|
||||
"parent_path_rejected": true,
|
||||
"other_account_folder_status": 401,
|
||||
"provider_boundary_verified": true,
|
||||
"coding_agent_fence_verified_before_publication": true,
|
||||
"kv_version": 1,
|
||||
"coding_agents_denied": true,
|
||||
"escrow_read": false,
|
||||
"operator_password_delivered": false,
|
||||
"retained_backups_deleted": false,
|
||||
"source_ccr": "CCR-2026-0049",
|
||||
"ccr_sha256": "d391018abbda0fbc1acec4b5a22c0f99ce65525de940431df376a7552b3746cf",
|
||||
"eso_delivery_and_native_login_verification_pending": true
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
{
|
||||
"status": "runtime_installed_provider_admission_waiting_attended_login",
|
||||
"status": "actual_full_bundle_readback_verified_independent_recovery_pending",
|
||||
"observed_at": "2026-10-15T23:56:00.293983+00:00",
|
||||
"approval_decision": "40bfae40-6108-4e51-8613-073339f5345f",
|
||||
"approved_platform_source_commit": "69a792c",
|
||||
"first_owner_session": "child_failed_session_revoked",
|
||||
"second_owner_session": "metadata_review_succeeded_session_revoked",
|
||||
"new_identity_bundle_created": false,
|
||||
"new_identity_bundle_created": true,
|
||||
"identity_job_installed": true,
|
||||
"new_credentials_published_to_openbao": false,
|
||||
"new_credentials_published_to_openbao": true,
|
||||
"existing_backup_changes": false,
|
||||
"observed_permissions": 21,
|
||||
"requested_permissions": 5,
|
||||
|
|
@ -35,10 +35,10 @@
|
|||
"publisher": 21,
|
||||
"retention": 25
|
||||
},
|
||||
"focused_tests_passed": 52,
|
||||
"focused_tests_passed": 56,
|
||||
"temporary_dns_selector_exclusions_applied": true,
|
||||
"network_policy_rule_lines_present": 649,
|
||||
"installed_runtime_release": "bf8b38f185037515cfad9eebcddefc9f60ace724c32e10d89ae8307dc95a4eb7",
|
||||
"installed_runtime_release": "758ba4d170ba5ed4b6c563d3a379e496c5f81e8bff28c45d142a6bacbfaa21bf",
|
||||
"current_login_session_active": false,
|
||||
"latest_login_failure": "callback_timeout_before_handoff",
|
||||
"latest_timeout_possible_session_revocation_confirmed": false,
|
||||
|
|
@ -46,6 +46,10 @@
|
|||
"publisher_fixture_cleanup_verified": true,
|
||||
"parent_path_rejection_status": 400,
|
||||
"corrected_boundary_tests_passed": 16,
|
||||
"corrected_native_boundary_and_delivery_not_yet_run": true,
|
||||
"helper_network_allow_count": 0
|
||||
"corrected_native_boundary_and_delivery_not_yet_run": false,
|
||||
"helper_network_allow_count": 0,
|
||||
"actual_provider_boundary_and_native_delivery_verified": true,
|
||||
"actual_ciphertext_bytes": 109738344,
|
||||
"actual_ciphertext_sha256": "7117b3fe1e4840c86b301c94c85e6b16709e5626bb0884bd0580bde630e30e30",
|
||||
"actual_independent_key_recovery_pending": true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
"""Exact task-only replacement of disabled NK49 units; preserve rollback bytes."""
|
||||
import hashlib,importlib.util,json,os,subprocess,sys,tempfile
|
||||
from pathlib import Path
|
||||
os.umask(0o077)
|
||||
root=Path(sys.argv[1]);old=Path(sys.argv[2]);out=Path(sys.argv[3])
|
||||
expected_old='bb5579ce72967f0ce78e6e0afb361cfd5e3793768d3eb5b422718033505a0014'
|
||||
assert hashlib.sha256(old.read_bytes()).hexdigest()==expected_old
|
||||
previous=json.loads(old.read_text());packet=json.loads((root/'install-packet.json').read_text())
|
||||
s=importlib.util.spec_from_file_location('installer',root/'tools/bootstrap-recovery/install-identity-backup.py');m=importlib.util.module_from_spec(s);s.loader.exec_module(m)
|
||||
m.owner();assert packet==m.render(packet['decision_ref'])
|
||||
assert packet['decision_ref']==previous['decision_ref']
|
||||
for unit in ['net-kingdom-identity-backup.timer','net-kingdom-identity-backup-health.timer']:
|
||||
for action,expected in [('is-enabled',b'disabled'),('is-active',b'inactive')]:
|
||||
r=subprocess.run(['systemctl',action,unit],stdout=subprocess.PIPE,stderr=subprocess.DEVNULL,timeout=20);assert r.stdout.strip()==expected
|
||||
paths={name:Path('/etc/systemd/system')/name for name in previous['units']}
|
||||
assert set(paths)==set(packet['units'])
|
||||
for name,path in paths.items():assert not path.is_symlink() and path.read_text()==previous['units'][name]
|
||||
backup=Path(tempfile.mkdtemp(prefix='nk49-disabled-unit-rollback-',dir='/tmp'))
|
||||
try:
|
||||
for name,path in paths.items():path.rename(backup/name)
|
||||
m.install(packet)
|
||||
except BaseException:
|
||||
for name,path in paths.items():
|
||||
if (backup/name).exists():
|
||||
assert not path.exists() or path.read_text()==packet['units'][name]
|
||||
os.replace(backup/name,path)
|
||||
subprocess.run(['systemctl','daemon-reload'],stdout=subprocess.DEVNULL,stderr=subprocess.DEVNULL,timeout=30)
|
||||
raise
|
||||
out.write_text(json.dumps({'status':'installed_timers_disabled','release_id':packet['release_id'],'previous_release':previous['release_id'],'rollback_units':str(backup)},indent=2)+'\n')
|
||||
|
|
@ -104,13 +104,14 @@ consumer_guidance:
|
|||
or recovery
|
||||
- 2026-10-14 architecture review estimates operational maturity separately; a registry
|
||||
entry does not authorize execution or establish deployment.
|
||||
- Identity backup implementation passed 49 focused checks including synthetic age/native
|
||||
PostgreSQL recovery; new CCR-2026-0048/0049 grants and first actual full bundle
|
||||
remain pending owner admission.
|
||||
- Actual old privacyIDEA database recovery passed; full token decryption, audit
|
||||
continuity and directory/issuer authentication remain open.
|
||||
- Storage Share initial destination only; no second-provider full identity copy
|
||||
or autonomous bare-VM restore is established.
|
||||
- Actual first full identity bundle captured, sealed and Storage Share readback
|
||||
verified on2026-10-16; both scoped readers passed native identity/namespace/audience/ESO
|
||||
checks. Independent human-key full archive proof and complete service authentication
|
||||
remain pending.56 focused implementation checks pass.
|
||||
---
|
||||
|
||||
# Guarded bootstrap and recovery preparation
|
||||
|
|
|
|||
|
|
@ -182,3 +182,18 @@ def test_provider_parent_400_requires_independent_outside_folder_denial(monkeypa
|
|||
with pytest.raises(setup.Refused):setup.boundary(None,owner_auth,token,'publisher',receipt)
|
||||
assert not receipt.get('provider_boundary_verified')
|
||||
assert not store
|
||||
|
||||
|
||||
def test_eso_native_default_fields_preserve_exact_source_scope(monkeypatch):
|
||||
import base64
|
||||
from copy import deepcopy
|
||||
es={'metadata':{'uid':'fixture-eso'},'spec':{'target':{'name':'net-kingdom-backup-publisher'},'secretStoreRef':{'kind':'SecretStore','name':'net-kingdom-backup-publisher'},'data':[{'secretKey':'token','remoteRef':{'key':'workloads/net-kingdom/identity-backup/publisher','property':'NC_WEBDAV_TOKEN','conversionStrategy':'Default','decodingStrategy':'None','metadataPolicy':'None'}}]},'status':{'conditions':[{'type':'Ready','status':'True'}]}}
|
||||
secret={'metadata':{'ownerReferences':[{'uid':'fixture-eso'}]},'data':{'token':base64.b64encode(TOKEN.encode()).decode()}}
|
||||
monkeypatch.setattr(runtime.capture,'get',lambda ns,kind,name:es if kind=='externalsecret' else secret)
|
||||
assert runtime.token('publisher')==TOKEN
|
||||
original=deepcopy(es)
|
||||
for key,value in [('conversionStrategy','Unicode'),('property','OTHER'),('version','old')]:
|
||||
es.clear();es.update(deepcopy(original));es['spec']['data'][0]['remoteRef'][key]=value
|
||||
with pytest.raises(runtime.offsite.Refused):runtime.token('publisher')
|
||||
es.clear();es.update(deepcopy(original));es['spec']['dataFrom']=[{'extract':{'key':'foreign'}}]
|
||||
with pytest.raises(runtime.offsite.Refused):runtime.token('publisher')
|
||||
|
|
|
|||
|
|
@ -40,8 +40,15 @@ def write(path,value):
|
|||
def token(action):
|
||||
name='net-kingdom-backup-'+action
|
||||
es=capture.get('sso','externalsecret',name)
|
||||
if (es['spec']['target']['name']!=name or es['spec']['data']!=[{'secretKey':'token','remoteRef':{
|
||||
entries=[]
|
||||
for entry in es['spec'].get('data',[]):
|
||||
item=dict(entry);ref=dict(item.get('remoteRef',{}))
|
||||
for key,default in [('conversionStrategy','Default'),('decodingStrategy','None'),('metadataPolicy','None')]:
|
||||
if ref.pop(key,default)!=default:raise offsite.Refused()
|
||||
item['remoteRef']=ref;entries.append(item)
|
||||
if (es['spec']['target']['name']!=name or entries!=[{'secretKey':'token','remoteRef':{
|
||||
'key':'workloads/net-kingdom/identity-backup/'+action,'property':'NC_WEBDAV_TOKEN'}}]
|
||||
or es['spec'].get('dataFrom') or es['spec'].get('secretStoreRef')!={'kind':'SecretStore','name':name}
|
||||
or not any(c['type']=='Ready' and c['status']=='True' for c in es['status'].get('conditions',[]))):raise offsite.Refused()
|
||||
secret=capture.get('sso','secret',name)
|
||||
if (set(secret['data'])!={'token'} or not any(o['uid']==es['metadata']['uid'] for o in secret['metadata'].get('ownerReferences',[]))):raise offsite.Refused()
|
||||
|
|
|
|||
|
|
@ -21,10 +21,13 @@ from state_hub_preflight_lane import bao,data,capabilities,revoke
|
|||
|
||||
|
||||
def verify(action,expected,receipt,kubeconfig):
|
||||
receipt['phase']='validate_approved_contract'
|
||||
ccr,lane=setup.contract(action,expected)
|
||||
receipt['phase']='verify_attended_identity'
|
||||
ident=data(bao(['token','lookup','-format=json']))['data']
|
||||
if 'platform-admin' not in ident.get('policies',[]) or 'root' in ident.get('policies',[]) or not ident.get('path','').startswith('auth/netkingdom/'):raise setup.Refused()
|
||||
capture=runtime.capture;capture.KUBE=['kubectl','--kubeconfig',kubeconfig]
|
||||
receipt['phase']='verify_cluster_identity'
|
||||
if capture.get(None,'namespace','kube-system')['metadata']['uid']!=capture.prepare.CLUSTER_UID:raise setup.Refused()
|
||||
def login(sa,audience,negative=False,namespace='sso'):
|
||||
jwt=capture.command(['-n',namespace,'create','token',sa,'--audience='+audience,'--duration=10m']).decode().strip()
|
||||
|
|
@ -35,18 +38,25 @@ def verify(action,expected,receipt,kubeconfig):
|
|||
if b'403' not in result.stderr and b'400' not in result.stderr:raise setup.Refused()
|
||||
return None
|
||||
return data(result)['auth']
|
||||
receipt['phase']='native_reader_login'
|
||||
auth=login(lane['service_account'],'openbao');token=auth['client_token']
|
||||
receipt['native_reader_metadata']={k:auth.get(k) for k in ['token_policies','identity_policies','policies','lease_duration']}
|
||||
try:
|
||||
receipt['phase']='effective_reader_policy_ttl'
|
||||
if (auth['token_policies']!=[lane['policy']] or auth.get('identity_policies')
|
||||
or auth.get('policies',auth['token_policies'])!=[lane['policy']] or not 0<auth['lease_duration']<=900):raise setup.Refused()
|
||||
paths=[lane['kv_path'],lane['kv_path'].replace('/data/','/metadata/',1),lane['kv_path']+'-sibling',
|
||||
'platform/data/workloads/railiance/backup/offsite-lane','auth/token/create']
|
||||
receipt['phase']='reader_capabilities'
|
||||
cap=capabilities(token,paths)
|
||||
receipt['capabilities_checked']=cap
|
||||
if cap[paths[0]]!=['read'] or any(cap[p]!=['deny'] for p in paths[1:]):raise setup.Refused()
|
||||
receipt['phase']='native_kv_eso_equality'
|
||||
native=data(bao(['read','-format=json',lane['kv_path']],token=token))['data']
|
||||
if set(native['data'])!={'NC_WEBDAV_TOKEN'} or native['data']['NC_WEBDAV_TOKEN']!=runtime.token(action):raise setup.Refused()
|
||||
receipt['kv_version']=native['metadata']['version']
|
||||
finally:revoke(token)
|
||||
receipt['phase']='negative_reader_logins'
|
||||
login('default','openbao',True);login(lane['service_account'],'wrong-audience',True)
|
||||
proposed={'apiVersion':'v1','kind':'ServiceAccount','metadata':{'name':lane['service_account'],'namespace':'default'},'automountServiceAccountToken':False}
|
||||
receipt['wrong_namespace_probe_attempted']=proposed['metadata']
|
||||
|
|
|
|||
|
|
@ -558,6 +558,23 @@ Fresh capture metadata reports zero helper allow policies. Installed timers are
|
|||
disabled. No new coding-agent credential or root fallback; partial owner share26
|
||||
is undistributed. Pending browser status from operator, without duplicate logins.
|
||||
|
||||
2026-10-16 first actual full identity backup: provider/reader admission completed.
|
||||
Both exact native roles have one policy/900-second TTL and deny sibling,
|
||||
metadata, old escrow and token-create; wrong-SA/namespace/audience negative cases
|
||||
passed, ESO values matched KV version1 and temporary readers/session were revoked.
|
||||
Fixed strict ESO comparison to accept only its known no-transformation defaults.
|
||||
56 focused checks passed, including real synthetic encrypted/native PG recovery.
|
||||
The disabled runtime was updated through exact previous-unit/source guards.
|
||||
|
||||
First full original-key/current-data capture and Storage Share publication plus
|
||||
fresh downloaded hash readback succeeded: artifact
|
||||
`net-kingdom-identity-20261016T063159Z-5882771aa2ce.tar.age`,109738344bytes,
|
||||
SHA256`7117b3fe1e4840c86b301c94c85e6b16709e5626bb0884bd0580bde630e30e30`.
|
||||
Helper and plaintext cleanup verified. No age private key was used by the agent.
|
||||
Independent owner retrieval/human-key recovery is next; both timers remain
|
||||
disabled. Full native service/token/audit/caller and legacy credential
|
||||
acceptance remain open; T02 progress/T03 wait.
|
||||
|
||||
## Verify owner-approved recovery and current native callers
|
||||
|
||||
```task
|
||||
|
|
@ -565,7 +582,7 @@ id: NK-WP-0049-T03
|
|||
status: wait
|
||||
priority: high
|
||||
depends_on: [NK-WP-0049-T02]
|
||||
blocking_reason: "The actual off-host PI database decrypted and restored with the independently held key; wrong-key refusal and cleanup passed. Operator reports no known full identity bundle and requested missing coverage preparation. Full-profile source adapters, bootstrap handoff and discovery are implemented/tested, but CCR-2026-0048/0049 owner admission, first actual full capture/off-site proof and native token/audit/directory/issuer recovery remain open, along with T02 current-owner and legacy-credential disposition. No live bootstrap/root retrieval or deferred exposure rotation is authorized."
|
||||
blocking_reason: "Both new scoped readers passed native identity and ESO delivery acceptance. The first full identity bundle was captured and Storage Share readback verified with helper/plaintext cleanup. Independent owner download and attended human-key archive recovery remain pending; both timers are disabled. Full directory/issuer/MFA/token/audit continuity plus current caller and T02 legacy credential acceptance remain open. No root fallback or deferred exposure rotation is authorized."
|
||||
state_hub_task_id: "d68cd4d3-1ba3-530e-88f9-26ebb8dd6eac"
|
||||
```
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue