Repository navigation
Self-enroll OpenZiti identity at startup via ziti-management #89
Description
User Request
Replace static identity file provisioning with runtime self-enrollment. The Gateway should call ziti-management.RequestServiceIdentity() at startup to obtain an enrolled OpenZiti identity, and periodically extend the lease via ExtendIdentityLease().
See Service Identity Self-Enrollment.
Specification
Current Behavior
The Gateway reads a pre-provisioned identity file from ZITI_IDENTITY_FILE env var and calls ziti.NewContextFromFile().
Target Behavior
-
At startup, if OpenZiti is enabled (determined by a config flag — see below), the Gateway:
a. Creates a gRPC client to ziti-management (already exists:zitimgmtclient.Client).
b. CallsRequestServiceIdentity(service_type: SERVICE_TYPE_GATEWAY).
c. Receivesziti_identity_id+identity_json(bytes).
d. Writesidentity_jsonto a temp file on ephemeral disk (e.g.,os.CreateTemp("", "ziti-identity-*.json")).
e. Loads the identity viaziti.NewContextFromFile(tempFile).
f. Proceeds as before:ListenWithOptions("gateway", ...). -
Lease renewal: starts a background goroutine that calls
ExtendIdentityLease(ziti_identity_id)everyZITI_LEASE_RENEWAL_INTERVAL(default: 2 minutes — well under the 5-minute lease TTL). On error, log and retry on next tick. On context cancellation, stop. -
Shutdown: temp file is cleaned up via
defer os.Remove(tempFile).
Config Changes
Replace ZITI_IDENTITY_FILE with a boolean-like enablement. The identity file is no longer externally provided — it's obtained at runtime.
| Env Var | Default | Description |
|---|---|---|
ZITI_ENABLED |
false |
Enable OpenZiti self-enrollment |
ZITI_MANAGEMENT_GRPC_TARGET |
ziti-management:50051 |
Already exists |
ZITI_LEASE_RENEWAL_INTERVAL |
2m |
How often to extend the lease |
ZITI_SERVICE_NAME |
gateway |
Already exists |
Remove ZITI_IDENTITY_FILE from the config — it's no longer used.
Changes to zitimgmtclient
The existing zitimgmtclient.Client only has ResolveIdentity. Add:
RequestServiceIdentity(ctx, serviceType) (zitiIdentityID string, identityJSON []byte, error)ExtendIdentityLease(ctx, zitiIdentityID string) error
This requires regenerating the proto stubs from the updated BSR (which now includes the new RPCs).
Changes to main.go
The OpenZiti setup block (currently gated on config.ZitiIdentityFile != "") should be replaced with a self-enrollment flow gated on config.ZitiEnabled:
if config.ZitiEnabled {
// 1. Request identity from ziti-management
zitiIdentityID, identityJSON, err := zitiMgmtClient.RequestServiceIdentity(ctx, SERVICE_TYPE_GATEWAY)
// 2. Write to temp file
tmpFile, err := os.CreateTemp("", "ziti-identity-*.json")
tmpFile.Write(identityJSON)
tmpFile.Close()
defer os.Remove(tmpFile.Name())
// 3. Load identity
zitiContext, err := ziti.NewContextFromFile(tmpFile.Name())
defer zitiContext.Close()
// 4. Start lease renewal
go renewLease(ctx, zitiMgmtClient, zitiIdentityID, leaseRenewalInterval)
// 5. Listen (same as before)
listener, err := zitiContext.ListenWithOptions(serviceName, ziti.DefaultListenOptions())
go server.Serve(listener)
}Proto Stub Regeneration
The Gateway needs to pull the updated proto from BSR to get the new RequestServiceIdentity and ExtendIdentityLease RPCs. Run buf dep update + buf generate to regenerate stubs.
Lease Renewal Function
func renewLease(ctx context.Context, client *zitimgmtclient.Client, identityID string, interval time.Duration) {
ticker := time.NewTicker(interval)
defer ticker.Stop()
for {
select {
case <-ctx.Done():
return
case <-ticker.C:
if err := client.ExtendIdentityLease(ctx, identityID); err != nil {
log.Printf("failed to extend ziti lease: %v", err)
}
}
}
}
Activity