mirror of
https://github.com/hanzoai/egress.git
synced 2026-10-30 15:08:38 +00:00
Proved on a host: sealed identity works, the service starts, and the store then fails — because EGRESS_KMS shipped as zap://kms.hanzo.ai:9999 and that port is a direct dial the public edge does not carry. The name resolves to the CDN, which proxies 443 and nothing else. So the documented endpoint could not be reached from the one place this service is designed to run: off the cluster. https is reachable, and it authenticates differently — a machine identity exchanged at IAM rather than an envelope signature — which this config had no fields for. It has them now, and the endpoint picks the gate: zap:// takes the mnemonic, http(s):// takes the client credentials, and a missing credential is a refusal rather than a quiet attempt at the other door with the wrong one. The client secret is sealed exactly like the mnemonic, so adding a second transport did not add a second way to hold a secret. One reader, any name. Verified on the host in this order: no credential → 243/CREDENTIALS before the process starts; sealed identity → starts, reaches the store, fails on transport; corrected endpoint → reaches IAM and names precisely what is missing. Each failure moved one gate further, which is what a fail-closed chain should look like from outside.
85 lines
2.9 KiB
Go
85 lines
2.9 KiB
Go
package egress
|
|
|
|
import (
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// THE IDENTITY IS NOT IN THE ENVIRONMENT, and this is the test that keeps it out.
|
|
//
|
|
// The store SDK will take the mnemonic from LUX_MNEMONIC, which put the one
|
|
// secret every provider credential hangs off into a plaintext file and into this
|
|
// process's environment — where /proc/<pid>/environ hands it to anything running
|
|
// as root. Invariant 3 forbids exactly that, and the identity was the one
|
|
// credential exempting itself from the rule it exists to enforce.
|
|
func TestTheIdentityIsNeverTakenFromTheEnvironment(t *testing.T) {
|
|
t.Setenv("LUX_MNEMONIC", "these words must never be read from here")
|
|
t.Setenv("MNEMONIC", "nor these")
|
|
t.Setenv("CREDENTIALS_DIRECTORY", "")
|
|
|
|
got, err := sealed("mnemonic")
|
|
if err == nil {
|
|
t.Fatalf("read %q with no sealed credential present — the environment was trusted", got)
|
|
}
|
|
if strings.Contains(err.Error(), "must never be read") {
|
|
t.Error("the refusal quoted the secret back")
|
|
}
|
|
}
|
|
|
|
// Under systemd the value comes from the credential directory, which is
|
|
// memory-backed and readable only by this unit.
|
|
func TestTheIdentityComesFromTheSealedCredential(t *testing.T) {
|
|
dir := t.TempDir()
|
|
const want = "abandon abandon abandon abandon abandon about"
|
|
if err := os.WriteFile(filepath.Join(dir, "mnemonic"), []byte(want+"\n"), 0o400); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("CREDENTIALS_DIRECTORY", dir)
|
|
t.Setenv("LUX_MNEMONIC", "a different value that must lose")
|
|
|
|
got, err := sealed("mnemonic")
|
|
if err != nil {
|
|
t.Fatalf("sealed credential present and unreadable: %v", err)
|
|
}
|
|
if got != want {
|
|
t.Errorf("read %q, want the sealed value", got)
|
|
}
|
|
}
|
|
|
|
// An empty credential is a refusal, not an empty identity. A service that
|
|
// derived an identity from "" would authenticate as something, and that
|
|
// something is not us.
|
|
func TestAnEmptySealedCredentialIsRefused(t *testing.T) {
|
|
dir := t.TempDir()
|
|
if err := os.WriteFile(filepath.Join(dir, "mnemonic"), []byte(" \n"), 0o400); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Setenv("CREDENTIALS_DIRECTORY", dir)
|
|
if _, err := sealed("mnemonic"); err == nil {
|
|
t.Error("an empty mnemonic was accepted")
|
|
}
|
|
}
|
|
|
|
// THE ENDPOINT PICKS THE GATE, and getting this wrong is what a deploy pays for.
|
|
// zap:// is a direct dial the public edge does not carry, so from off the cluster
|
|
// — which is where this service lives — https is the reachable transport and it
|
|
// authenticates by machine identity instead of by envelope signature.
|
|
func TestTheEndpointDecidesTheTransport(t *testing.T) {
|
|
for _, c := range []struct {
|
|
endpoint string
|
|
zap bool
|
|
}{
|
|
{"zap://kms.hanzo.ai:9999", true},
|
|
{"ZAP://kms.hanzo.ai:9999", true},
|
|
{"zap+mdns://_kms._tcp", true},
|
|
{"https://kms.hanzo.ai", false},
|
|
{"http://kms.hanzo.svc.cluster.local:8443", false},
|
|
} {
|
|
if got := overZAP(c.endpoint); got != c.zap {
|
|
t.Errorf("overZAP(%q) = %v, want %v", c.endpoint, got, c.zap)
|
|
}
|
|
}
|
|
}
|