Skip to content

Commit

Permalink
Merge pull request #2368 from openziti/fix.2345.isCertExtendable
Browse files Browse the repository at this point in the history
Fix.2345.is cert extendable
  • Loading branch information
andrewpmartinez committed Sep 4, 2024
2 parents c405c48 + f7ed367 commit 107aa4b
Show file tree
Hide file tree
Showing 36 changed files with 1,092 additions and 864 deletions.
12 changes: 7 additions & 5 deletions common/oidc_tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,12 @@ const (
CustomClaimsCertFingerprints = "z_cfs"

// CustomClaimsTokenType and other constants below may not appear as referenced, but are used in `json: ""` tags. Provided here for external use.
CustomClaimsTokenType = "z_t"
CustomClaimServiceId = "z_sid"
CustomClaimIdentityId = "z_iid"
CustomClaimServiceType = "z_st"
CustomClaimRemoteAddress = "z_ra"
CustomClaimsTokenType = "z_t"
CustomClaimServiceId = "z_sid"
CustomClaimIdentityId = "z_iid"
CustomClaimServiceType = "z_st"
CustomClaimRemoteAddress = "z_ra"
CustomClaimIsCertExtendable = "z_ice"

DefaultAccessTokenDuration = 30 * time.Minute
DefaultIdTokenDuration = 30 * time.Minute
Expand All @@ -66,6 +67,7 @@ type CustomClaims struct {
SdkInfo *rest_model.SdkInfo `json:"z_sdk"`
EnvInfo *rest_model.EnvInfo `json:"z_env"`
RemoteAddress string `json:"z_ra"`
IsCertExtendable bool `json:"z_ice"`
}

func (c *CustomClaims) ToMap() (map[string]any, error) {
Expand Down
1,529 changes: 770 additions & 759 deletions common/pb/edge_cmd_pb/edge_cmd.pb.go

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions common/pb/edge_cmd_pb/edge_cmd.proto
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ message Authenticator {
string pem = 2;
string unverifiedFingerprint = 3;
string unverifiedPem = 4;
bool isIssuedByNetwork = 5;
}

message Updb {
Expand Down
36 changes: 20 additions & 16 deletions controller/db/api_session_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ import (
)

const (
FieldApiSessionIdentity = "identity"
FieldApiSessionToken = "token"
FieldApiSessionConfigTypes = "configTypes"
FieldApiSessionIPAddress = "ipAddress"
FieldApiSessionMfaComplete = "mfaComplete"
FieldApiSessionMfaRequired = "mfaRequired"
FieldApiSessionLastActivityAt = "lastActivityAt"
FieldApiSessionAuthenticator = "authenticator"
FieldApiSessionIdentity = "identity"
FieldApiSessionToken = "token"
FieldApiSessionConfigTypes = "configTypes"
FieldApiSessionIPAddress = "ipAddress"
FieldApiSessionMfaComplete = "mfaComplete"
FieldApiSessionMfaRequired = "mfaRequired"
FieldApiSessionLastActivityAt = "lastActivityAt"
FieldApiSessionAuthenticator = "authenticator"
FieldApiSessionIsCertExtendable = "isCertExtendable"

EventFullyAuthenticated events.EventName = "FULLY_AUTHENTICATED"

Expand All @@ -47,14 +48,15 @@ const (

type ApiSession struct {
boltz.BaseExtEntity
IdentityId string `json:"identityId"`
Token string `json:"-"`
IPAddress string `json:"ipAddress"`
ConfigTypes []string `json:"configTypes"`
MfaComplete bool `json:"mfaComplete"`
MfaRequired bool `json:"mfaRequired"`
LastActivityAt time.Time `json:"lastActivityAt"`
AuthenticatorId string `json:"authenticatorId"`
IdentityId string `json:"identityId"`
Token string `json:"-"`
IPAddress string `json:"ipAddress"`
ConfigTypes []string `json:"configTypes"`
MfaComplete bool `json:"mfaComplete"`
MfaRequired bool `json:"mfaRequired"`
LastActivityAt time.Time `json:"lastActivityAt"`
AuthenticatorId string `json:"authenticatorId"`
IsCertExtendable bool `json:"isCertExtendable"`
}

func NewApiSession(identityId string) *ApiSession {
Expand Down Expand Up @@ -111,6 +113,7 @@ func (store *apiSessionStoreImpl) FillEntity(entity *ApiSession, bucket *boltz.T
entity.MfaComplete = bucket.GetBoolWithDefault(FieldApiSessionMfaComplete, false)
entity.MfaRequired = bucket.GetBoolWithDefault(FieldApiSessionMfaRequired, false)
entity.AuthenticatorId = bucket.GetStringWithDefault(FieldApiSessionAuthenticator, "")
entity.IsCertExtendable = bucket.GetBoolWithDefault(FieldApiSessionIsCertExtendable, false)
lastActivityAt := bucket.GetTime(FieldApiSessionLastActivityAt) //not orError due to migration v18

if lastActivityAt != nil {
Expand All @@ -128,6 +131,7 @@ func (store *apiSessionStoreImpl) PersistEntity(entity *ApiSession, ctx *boltz.P
ctx.SetBool(FieldApiSessionMfaRequired, entity.MfaRequired)
ctx.SetString(FieldApiSessionAuthenticator, entity.AuthenticatorId)
ctx.SetTimeP(FieldApiSessionLastActivityAt, &entity.LastActivityAt)
ctx.SetBool(FieldApiSessionIsCertExtendable, entity.IsCertExtendable)
}

func (store *apiSessionStoreImpl) GetEventsEmitter() events.EventEmmiter {
Expand Down
15 changes: 10 additions & 5 deletions controller/db/authenticator_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,9 @@ const (
FieldAuthenticatorMethod = "method"
FieldAuthenticatorIdentity = "identity"

FieldAuthenticatorCertFingerprint = "certFingerprint"
FieldAuthenticatorCertPem = "certPem"
FieldAuthenticatorCertFingerprint = "certFingerprint"
FieldAuthenticatorCertPem = "certPem"
FieldAuthenticatorCertIsIssuedByNetwork = "isIssuedByNetwork"

FieldAuthenticatorUnverifiedCertPem = "unverifiedCertPem"
FieldAuthenticatorUnverifiedCertFingerprint = "unverifiedCertFingerprint"
Expand All @@ -52,9 +53,10 @@ type AuthenticatorSubType interface {
}

type AuthenticatorCert struct {
Authenticator `json:"-"`
Fingerprint string `json:"fingerprint"`
Pem string `json:"pem"`
Authenticator `json:"-"`
Fingerprint string `json:"fingerprint"`
Pem string `json:"pem"`
IsIssuedByNetwork bool `json:"IsIssuedByNetwork"`

UnverifiedPem string `json:"unverifiedPem"`
UnverifiedFingerprint string `json:"unverifiedFingerprint"`
Expand Down Expand Up @@ -145,6 +147,7 @@ func (store *authenticatorStoreImpl) initializeLocal() {
store.AddSymbol(FieldAuthenticatorMethod, ast.NodeTypeString)
store.AddSymbol(FieldAuthenticatorCertFingerprint, ast.NodeTypeString)
store.AddSymbol(FieldAuthenticatorCertPem, ast.NodeTypeString)
store.AddSymbol(FieldAuthenticatorCertIsIssuedByNetwork, ast.NodeTypeBool)
store.AddSymbol(FieldAuthenticatorUpdbUsername, ast.NodeTypeString)
store.AddSymbol(FieldAuthenticatorUpdbPassword, ast.NodeTypeString)
store.AddSymbol(FieldAuthenticatorUpdbSalt, ast.NodeTypeString)
Expand All @@ -168,6 +171,7 @@ func (store *authenticatorStoreImpl) FillEntity(entity *Authenticator, bucket *b
authCert := &AuthenticatorCert{}
authCert.Fingerprint = bucket.GetStringWithDefault(FieldAuthenticatorCertFingerprint, "")
authCert.Pem = bucket.GetStringWithDefault(FieldAuthenticatorCertPem, "")
authCert.IsIssuedByNetwork = bucket.GetBoolWithDefault(FieldAuthenticatorCertIsIssuedByNetwork, false)

authCert.UnverifiedPem = bucket.GetStringWithDefault(FieldAuthenticatorUnverifiedCertPem, "")
authCert.UnverifiedFingerprint = bucket.GetStringWithDefault(FieldAuthenticatorUnverifiedCertFingerprint, "")
Expand All @@ -192,6 +196,7 @@ func (store *authenticatorStoreImpl) PersistEntity(entity *Authenticator, ctx *b
if authCert, ok := entity.SubType.(*AuthenticatorCert); ok {
ctx.SetString(FieldAuthenticatorCertFingerprint, authCert.Fingerprint)
ctx.SetString(FieldAuthenticatorCertPem, authCert.Pem)
ctx.SetBool(FieldAuthenticatorCertIsIssuedByNetwork, authCert.IsIssuedByNetwork)

ctx.SetString(FieldAuthenticatorUnverifiedCertFingerprint, authCert.UnverifiedFingerprint)
ctx.SetString(FieldAuthenticatorUnverifiedCertPem, authCert.UnverifiedPem)
Expand Down
88 changes: 88 additions & 0 deletions controller/db/migration_v37.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
/*
Copyright NetFoundry Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package db

import (
"fmt"
nfpem "github.com/openziti/foundation/v2/pem"
"github.com/openziti/storage/ast"
"github.com/openziti/storage/boltz"
)

func (m *Migrations) setAuthenticatorIsIssuedByNetwork(step *boltz.MigrationStep) {
cursor := m.stores.Authenticator.IterateIds(step.Ctx.Tx(), ast.BoolNodeTrue)

if m.signingCert == nil {
return //test instances do not load a config and do not have a signing cert
}

var toUpdate []string
for cursor.IsValid() {
id := string(cursor.Current())
authenticator, err := m.stores.Authenticator.LoadById(step.Ctx.Tx(), id)

if err != nil {
step.SetError(fmt.Errorf("error loading authenticator[%s]: %s", id, err))
break
}

if authenticator.Type == MethodAuthenticatorCert {
cert := authenticator.ToCert()
if cert == nil {
step.SetError(fmt.Errorf("error converting authenticator [%s] to sub type certificate, got nil back", err))
break
}

certs := nfpem.PemStringToCertificates(cert.Pem)
if len(certs) == 0 {
step.SetError(fmt.Errorf("certificate authenticator [%s] have a PEM that decoded to 0 x509.Certificates", id))
break
}

if err := certs[0].CheckSignatureFrom(m.signingCert); err == nil {
toUpdate = append(toUpdate, id)
}
}

cursor.Next()
}

for _, id := range toUpdate {
authenticator, err := m.stores.Authenticator.LoadById(step.Ctx.Tx(), id)

if err != nil {
step.SetError(fmt.Errorf("error loading authenticator[%s]: %s", id, err))
return
}

if authenticator == nil {
step.SetError(fmt.Errorf("authenticator [%s] not found", id))
return
}

cert := authenticator.ToCert()
cert.IsIssuedByNetwork = true

err = m.stores.Authenticator.Update(step.Ctx, authenticator, boltz.MapFieldChecker{
FieldAuthenticatorCertIsIssuedByNetwork: struct{}{},
})

if err != nil {
step.SetError(fmt.Errorf("error updating authenticator[%s]: %s", id, err))
}
}
}
15 changes: 11 additions & 4 deletions controller/db/migrations.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,23 +17,26 @@
package db

import (
"crypto/x509"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/storage/boltz"
"github.com/pkg/errors"
)

const (
CurrentDbVersion = 36
CurrentDbVersion = 37
FieldVersion = "version"
)

type Migrations struct {
stores *Stores
stores *Stores
signingCert *x509.Certificate
}

func RunMigrations(db boltz.Db, stores *Stores) error {
func RunMigrations(db boltz.Db, stores *Stores, signingCert *x509.Certificate) error {
migrations := &Migrations{
stores: stores,
stores: stores,
signingCert: signingCert,
}

mm := boltz.NewMigratorManager(db)
Expand Down Expand Up @@ -171,6 +174,10 @@ func (m *Migrations) migrate(step *boltz.MigrationStep) int {
m.dropEntity(step, EntityTypeApiSessionCertificates)
}

if step.CurrentVersion < 37 {
m.setAuthenticatorIsIssuedByNetwork(step)
}

// current version
if step.CurrentVersion <= CurrentDbVersion {
return CurrentDbVersion
Expand Down
5 changes: 3 additions & 2 deletions controller/db/stores.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package db

import (
"context"
"crypto/x509"
"errors"
"github.com/michaelquigley/pfxlog"
"github.com/openziti/foundation/v2/errorz"
Expand Down Expand Up @@ -238,7 +239,7 @@ func (f DbProviderF) GetDb() boltz.Db {
return f()
}

func InitStores(db boltz.Db, rateLimiter rate.RateLimiter) (*Stores, error) {
func InitStores(db boltz.Db, rateLimiter rate.RateLimiter, signingCert *x509.Certificate) (*Stores, error) {
dbProvider := DbProviderF(func() boltz.Db {
return db
})
Expand Down Expand Up @@ -360,7 +361,7 @@ func InitStores(db boltz.Db, rateLimiter rate.RateLimiter) (*Stores, error) {
return nil, errorHolder.GetError()
}

if err = RunMigrations(db, externalStores); err != nil {
if err = RunMigrations(db, externalStores, signingCert); err != nil {
return nil, err
}

Expand Down
4 changes: 2 additions & 2 deletions controller/db/testing.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ func (ctx *TestContext) Init() {
ctx.InitDb(Open)

var err error
ctx.stores, err = InitStores(ctx.GetDb(), command.NoOpRateLimiter{})
ctx.stores, err = InitStores(ctx.GetDb(), command.NoOpRateLimiter{}, nil)
ctx.NoError(err)

ctx.NoError(RunMigrations(ctx.GetDb(), ctx.stores))
ctx.NoError(RunMigrations(ctx.GetDb(), ctx.stores, nil))
ctx.NoError(ctx.stores.EventualEventer.Start(ctx.closeNotify))
}

Expand Down
10 changes: 9 additions & 1 deletion controller/env/appenv.go
Original file line number Diff line number Diff line change
Expand Up @@ -548,6 +548,7 @@ func (ae *AppEnv) ProcessJwt(rc *response.RequestContext, token *jwt.Token) erro
ExpirationDuration: time.Until(rc.Claims.Expiration.AsTime()),
LastActivityAt: time.Now(),
AuthenticatorId: "oidc",
IsCertExtendable: rc.Claims.IsCertExtendable,
}

rc.AuthPolicy, err = ae.GetManagers().AuthPolicy.Read(rc.Identity.AuthPolicyId)
Expand Down Expand Up @@ -650,7 +651,14 @@ func ProcessAuthQueries(ae *AppEnv, rc *response.RequestContext) {
}

func NewAppEnv(host HostController) (*AppEnv, error) {
stores, err := db.InitStores(host.GetDb(), host.GetCommandDispatcher().GetRateLimiter())
var signingCert *x509.Certificate
cfg := host.GetConfig()

if cfg.Edge != nil && cfg.Edge.Enrollment.SigningCert != nil {
signingCert = host.GetConfig().Edge.Enrollment.SigningCert.Cert().Leaf
}

stores, err := db.InitStores(host.GetDb(), host.GetCommandDispatcher().GetRateLimiter(), signingCert)
if err != nil {
return nil, err
}
Expand Down
27 changes: 14 additions & 13 deletions controller/internal/routes/api_session_api_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ package routes
import (
"github.com/go-openapi/strfmt"
"github.com/openziti/edge-api/rest_model"
"github.com/openziti/foundation/v2/stringz"
"github.com/openziti/ziti/controller/env"
"github.com/openziti/ziti/controller/model"
"github.com/openziti/ziti/controller/response"
"github.com/openziti/ziti/controller/models"
"github.com/openziti/foundation/v2/stringz"
"github.com/openziti/ziti/controller/response"
"path"
)

Expand Down Expand Up @@ -64,17 +64,18 @@ func MapApiSessionToRestModel(ae *env.AppEnv, apiSession *model.ApiSession) (*re
lastActivityAt := strfmt.DateTime(apiSession.LastActivityAt)

ret := &rest_model.APISessionDetail{
BaseEntity: BaseEntityToRestModel(apiSession, ApiSessionLinkFactory),
IdentityID: &apiSession.IdentityId,
Identity: ToEntityRef(apiSession.Identity.Name, apiSession.Identity, IdentityLinkFactory),
Token: &apiSession.Token,
IPAddress: &apiSession.IPAddress,
ConfigTypes: stringz.SetToSlice(apiSession.ConfigTypes),
AuthQueries: rest_model.AuthQueryList{}, //not in a request context, can't fill
IsMfaComplete: &apiSession.MfaComplete,
IsMfaRequired: &apiSession.MfaRequired,
LastActivityAt: lastActivityAt,
AuthenticatorID: &apiSession.AuthenticatorId,
BaseEntity: BaseEntityToRestModel(apiSession, ApiSessionLinkFactory),
IdentityID: &apiSession.IdentityId,
Identity: ToEntityRef(apiSession.Identity.Name, apiSession.Identity, IdentityLinkFactory),
Token: &apiSession.Token,
IPAddress: &apiSession.IPAddress,
ConfigTypes: stringz.SetToSlice(apiSession.ConfigTypes),
AuthQueries: rest_model.AuthQueryList{}, //not in a request context, can't fill
IsMfaComplete: &apiSession.MfaComplete,
IsMfaRequired: &apiSession.MfaRequired,
LastActivityAt: lastActivityAt,
AuthenticatorID: &apiSession.AuthenticatorId,
IsCertExtendable: &apiSession.IsCertExtendable,
}

if ret.ConfigTypes == nil {
Expand Down
Loading

0 comments on commit 107aa4b

Please sign in to comment.