mirror of
https://github.com/hanzozt/zt.git
synced 2026-10-30 15:04:55 +00:00
With no subcommand, `zt controller` and `zt router` run the two roles of one pod from the environment. The controller creates its PKI, config and database on first boot and makes Hanzo IAM the only way in: an ext-jwt signer keyed on `sub`, an auth policy admitting only it, an admin per ZT_IAM_ADMINS subject, and password login refused by the default policy. The default admin the database requires gets a random password held only as its hash. Later boots create what is missing and touch nothing else. An ext-jwt signer demanded an audience match even when none was set, so one IAM issuing tokens to many clients could not be trusted as a whole. An unset audience now accepts any; issuer and signature still decide. Router enrollment verifies its token against the certificate the token's issuer serves and posts back there. Behind a TLS-terminating proxy that is the proxy's certificate, so the router beside its controller enrolls over loopback instead. Co-authored-by: Hanzo Dev <[email protected]>
51 lines
1.7 KiB
Go
51 lines
1.7 KiB
Go
package model
|
|
|
|
import (
|
|
"crypto/x509"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
"github.com/hanzozt/zt/v2/controller/db"
|
|
cmap "github.com/orcaman/concurrent-map/v2"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestVerifyTokenByInspectionAudience(t *testing.T) {
|
|
ca := newRootCa()
|
|
leaf := ca.NewLeafWithAKID()
|
|
key, err := newKey(leaf.cert, []*x509.Certificate{leaf.cert, ca.cert})
|
|
require.NoError(t, err)
|
|
resolver, err := newTestJwksResolver()
|
|
require.NoError(t, err)
|
|
resolver.AddKey(key, leaf.key)
|
|
|
|
issuer, endpoint, claim := "https://hanzo.id", "https://hanzo.id/v1/iam/.well-known/jwks", "sub"
|
|
signer := &db.ExternalJwtSigner{Name: "iam", Issuer: &issuer, JwksEndpoint: &endpoint, IdentityIdClaimsSelector: &claim, Enabled: true}
|
|
cache := &TokenIssuerCache{issuers: cmap.New[TokenIssuer]()}
|
|
cache.issuers.Set(issuer, &TokenIssuerExtJwt{externalJwtSigner: signer, jwksResolver: resolver, kidToPubKey: map[string]IssuerPublicKey{}})
|
|
|
|
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.RegisteredClaims{
|
|
Issuer: issuer,
|
|
Subject: "admin/hanzo-cloud",
|
|
Audience: jwt.ClaimStrings{"hanzo-cloud"},
|
|
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)),
|
|
})
|
|
token.Header["kid"] = key.KeyId
|
|
signed, err := token.SignedString(leaf.key)
|
|
require.NoError(t, err)
|
|
|
|
t.Run("a signer without an audience accepts any", func(t *testing.T) {
|
|
result, err := cache.VerifyTokenByInspection(signed)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "admin/hanzo-cloud", result.IdClaimValue)
|
|
})
|
|
|
|
t.Run("a signer with an audience requires it", func(t *testing.T) {
|
|
audience := "hanzo-cli"
|
|
signer.Audience = &audience
|
|
_, err := cache.VerifyTokenByInspection(signed)
|
|
require.Error(t, err)
|
|
})
|
|
}
|