Skip to content

Self-enroll OpenZiti identity at startup via ziti-management #89

Open

Description

@rowan-stein

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

  1. 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. Calls RequestServiceIdentity(service_type: SERVICE_TYPE_GATEWAY).
    c. Receives ziti_identity_id + identity_json (bytes).
    d. Writes identity_json to a temp file on ephemeral disk (e.g., os.CreateTemp("", "ziti-identity-*.json")).
    e. Loads the identity via ziti.NewContextFromFile(tempFile).
    f. Proceeds as before: ListenWithOptions("gateway", ...).

  2. Lease renewal: starts a background goroutine that calls ExtendIdentityLease(ziti_identity_id) every ZITI_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.

  3. 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions