mirror of
https://github.com/hanzoai/egress.git
synced 2026-10-30 15:08:38 +00:00
A caller sends a request with no vendor credential. Egress authenticates the workload behind it, checks that workload may spend on that provider, rewrites the call onto an address it chose itself, and attaches the credential it holds in memory. The credential is never returned and never written down. Three allowlists, none of them reachable from a request field: the provider table fixes the destination, the operation set fixes which calls exist, and the grant table fixes who may spend on what. A caller names a provider; it cannot name a host, so the credential can never sign a request to somewhere else. Callers present the projected service-account token the kubelet mints for them. Egress asks the cluster what one means rather than verifying it here, which keeps revocation instant and leaves no signature checking to get wrong. Credentials resolve from KMS at boot and refresh on a timer, so a value rewritten there is in use within the interval with no redeploy. A read names the full coordinate - path, name and environment - because a bare name resolves to a different partition and a missing environment to a different environment, and both return "not found" for a record that exists.
72 lines
2.8 KiB
YAML
72 lines
2.8 KiB
YAML
# Network policy is written as Cilium policy throughout, because two of the
|
|
# things that need saying — "the cluster API server" and "this vendor by name" —
|
|
# cannot be said in a plain NetworkPolicy, and expressing half the rules in one
|
|
# dialect and half in another leaves nobody able to read the whole boundary.
|
|
#
|
|
# The credential no longer living in a caller is the control. These policies are
|
|
# the second latch: they make the single path structural, so a credential that
|
|
# somehow reached a pod again still could not be spent from there.
|
|
apiVersion: cilium.io/v2
|
|
kind: CiliumNetworkPolicy
|
|
metadata:
|
|
name: egress
|
|
namespace: hanzo-egress
|
|
spec:
|
|
endpointSelector:
|
|
matchLabels: {app: egress}
|
|
ingress:
|
|
# Only the namespaces that hold a granted workload may open a connection at
|
|
# all. The grant table inside egress is the authority on who may spend; this
|
|
# keeps everything else from reaching the door to try.
|
|
- fromEndpoints:
|
|
- matchLabels: {"k8s:io.kubernetes.pod.namespace": hanzo}
|
|
- matchLabels: {"k8s:io.kubernetes.pod.namespace": zen}
|
|
- matchLabels: {"k8s:io.kubernetes.pod.namespace": enso}
|
|
toPorts:
|
|
- ports: [{port: "8080", protocol: TCP}]
|
|
egress:
|
|
# Name resolution.
|
|
- toEndpoints:
|
|
- matchLabels:
|
|
{"k8s:io.kubernetes.pod.namespace": kube-system, "k8s:k8s-app": kube-dns}
|
|
toPorts:
|
|
- ports: [{port: "53", protocol: UDP}, {port: "53", protocol: TCP}]
|
|
rules:
|
|
dns: [{matchPattern: "*"}]
|
|
# Asking the cluster to authenticate a caller's token.
|
|
- toEntities: [kube-apiserver]
|
|
# Reading its own credentials from the secrets plane.
|
|
- toEndpoints:
|
|
- matchLabels: {"k8s:io.kubernetes.pod.namespace": hanzo, "k8s:app": cloud}
|
|
toPorts:
|
|
- ports: [{port: "8000", protocol: TCP}]
|
|
# The one vendor it may reach, by name. Adding a provider means adding it
|
|
# here as well as to the table in the binary — deliberately two places,
|
|
# because reaching a new third party should be visible in the network
|
|
# boundary and not only in code.
|
|
- toFQDNs:
|
|
- {matchName: "openrouter.ai"}
|
|
toPorts:
|
|
- ports: [{port: "443", protocol: TCP}]
|
|
---
|
|
# The other half: nothing except egress may reach the vendor directly.
|
|
#
|
|
# enableDefaultDeny is false on purpose and MUST stay false. A clusterwide policy
|
|
# that enabled it would put every endpoint in the cluster into default-deny for
|
|
# egress, which is a total outage. This adds one prohibition and changes nothing
|
|
# else about what any workload may do.
|
|
apiVersion: cilium.io/v2
|
|
kind: CiliumClusterwideNetworkPolicy
|
|
metadata:
|
|
name: vendor-direct-deny
|
|
spec:
|
|
endpointSelector:
|
|
matchExpressions:
|
|
- {key: app, operator: NotIn, values: [egress]}
|
|
enableDefaultDeny:
|
|
egress: false
|
|
ingress: false
|
|
egressDeny:
|
|
- toFQDNs:
|
|
- {matchName: "openrouter.ai"}
|