Skip to content

Setting Up Subdomains Realms

Frank Yglesias Bertheau edited this page Jul 7, 2026 · 1 revision
<h1>ᚾ&nbsp;NornGate — Setting Up Subdomains (Realms)</h1>

Provision a trust zone. A realm is not just DNS — a subdomain is a trust boundary, so provisioning one means binding a name to a posture and a bundle of isolation resources. The topology you build here is the threat model. This guide runs a real example end to end: standing up Jotunheim, the untrusted execution realm.

Field names on the Realm resource are the derived config model (Configuration); the Kubernetes and Istio resources are real APIs.


ᚨ Before you start

Decide two things first, because everything else follows from them:

  • Posture — trusted / untrusted / edge / federated. Jotunheim is untrusted: it runs code that must be assumed hostile.
  • Tier — Heavens / Middle / Below. Posture and tier together fix the isolation defaults.

A realm is done only when it is gated (requests to its subdomain traverse G0–G4) and isolated (traffic from other realms is denied). You will verify both.


ᛟ Define the realm

The Realm resource binds the subdomain to its posture and references its isolation resources.

apiVersion: norngate.io/v1
kind: Realm
metadata: { name: jotunheim }
spec:
  subdomain: "jotun.*"
  tier: middle
  posture: untrusted
  routeTimeout: 60s
  credentials: none
  networkPolicyRef: jotunheim-default-deny
  quotaRef: jotunheim-quota

ᛉ Isolate it

Four resources make the boundary real. Apply all of them — a subdomain without them is a label, not a zone.

Network — default-deny. Deny all pod traffic, then allow only the spine in and brokered egress out.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: jotunheim-default-deny, namespace: jotunheim }
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - namespaceSelector: { matchLabels: { realm: asgard } }
  egress:
    - to:
        - namespaceSelector: { matchLabels: { egress: broker } }

Why: this stops lateral movement — a compromised Jotunheim pod cannot reach Midgard's PII, because the only ingress it accepts is the control plane and the only egress is the broker.

Identity — STRICT mTLS. Every peer must present a SPIFFE identity; no plaintext.

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: { name: jotunheim-mtls, namespace: jotunheim }
spec:
  mtls: { mode: STRICT }

Why: STRICT mTLS is what makes G1's subject attribute trustworthy — the identity comes from a signed SVID, not the request body, closing attribute forgery (Gates & Attributes).

Authorization — deny by default. An ALLOW policy with no rules permits nothing until you add explicit rules.

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata: { name: jotunheim-deny, namespace: jotunheim }
spec: {}

Why: deny-by-default at the mesh mirrors default-deny at G1 — the realm refuses traffic it was not explicitly told to accept.

Quota — cap the blast radius.

apiVersion: v1
kind: ResourceQuota
metadata: { name: jotunheim-quota, namespace: jotunheim }
spec:
  hard: { requests.cpu: "50", requests.memory: 100Gi, pods: "200" }

Why: an untrusted realm must not be able to exhaust the cluster — the quota bounds a runaway or hostile workload to its own zone.


ᛖ Wire to the spine

Route the subdomain to the realm's runtime through the ingress gateway, with the realm's route timeout (distinct from gate timeouts).

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata: { name: jotunheim-route, namespace: jotunheim }
spec:
  hosts: ["jotun.norngate.com"]
  gateways: ["asgard/ingress"]
  http:
    - timeout: 60s
      route:
        - destination: { host: jotunheim-runtime.jotunheim.svc.cluster.local }

Why: routing through the spine is what puts the realm behind the gates — there is no path to the runtime that skips the sidecar, so total mediation holds (The Spine).


ᛗ Credential posture

Jotunheim carries credentials: nonezero standing credentials. Workloads hold no keys; a credential is injected only into a committed action and only at the boundary.

Why: if the realm you assume is hostile holds nothing to steal, a compromise yields nothing to replay. This is credential starvation applied at the zone level (Security & Fail-closed).


ᛒ Apply, verify, roll back

# 1. validate + apply the bundle
norngate-cli config validate jotunheim/*.yaml
norngate-cli config apply jotunheim/*.yaml
norngate-cli realm describe jotunheim

Verify it is gated — a request to jotun.norngate.com must produce gate records:

norngate-cli audit query --realm jotunheim --from now-5m

If G0–G4 records appear, the realm is on the gated path. If none do, traffic is bypassing the spine — stop and fix the VirtualService before proceeding.

Verify it is isolated — attempt traffic from another realm (e.g. a Midgard pod) to a Jotunheim pod; it must be denied by the NetworkPolicy and AuthorizationPolicy. A realm that answers cross-realm traffic is not yet a boundary.

Roll back — the realm and its resources are versioned config; re-pin or delete the bundle:

norngate-cli config apply --pin <previous-version>

ᚱ Next steps


Iconography

Section glyphs are Elder Futhark runes (Unicode Runic block, U+16A0–U+16FF) — semantic, not emoji. Full set on Home:

Rune Name Gloss Marks
Nauðiz need, constraint the platform mark
Ansuz the word, instruction before you start
Othala enclosed estate, boundary define the realm
Algiz protection, warding isolate it
Ehwaz the horse, conveyance wire to the spine
Mannaz the self, identity credential posture
Berkanan growth, the change made apply / verify / roll back
Raidō the ride, the road next steps

References. Kubernetes NetworkPolicy + ResourceQuota; Istio PeerAuthentication / AuthorizationPolicy / VirtualService (per The Spine); SPIFFE/SPIRE; NIST SP 800-207 (zero-trust / microsegmentation — the basis for topology-as-threat-model). Naming doctrine: Prose Edda / Poetic Edda, per Norse Cosmology & Platform Design.

ᚾ NornGate — Setting Up Subdomains (Realms)

Provision a trust zone. A realm is not just DNS — a subdomain is a trust boundary, so provisioning one means binding a name to a posture and a bundle of isolation resources. The topology you build here is the threat model. This guide runs a real example end to end: standing up Jotunheim, the untrusted execution realm.

Field names on the Realm resource are the derived config model ([Configuration](Configuration)); the Kubernetes and Istio resources are real APIs.


ᚨ Before you start

Decide two things first, because everything else follows from them:

  • Posture — trusted / untrusted / edge / federated. Jotunheim is untrusted: it runs code that must be assumed hostile.
  • Tier — Heavens / Middle / Below. Posture and tier together fix the isolation defaults.

A realm is done only when it is gated (requests to its subdomain traverse G0–G4) and isolated (traffic from other realms is denied). You will verify both.


ᛟ Define the realm

The Realm resource binds the subdomain to its posture and references its isolation resources.

apiVersion: norngate.io/v1
kind: Realm
metadata: { name: jotunheim }
spec:
  subdomain: "jotun.*"
  tier: middle
  posture: untrusted
  routeTimeout: 60s
  credentials: none
  networkPolicyRef: jotunheim-default-deny
  quotaRef: jotunheim-quota

ᛉ Isolate it

Four resources make the boundary real. Apply all of them — a subdomain without them is a label, not a zone.

Network — default-deny. Deny all pod traffic, then allow only the spine in and brokered egress out.

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: jotunheim-default-deny, namespace: jotunheim }
spec:
  podSelector: {}
  policyTypes: [Ingress, Egress]
  ingress:
    - from:
        - namespaceSelector: { matchLabels: { realm: asgard } }
  egress:
    - to:
        - namespaceSelector: { matchLabels: { egress: broker } }

Why: this stops lateral movement — a compromised Jotunheim pod cannot reach Midgard's PII, because the only ingress it accepts is the control plane and the only egress is the broker.

Identity — STRICT mTLS. Every peer must present a SPIFFE identity; no plaintext.

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata: { name: jotunheim-mtls, namespace: jotunheim }
spec:
  mtls: { mode: STRICT }

Why: STRICT mTLS is what makes G1's subject attribute trustworthy — the identity comes from a signed SVID, not the request body, closing attribute forgery ([Gates & Attributes](Gates-and-Attributes)).

Authorization — deny by default. An ALLOW policy with no rules permits nothing until you add explicit rules.

apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata: { name: jotunheim-deny, namespace: jotunheim }
spec: {}

Why: deny-by-default at the mesh mirrors default-deny at G1 — the realm refuses traffic it was not explicitly told to accept.

Quota — cap the blast radius.

apiVersion: v1
kind: ResourceQuota
metadata: { name: jotunheim-quota, namespace: jotunheim }
spec:
  hard: { requests.cpu: "50", requests.memory: 100Gi, pods: "200" }

Why: an untrusted realm must not be able to exhaust the cluster — the quota bounds a runaway or hostile workload to its own zone.


ᛖ Wire to the spine

Route the subdomain to the realm's runtime through the ingress gateway, with the realm's route timeout (distinct from gate timeouts).

apiVersion: networking.istio.io/v1
kind: VirtualService
metadata: { name: jotunheim-route, namespace: jotunheim }
spec:
  hosts: ["jotun.norngate.com"]
  gateways: ["asgard/ingress"]
  http:
    - timeout: 60s
      route:
        - destination: { host: jotunheim-runtime.jotunheim.svc.cluster.local }

Why: routing through the spine is what puts the realm behind the gates — there is no path to the runtime that skips the sidecar, so total mediation holds ([The Spine](The-Spine)).


ᛗ Credential posture

Jotunheim carries credentials: nonezero standing credentials. Workloads hold no keys; a credential is injected only into a committed action and only at the boundary.

Why: if the realm you assume is hostile holds nothing to steal, a compromise yields nothing to replay. This is credential starvation applied at the zone level ([Security & Fail-closed](Security-and-Fail-Closed)).


ᛒ Apply, verify, roll back

# 1. validate + apply the bundle
norngate-cli config validate jotunheim/*.yaml
norngate-cli config apply jotunheim/*.yaml
norngate-cli realm describe jotunheim

Verify it is gated — a request to jotun.norngate.com must produce gate records:

norngate-cli audit query --realm jotunheim --from now-5m

If G0–G4 records appear, the realm is on the gated path. If none do, traffic is bypassing the spine — stop and fix the VirtualService before proceeding.

Verify it is isolated — attempt traffic from another realm (e.g. a Midgard pod) to a Jotunheim pod; it must be denied by the NetworkPolicy and AuthorizationPolicy. A realm that answers cross-realm traffic is not yet a boundary.

Roll back — the realm and its resources are versioned config; re-pin or delete the bundle:

norngate-cli config apply --pin <previous-version>

ᚱ Next steps


Iconography

Section glyphs are Elder Futhark runes (Unicode Runic block, U+16A0–U+16FF) — semantic, not emoji. Full set on [Home](Home#iconography):

Rune Name Gloss Marks
Nauðiz need, constraint the platform mark
Ansuz the word, instruction before you start
Othala enclosed estate, boundary define the realm
Algiz protection, warding isolate it
Ehwaz the horse, conveyance wire to the spine
Mannaz the self, identity credential posture
Berkanan growth, the change made apply / verify / roll back
Raidō the ride, the road next steps

References. [Kubernetes NetworkPolicy](https://kubernetes.io/docs/concepts/services-networking/network-policies/) + ResourceQuota; Istio PeerAuthentication / AuthorizationPolicy / VirtualService (per [The Spine](The-Spine)); [SPIFFE](https://spiffe.io/)/SPIRE; [NIST SP 800-207](https://csrc.nist.gov/pubs/sp/800/207/final) (zero-trust / microsegmentation — the basis for topology-as-threat-model). Naming doctrine: Prose Edda / Poetic Edda, per [Norse Cosmology & Platform Design](Norse-Cosmology-and-Platform-Design).

Clone this wiki locally