feat: implement RMASTER-WP-0023 private-by-default exposure
Add the exposure contract, additive family schema fields, validator checks and fixtures, the reef-railiance exception snapshot, and routed intakes. Enforcement stays in the owning repos.
This commit is contained in:
parent
6301798fab
commit
4a664533d3
24 changed files with 998 additions and 13 deletions
12
tools/testdata/family-declarations/bad-exposure/rail-kubernetes/declarations/rail.yaml
vendored
Normal file
12
tools/testdata/family-declarations/bad-exposure/rail-kubernetes/declarations/rail.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
kind: execution-rail
|
||||
rail_id: rail-kubernetes
|
||||
repo: rail-kubernetes
|
||||
ownership_repo: railiance-cluster
|
||||
contract_version: 1.0.0
|
||||
composition_kind: base
|
||||
execution_architecture: kubernetes
|
||||
readiness_state: verified
|
||||
required_substrate_capabilities:
|
||||
- kubernetes-api
|
||||
supported_rollout_modes:
|
||||
- stage1-run
|
||||
37
tools/testdata/family-declarations/bad-exposure/rapp-nogrant/declarations/rapp.yaml
vendored
Normal file
37
tools/testdata/family-declarations/bad-exposure/rapp-nogrant/declarations/rapp.yaml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
kind: managed-workload-package
|
||||
repo_family: rapp
|
||||
rapp_id: rapp-nogrant
|
||||
repo: rapp-nogrant
|
||||
ownership_repo: railiance-platform
|
||||
contract_version: 1.0.0
|
||||
readiness_state: declared
|
||||
workload_identity:
|
||||
name: nogrant
|
||||
package_type: helm-managed-platform-service
|
||||
data_classification: internal
|
||||
criticality: medium
|
||||
primary_rail: rail-kubernetes
|
||||
supported_rails:
|
||||
- rail-kubernetes
|
||||
bound_reefs:
|
||||
- reef-example
|
||||
runtime_dependencies:
|
||||
- kubernetes-api
|
||||
composition:
|
||||
purpose: Fixture rapp used to exercise exposure validation.
|
||||
member_repos:
|
||||
- repo: rapp-nogrant
|
||||
role: package
|
||||
deployables:
|
||||
- nogrant
|
||||
rollout_contract:
|
||||
default_mode: helm-upgrade-install
|
||||
smoke_contract:
|
||||
required:
|
||||
- example-status-ok
|
||||
rollback_contract:
|
||||
order:
|
||||
- helm-rollback
|
||||
exposure:
|
||||
posture: public
|
||||
binding_admission: production-approved
|
||||
42
tools/testdata/family-declarations/bad-exposure/rapp-unapproved/declarations/rapp.yaml
vendored
Normal file
42
tools/testdata/family-declarations/bad-exposure/rapp-unapproved/declarations/rapp.yaml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
kind: managed-workload-package
|
||||
repo_family: rapp
|
||||
rapp_id: rapp-unapproved
|
||||
repo: rapp-unapproved
|
||||
ownership_repo: railiance-platform
|
||||
contract_version: 1.0.0
|
||||
readiness_state: declared
|
||||
workload_identity:
|
||||
name: unapproved
|
||||
package_type: helm-managed-platform-service
|
||||
data_classification: internal
|
||||
criticality: medium
|
||||
primary_rail: rail-kubernetes
|
||||
supported_rails:
|
||||
- rail-kubernetes
|
||||
bound_reefs:
|
||||
- reef-example
|
||||
runtime_dependencies:
|
||||
- kubernetes-api
|
||||
composition:
|
||||
purpose: Fixture rapp used to exercise exposure validation.
|
||||
member_repos:
|
||||
- repo: rapp-unapproved
|
||||
role: package
|
||||
deployables:
|
||||
- unapproved
|
||||
rollout_contract:
|
||||
default_mode: helm-upgrade-install
|
||||
smoke_contract:
|
||||
required:
|
||||
- example-status-ok
|
||||
rollback_contract:
|
||||
order:
|
||||
- helm-rollback
|
||||
exposure:
|
||||
posture: public
|
||||
binding_admission: verified
|
||||
grant:
|
||||
hostname: unapproved.example
|
||||
reason: should fail
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-platform
|
||||
42
tools/testdata/family-declarations/bad-exposure/rapp-wrongreef/declarations/rapp.yaml
vendored
Normal file
42
tools/testdata/family-declarations/bad-exposure/rapp-wrongreef/declarations/rapp.yaml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
kind: managed-workload-package
|
||||
repo_family: rapp
|
||||
rapp_id: rapp-wrongreef
|
||||
repo: rapp-wrongreef
|
||||
ownership_repo: railiance-platform
|
||||
contract_version: 1.0.0
|
||||
readiness_state: declared
|
||||
workload_identity:
|
||||
name: wrongreef
|
||||
package_type: helm-managed-platform-service
|
||||
data_classification: internal
|
||||
criticality: medium
|
||||
primary_rail: rail-kubernetes
|
||||
supported_rails:
|
||||
- rail-kubernetes
|
||||
bound_reefs:
|
||||
- reef-closed
|
||||
runtime_dependencies:
|
||||
- kubernetes-api
|
||||
composition:
|
||||
purpose: Fixture rapp used to exercise exposure validation.
|
||||
member_repos:
|
||||
- repo: rapp-wrongreef
|
||||
role: package
|
||||
deployables:
|
||||
- wrongreef
|
||||
rollout_contract:
|
||||
default_mode: helm-upgrade-install
|
||||
smoke_contract:
|
||||
required:
|
||||
- example-status-ok
|
||||
rollback_contract:
|
||||
order:
|
||||
- helm-rollback
|
||||
exposure:
|
||||
posture: public
|
||||
binding_admission: production-approved
|
||||
grant:
|
||||
hostname: closed.example
|
||||
reason: reef has no public surface
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-platform
|
||||
12
tools/testdata/family-declarations/bad-exposure/reef-closed/declarations/reef.yaml
vendored
Normal file
12
tools/testdata/family-declarations/bad-exposure/reef-closed/declarations/reef.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
kind: substrate-reef
|
||||
reef_id: reef-closed
|
||||
repo: reef-closed
|
||||
ownership_repo: railiance-infra
|
||||
substrate_kind: server-group
|
||||
lifecycle_state: active
|
||||
criticality: high
|
||||
primary_rail: rail-kubernetes
|
||||
hosted_rails:
|
||||
- rail-kubernetes
|
||||
current_members:
|
||||
- Closed01
|
||||
20
tools/testdata/family-declarations/bad-exposure/reef-example/declarations/reef.yaml
vendored
Normal file
20
tools/testdata/family-declarations/bad-exposure/reef-example/declarations/reef.yaml
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
kind: substrate-reef
|
||||
reef_id: reef-example
|
||||
repo: reef-example
|
||||
ownership_repo: railiance-infra
|
||||
substrate_kind: server-group
|
||||
lifecycle_state: active
|
||||
criticality: high
|
||||
primary_rail: rail-kubernetes
|
||||
hosted_rails:
|
||||
- rail-kubernetes
|
||||
current_members:
|
||||
- Example01
|
||||
exposure:
|
||||
posture: public
|
||||
grants:
|
||||
- hostname: example.coulomb.social
|
||||
port: 443
|
||||
reason: fixture public DNS surface
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-infra
|
||||
19
tools/testdata/family-declarations/bad-exposure/reef-k3s/declarations/reef.yaml
vendored
Normal file
19
tools/testdata/family-declarations/bad-exposure/reef-k3s/declarations/reef.yaml
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
kind: substrate-reef
|
||||
reef_id: reef-k3s
|
||||
repo: reef-k3s
|
||||
ownership_repo: railiance-infra
|
||||
substrate_kind: server-group
|
||||
lifecycle_state: active
|
||||
criticality: high
|
||||
primary_rail: rail-kubernetes
|
||||
hosted_rails:
|
||||
- rail-kubernetes
|
||||
current_members:
|
||||
- K3s01
|
||||
exposure:
|
||||
posture: public
|
||||
grants:
|
||||
- port: 6443
|
||||
reason: must be rejected
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-infra
|
||||
15
tools/testdata/family-declarations/bad-exposure/reef-storage-example/declarations/reef.yaml
vendored
Normal file
15
tools/testdata/family-declarations/bad-exposure/reef-storage-example/declarations/reef.yaml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
kind: substrate-reef
|
||||
reef_id: reef-storage-example
|
||||
repo: reef-storage-example
|
||||
ownership_repo: railiance-platform
|
||||
substrate_kind: object-storage
|
||||
lifecycle_state: planned
|
||||
criticality: high
|
||||
current_members: []
|
||||
exposure:
|
||||
posture: public
|
||||
grants:
|
||||
- hostname: s3.example
|
||||
reason: provider API is not a listener we open
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-platform
|
||||
8
tools/testdata/family-declarations/good-exposure/inventory.json
vendored
Normal file
8
tools/testdata/family-declarations/good-exposure/inventory.json
vendored
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"source": "fixture",
|
||||
"captured_at": "2026-08-15T00:00:00Z",
|
||||
"reef": "reef-example",
|
||||
"deployables": [
|
||||
{"name": "example", "namespace": "example", "kind": "helm-release"}
|
||||
]
|
||||
}
|
||||
21
tools/testdata/family-declarations/good-exposure/rail-knative/declarations/rail.yaml
vendored
Normal file
21
tools/testdata/family-declarations/good-exposure/rail-knative/declarations/rail.yaml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
kind: execution-rail
|
||||
rail_id: rail-knative
|
||||
repo: rail-knative
|
||||
ownership_repo: railiance-cluster
|
||||
contract_version: 1.0.0
|
||||
composition_kind: derived
|
||||
base_rail: rail-kubernetes
|
||||
base_rail_contract: 1.0.0
|
||||
execution_architecture: knative-serving-on-kubernetes
|
||||
readiness_state: verified
|
||||
required_substrate_capabilities:
|
||||
- kubernetes-api
|
||||
- knative-serving
|
||||
supported_rollout_modes:
|
||||
- revision-canary
|
||||
inherited_semantics:
|
||||
- workload-identity
|
||||
overridden_semantics:
|
||||
- request-activation
|
||||
compatibility_constraints:
|
||||
- rail-kubernetes contract must equal 1.0.0
|
||||
12
tools/testdata/family-declarations/good-exposure/rail-kubernetes/declarations/rail.yaml
vendored
Normal file
12
tools/testdata/family-declarations/good-exposure/rail-kubernetes/declarations/rail.yaml
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
kind: execution-rail
|
||||
rail_id: rail-kubernetes
|
||||
repo: rail-kubernetes
|
||||
ownership_repo: railiance-cluster
|
||||
contract_version: 1.0.0
|
||||
composition_kind: base
|
||||
execution_architecture: kubernetes
|
||||
readiness_state: verified
|
||||
required_substrate_capabilities:
|
||||
- kubernetes-api
|
||||
supported_rollout_modes:
|
||||
- stage1-run
|
||||
42
tools/testdata/family-declarations/good-exposure/rapp-example/declarations/rapp.yaml
vendored
Normal file
42
tools/testdata/family-declarations/good-exposure/rapp-example/declarations/rapp.yaml
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
kind: managed-workload-package
|
||||
repo_family: rapp
|
||||
rapp_id: rapp-example
|
||||
repo: rapp-example
|
||||
ownership_repo: railiance-platform
|
||||
contract_version: 1.0.0
|
||||
readiness_state: declared
|
||||
workload_identity:
|
||||
name: example
|
||||
package_type: helm-managed-platform-service
|
||||
data_classification: internal
|
||||
criticality: medium
|
||||
primary_rail: rail-kubernetes
|
||||
supported_rails:
|
||||
- rail-kubernetes
|
||||
bound_reefs:
|
||||
- reef-example
|
||||
runtime_dependencies:
|
||||
- kubernetes-api
|
||||
composition:
|
||||
purpose: Fixture rapp used to exercise exposure validation.
|
||||
member_repos:
|
||||
- repo: rapp-example
|
||||
role: package
|
||||
deployables:
|
||||
- example
|
||||
rollout_contract:
|
||||
default_mode: helm-upgrade-install
|
||||
smoke_contract:
|
||||
required:
|
||||
- example-status-ok
|
||||
rollback_contract:
|
||||
order:
|
||||
- helm-rollback
|
||||
exposure:
|
||||
posture: public
|
||||
binding_admission: production-approved
|
||||
grant:
|
||||
hostname: example.coulomb.social
|
||||
reason: fixture public rapp
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-platform
|
||||
21
tools/testdata/family-declarations/good-exposure/reef-example/declarations/reef.yaml
vendored
Normal file
21
tools/testdata/family-declarations/good-exposure/reef-example/declarations/reef.yaml
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
kind: substrate-reef
|
||||
reef_id: reef-example
|
||||
repo: reef-example
|
||||
ownership_repo: railiance-infra
|
||||
substrate_kind: server-group
|
||||
lifecycle_state: active
|
||||
criticality: high
|
||||
primary_rail: rail-kubernetes
|
||||
hosted_rails:
|
||||
- rail-kubernetes
|
||||
- rail-knative
|
||||
current_members:
|
||||
- Example01
|
||||
exposure:
|
||||
posture: public
|
||||
grants:
|
||||
- hostname: example.coulomb.social
|
||||
port: 443
|
||||
reason: fixture public DNS surface
|
||||
approved_on: "2026-08-15"
|
||||
residual_risk_owner: railiance-infra
|
||||
|
|
@ -11,8 +11,11 @@ checks, in this order:
|
|||
2. sibling-field constraints JSON Schema cannot express
|
||||
3. declared member repos, named rails, and named reefs resolve on disk
|
||||
4. reef bound_rapps matches the projection of rapp.bound_reefs
|
||||
5. a deployable name belongs to at most one rapp
|
||||
6. if --inventory is given, every live deployable belongs to exactly one rapp
|
||||
5. exposure posture rules from ADR-0008 (public needs a grant and
|
||||
approved binding; rapp public needs a public reef surface; 6443 is
|
||||
never grantable; provider-delegated reefs cannot be public)
|
||||
6. a deployable name belongs to at most one rapp
|
||||
7. if --inventory is given, every live deployable belongs to exactly one rapp
|
||||
|
||||
This repo does not query a cluster. Live coverage consumes an inventory
|
||||
file produced by an implementation repo (RMASTER-WP-0021-T06).
|
||||
|
|
@ -53,6 +56,7 @@ DECL_BY_PREFIX = {
|
|||
}
|
||||
FLOATING_PIN = re.compile(r"^(latest|[\^~*]|.*\*|.*x$)", re.IGNORECASE)
|
||||
SLUG = re.compile(r"^[a-z0-9]+(-[a-z0-9]+)*$")
|
||||
FORBIDDEN_PUBLIC_PORTS = {6443}
|
||||
|
||||
|
||||
@dataclass
|
||||
|
|
@ -260,6 +264,119 @@ def check_resolution(loaded: Loaded, present: set[str], root: Path, report: Repo
|
|||
need(f"hosted_rails[{i}]", rail)
|
||||
|
||||
|
||||
def exposure_block(doc: dict[str, Any]) -> dict[str, Any] | None:
|
||||
block = doc.get("exposure")
|
||||
return block if isinstance(block, dict) else None
|
||||
|
||||
|
||||
def posture_of(doc: dict[str, Any]) -> str:
|
||||
block = exposure_block(doc)
|
||||
if block is None:
|
||||
return "private"
|
||||
posture = block.get("posture")
|
||||
return posture if isinstance(posture, str) else "private"
|
||||
|
||||
|
||||
def grants_of(loaded: Loaded) -> list[dict[str, Any]]:
|
||||
block = exposure_block(loaded.doc) if isinstance(loaded.doc, dict) else None
|
||||
if block is None:
|
||||
return []
|
||||
if loaded.family == "reef":
|
||||
return [item for item in as_list(block.get("grants")) if isinstance(item, dict)]
|
||||
grant = block.get("grant")
|
||||
return [grant] if isinstance(grant, dict) else []
|
||||
|
||||
|
||||
def reef_has_public_surface(loaded: Loaded) -> bool:
|
||||
return (
|
||||
loaded.family == "reef"
|
||||
and isinstance(loaded.doc, dict)
|
||||
and posture_of(loaded.doc) == "public"
|
||||
and bool(grants_of(loaded))
|
||||
)
|
||||
|
||||
|
||||
def reject_forbidden_ports(loaded: Loaded, report: Report) -> None:
|
||||
field_prefix = "exposure.grants" if loaded.family == "reef" else "exposure.grant"
|
||||
for i, grant in enumerate(grants_of(loaded)):
|
||||
port = grant.get("port")
|
||||
if port in FORBIDDEN_PUBLIC_PORTS:
|
||||
loc = field_prefix if loaded.family != "reef" else f"{field_prefix}[{i}].port"
|
||||
if loaded.family != "reef":
|
||||
loc = f"{field_prefix}.port"
|
||||
report.add(
|
||||
loaded.rel,
|
||||
loc,
|
||||
f"port {port} is not a grantable public surface",
|
||||
)
|
||||
|
||||
|
||||
def check_exposure(loaded_docs: list[Loaded], report: Report) -> None:
|
||||
reefs: dict[str, Loaded] = {}
|
||||
for loaded in loaded_docs:
|
||||
if loaded.family != "reef" or not isinstance(loaded.doc, dict):
|
||||
continue
|
||||
reef_id = loaded.doc.get("reef_id")
|
||||
if isinstance(reef_id, str):
|
||||
reefs[reef_id] = loaded
|
||||
|
||||
for loaded in loaded_docs:
|
||||
if not isinstance(loaded.doc, dict):
|
||||
continue
|
||||
reject_forbidden_ports(loaded, report)
|
||||
posture = posture_of(loaded.doc)
|
||||
block = exposure_block(loaded.doc) or {}
|
||||
if loaded.family == "reef":
|
||||
hosted = as_list(loaded.doc.get("hosted_rails"))
|
||||
primary = loaded.doc.get("primary_rail")
|
||||
provider_delegated = not primary and not hosted
|
||||
if posture == "public" and provider_delegated:
|
||||
report.add(
|
||||
loaded.rel,
|
||||
"exposure.posture",
|
||||
"provider-delegated reef cannot use exposure.public",
|
||||
)
|
||||
if posture == "public" and not grants_of(loaded):
|
||||
report.add(
|
||||
loaded.rel,
|
||||
"exposure.grants",
|
||||
"public reef requires at least one substrate grant",
|
||||
)
|
||||
continue
|
||||
if posture != "public":
|
||||
continue
|
||||
if not grants_of(loaded):
|
||||
report.add(
|
||||
loaded.rel,
|
||||
"exposure.grant",
|
||||
"public posture requires a grant object",
|
||||
)
|
||||
if loaded.family == "rail" and loaded.doc.get("readiness_state") != "production-approved":
|
||||
report.add(
|
||||
loaded.rel,
|
||||
"readiness_state",
|
||||
"public rail requires readiness_state production-approved",
|
||||
)
|
||||
if loaded.family != "rapp":
|
||||
continue
|
||||
admission = block.get("binding_admission")
|
||||
if admission != "production-approved":
|
||||
got = f"; got {admission!r}" if admission is not None else ""
|
||||
report.add(
|
||||
loaded.rel,
|
||||
"exposure.binding_admission",
|
||||
"public requires binding_admission production-approved "
|
||||
f"(not rapp readiness_state){got}",
|
||||
)
|
||||
bound = [item for item in as_list(loaded.doc.get("bound_reefs")) if isinstance(item, str)]
|
||||
if not any(reef_id in reefs and reef_has_public_surface(reefs[reef_id]) for reef_id in bound):
|
||||
report.add(
|
||||
loaded.rel,
|
||||
"exposure.posture",
|
||||
"public rapp is not bound to a reef that has granted a public surface",
|
||||
)
|
||||
|
||||
|
||||
def check_bound_rapps(loaded_docs: list[Loaded], report: Report) -> None:
|
||||
derived: dict[str, set[str]] = defaultdict(set)
|
||||
rapp_ids: set[str] = set()
|
||||
|
|
@ -466,6 +583,7 @@ def validate(
|
|||
check_reef(loaded, report)
|
||||
check_resolution(loaded, present, root, report)
|
||||
check_bound_rapps(loaded_docs, report)
|
||||
check_exposure(loaded_docs, report)
|
||||
check_deployable_uniqueness(loaded_docs, report)
|
||||
check_undeclared(repos, loaded_docs, root, report)
|
||||
if inventory_path is not None:
|
||||
|
|
@ -521,6 +639,30 @@ def self_test() -> int:
|
|||
if not bad.errors:
|
||||
failures.append("bad-stale-bound produced no errors")
|
||||
|
||||
exposed_root = TESTDATA / "good-exposure"
|
||||
exposed = validate(exposed_root, discover_repos(exposed_root), validators)
|
||||
if exposed.errors:
|
||||
failures.append(
|
||||
"good-exposure fixture produced errors:\n "
|
||||
+ "\n ".join(str(f) for f in exposed.errors)
|
||||
)
|
||||
|
||||
bad_exp_root = TESTDATA / "bad-exposure"
|
||||
bad_exp = validate(bad_exp_root, discover_repos(bad_exp_root), validators)
|
||||
exp_messages = "\n".join(str(f) for f in bad_exp.errors)
|
||||
expected_exposure = [
|
||||
("binding_admission production-approved", "unapproved public rapp"),
|
||||
("requires a grant object", "public rapp without grant"),
|
||||
("not bound to a reef that has granted a public surface", "public rapp on a private reef"),
|
||||
("port 6443 is not a grantable public surface", "6443 grant"),
|
||||
("provider-delegated reef cannot use exposure.public", "public storage reef"),
|
||||
]
|
||||
for needle, label in expected_exposure:
|
||||
if not any(needle in f.message for f in bad_exp.errors):
|
||||
failures.append(f"bad-exposure did not flag {label}:\n{exp_messages}")
|
||||
if not bad_exp.errors:
|
||||
failures.append("bad-exposure produced no errors")
|
||||
|
||||
live_rails = [
|
||||
Path("/home/worsch/rail-kubernetes"),
|
||||
Path("/home/worsch/rail-knative"),
|
||||
|
|
@ -541,7 +683,9 @@ def self_test() -> int:
|
|||
return 1
|
||||
print("self-test ok")
|
||||
print(f" good: {good.files} file(s), {len(good.errors)} error(s)")
|
||||
print(f" good-exposure: {exposed.files} file(s), {len(exposed.errors)} error(s)")
|
||||
print(f" bad-stale-bound: {bad.files} file(s), {len(bad.errors)} error(s) (expected)")
|
||||
print(f" bad-exposure: {bad_exp.files} file(s), {len(bad_exp.errors)} error(s) (expected)")
|
||||
return 0
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue