diff --git a/cmd/ateapi/main.go b/cmd/ateapi/main.go index 340ea5109a..f8d2694be2 100644 --- a/cmd/ateapi/main.go +++ b/cmd/ateapi/main.go @@ -76,7 +76,7 @@ var ( postgresSchema = pflag.String("postgres-schema", "public", "PostgreSQL schema for Substrate tables. This overrides a search_path connection parameter.") actorIDJWTPoolFile = pflag.String("actor-id-jwt-pool", "", "The file that contains the serialized JWT authority pool for signing actor JWTs") - egressGatewayAddress = pflag.String("egress-gateway-address", "", "Address of the egress PEP. Empty disables tunneled egress.") + egressGatewayAddress = pflag.String("egress-gateway-address", "", "Address of the egress PEP. Required.") actorIDCAPoolFile = pflag.String("actor-id-ca-pool", "", "The file that contains the CA pool for signing actor JWTs") podIdentityCACerts = pflag.String("pod-identity-ca-certs", "", "The file that contains the pod-identity CA bundle, used both for verifying client certificates presented to the gRPC server and for verifying atelet serving certificates when dialing atelet. If empty, client-cert verification is disabled and atelet dials will fail.") @@ -106,6 +106,9 @@ func main() { if *templateResyncInterval < minResyncInterval { serverboot.Fatal(ctx, "Invalid --template-resync-interval", fmt.Errorf("must be at least %s", minResyncInterval)) } + if err := validateEgressGatewayAddress(); err != nil { + serverboot.Fatal(ctx, "Invalid --egress-gateway-address", err) + } // Kept separate from ctx so that in-progress work (clients, informers) is // not cancelled the moment SIGTERM arrives. The drainOnShutdown @@ -375,6 +378,17 @@ func newObjectStore(ctx context.Context) (objectstore.Store, error) { } } +// validateEgressGatewayAddress refuses a configuration with no egress gateway. +// An actor started without one is not tunneled through the PEP at all, so its +// EgressPolicy goes unenforced; failing at startup keeps a cluster from +// reaching that state by omitting a flag. +func validateEgressGatewayAddress() error { + if *egressGatewayAddress == "" { + return fmt.Errorf("--egress-gateway-address is required") + } + return nil +} + // connectStore builds the PostgreSQL-backed store.Interface. Startup fails if // its configuration is missing or the database can't be reached. func connectStore(ctx context.Context) (store.Interface, error) { diff --git a/cmd/ateapi/main_test.go b/cmd/ateapi/main_test.go index b5c19f1b88..211a417f02 100644 --- a/cmd/ateapi/main_test.go +++ b/cmd/ateapi/main_test.go @@ -32,3 +32,30 @@ func TestConnectStoreRequiresPostgresConnectionString(t *testing.T) { t.Fatalf("connectStore() error = %v, want missing-connection-string error", err) } } + +func TestValidateEgressGatewayAddress(t *testing.T) { + for _, tc := range []struct { + name string + address string + wantErr bool + }{ + {name: "empty", address: "", wantErr: true}, + {name: "set", address: "atenet-egress.ate-system.svc:443"}, + } { + t.Run(tc.name, func(t *testing.T) { + oldAddress := *egressGatewayAddress + t.Cleanup(func() { + *egressGatewayAddress = oldAddress + }) + *egressGatewayAddress = tc.address + + err := validateEgressGatewayAddress() + if gotErr := err != nil; gotErr != tc.wantErr { + t.Fatalf("validateEgressGatewayAddress() error = %v, wantErr %t", err, tc.wantErr) + } + if tc.wantErr && !strings.Contains(err.Error(), "--egress-gateway-address is required") { + t.Errorf("validateEgressGatewayAddress() error = %v, want missing-address error", err) + } + }) + } +}