Skip to content

Commit

Permalink
Merge pull request #2469 from openziti/fix.2468.select.enrollment.sig…
Browse files Browse the repository at this point in the history
…ning.cert

fixes #2468 locates the correct server certificate for enrollments
  • Loading branch information
andrewpmartinez authored Oct 5, 2024
2 parents e9c8180 + 778807d commit e599a37
Show file tree
Hide file tree
Showing 4 changed files with 98 additions and 2 deletions.
87 changes: 86 additions & 1 deletion controller/env/appenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,14 +67,18 @@ import (
"github.com/pkg/errors"
"github.com/xeipuuv/gojsonschema"
"io"
"net"
"net/http"
"strings"
"time"
)

var _ model.Env = &AppEnv{}

const ZitiSession = "zt-session"
const (
ZitiSession = "zt-session"
ClientApiBinding = "edge-client"
)

const (
metricAuthLimiterCurrentQueuedCount = "auth.limiter.queued_count"
Expand Down Expand Up @@ -266,6 +270,87 @@ func (ae *AppEnv) GetConfig() *config.Config {
return ae.HostController.GetConfig()
}

// GetEnrollmentJwtSigner returns as Signer to use for enrollments based on the edge.api.address hostname
// or an error if one cannot be located that matches. Hostname matching is done across all identity server
// certificates, including alternate server certificates.
func (ae *AppEnv) GetEnrollmentJwtSigner() (jwtsigner.Signer, error) {
enrollmentCert, err := ae.getEnrollmentTlsCert()

if err != nil {
return nil, fmt.Errorf("could not determine enrollment signer: %w", err)
}

signMethod := getJwtSigningMethod(enrollmentCert)
kid := fmt.Sprintf("%x", sha1.Sum(enrollmentCert.Certificate[0]))
return jwtsigner.New(signMethod, enrollmentCert.PrivateKey, kid), nil
}

func (ae *AppEnv) getEnrollmentTlsCert() (*tls.Certificate, error) {
host, _, err := net.SplitHostPort(ae.GetConfig().Edge.Api.Address)

var hostnameErrors []error

if err != nil {
return nil, fmt.Errorf("could not parse edge.api.address for host and port during enrollment signer selection [%s]", ae.GetConfig().Edge.Api.Address)
}

tlsCert, err := ae.getCertForHostname(ae.GetConfig().Id.ServerCert(), host)

if err == nil {
return tlsCert, nil
} else {
hostnameErrors = append(hostnameErrors, err)
}

for _, serverConfig := range ae.GetHostController().GetXWebInstance().GetConfig().ServerConfigs {
clientApiFound := false
for _, curApi := range serverConfig.APIs {
if curApi.Binding() == ClientApiBinding {
clientApiFound = true
}
}

if clientApiFound {
tlsCert, err = ae.getCertForHostname(serverConfig.Identity.ServerCert(), host)

if err != nil {
hostnameErrors = append(hostnameErrors, err)
continue
}

if tlsCert != nil {
return tlsCert, nil
}
}
}

pfxlog.Logger().WithField("hostnameErrors", hostnameErrors).Errorf("could not find a server certificate for the edge.api.address host [%s]", host)

return nil, fmt.Errorf("could not find a configured server certificate that matches hostname [%s] in root controller identity nor in xweb identities", host)
}

func (ae *AppEnv) getCertForHostname(tlsCerts []*tls.Certificate, hostname string) (*tls.Certificate, error) {
for i, tlsCert := range tlsCerts {
if tlsCert.Leaf == nil {
if len(tlsCert.Certificate) > 0 {
var err error
tlsCert.Leaf, err = x509.ParseCertificate(tlsCert.Certificate[0])

if err != nil {
pfxlog.Logger().Warnf("failed to parse leading certificate in a tls configuration while determining enrollment certificate, entry at index %d is skipped, processing other certificates: %s", i, err)
continue
}
}
}

if tlsCert.Leaf.VerifyHostname(hostname) == nil {
return tlsCert, nil
}
}

return nil, fmt.Errorf("could not find a configured server certificate that matches hostname [%s]", hostname)
}

func (ae *AppEnv) GetServerJwtSigner() jwtsigner.Signer {
return ae.serverSigner
}
Expand Down
7 changes: 6 additions & 1 deletion controller/model/enrollment_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,8 +76,13 @@ func (entity *Enrollment) FillJwtInfoWithExpiresAt(env Env, subject string, expi
Subject: subject,
},
}
signer, err := env.GetEnrollmentJwtSigner()

signedJwt, err := env.GetServerJwtSigner().Generate(enrollmentClaims)
if err != nil {
return fmt.Errorf("could not get enrollment signer: %v", err)
}

signedJwt, err := signer.Generate(enrollmentClaims)

if err != nil {
return err
Expand Down
2 changes: 2 additions & 0 deletions controller/model/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ type Env interface {
GetFingerprintGenerator() cert.FingerprintGenerator
HandleServiceUpdatedEventForIdentityId(identityId string)

GetEnrollmentJwtSigner() (jwtsigner.Signer, error)

GetServerJwtSigner() jwtsigner.Signer
GetServerCert() (*tls.Certificate, string, jwt.SigningMethod)
JwtSignerKeyFunc(token *jwt.Token) (interface{}, error)
Expand Down
4 changes: 4 additions & 0 deletions controller/model/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ type TestContext struct {
dispatcher command.Dispatcher
}

func (ctx *TestContext) GetEnrollmentJwtSigner() (jwtsigner.Signer, error) {
return ctx, nil
}

func (ctx *TestContext) GetEventDispatcher() event.Dispatcher {
panic("implement me")
}
Expand Down

0 comments on commit e599a37

Please sign in to comment.