Skip to content

Commit

Permalink
new: QoL improvements (#89)
Browse files Browse the repository at this point in the history
* bootstrap: add basic authn and authz constructors

This patch refactors some helper function improving developers quality
of life regarding classic mtls and simple bahamut.Authorizer and
Authenticators.

* new: add bootstrap.MakeA3SRemoteAuth

This is a convenience function that streamlines the creation and
configuration of both a3s Authenticator and Authorizer that will be
ready to plug into a bahamut server.

* fixed: don't hardcode a3s api into MakeMongoManipulator

* remove replace

* fixed: tested function that should have never has passed
  • Loading branch information
primalmotion committed Sep 18, 2023
1 parent 3d68b50 commit 1caf6fe
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 12 deletions.
4 changes: 2 additions & 2 deletions cmd/a3s/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func main() {
}
}

m := bootstrap.MakeMongoManipulator(cfg.MongoConf, &hasher.Hasher{})
m := bootstrap.MakeMongoManipulator(cfg.MongoConf, &hasher.Hasher{}, api.Manager())
if err := indexes.Ensure(m, api.Manager(), "a3s"); err != nil {
zap.L().Fatal("Unable to ensure indexes", zap.Error(err))
}
Expand Down Expand Up @@ -376,7 +376,7 @@ func main() {

func createMongoDBAccount(cfg conf.MongoConf, username string) error {

m := bootstrap.MakeMongoManipulator(cfg, &hasher.Hasher{})
m := bootstrap.MakeMongoManipulator(cfg, &hasher.Hasher{}, api.Manager())

db, closeFunc, _ := manipmongo.GetDatabase(m)
defer closeFunc()
Expand Down
2 changes: 0 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -128,5 +128,3 @@ require (
k8s.io/helm v2.17.0+incompatible // indirect
rsc.io/qr v0.2.0 // indirect
)

replace github.com/mitchellh/mapstructure => github.com/mitchellh/mapstructure v1.4.3
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -752,8 +752,10 @@ github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrk
github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
github.com/mitchellh/hashstructure v1.1.0 h1:P6P1hdjqAAknpY/M1CGipelZgp+4y9ja9kmUZPXP+H0=
github.com/mitchellh/hashstructure v1.1.0/go.mod h1:xUDAozZz0Wmdiufv0uyhnHkUTN6/6d8ulp4AwfLKrmA=
github.com/mitchellh/mapstructure v1.4.3 h1:OVowDSCllw/YjdLkam3/sm7wEtOy59d8ndGgCcyj8cs=
github.com/mitchellh/mapstructure v1.4.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ=
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
Expand Down
85 changes: 85 additions & 0 deletions pkgs/bootstrap/auth.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package bootstrap

import (
"crypto/x509"

"go.aporeto.io/a3s/pkgs/token"
"go.aporeto.io/bahamut"
"go.aporeto.io/bahamut/authorizer/mtls"
"go.aporeto.io/bahamut/authorizer/simple"
"go.aporeto.io/elemental"
)

// MakeCNExcluderFunc returns an mtls.VerifierFunc that rejects mtls verification
// if the certificate has the given Common Name.
func MakeCNExcluderFunc(cn string) mtls.VerifierFunc {
return func(cert *x509.Certificate) bool { return cert.Subject.CommonName != cn }
}

// MakeMTLSRequestAuthenticator returns a bahamut.RequestAuthenticator that
// will allow requests presenting a client certificate signed by a CA present
// in the given caPool unless the given mtls.VerifierFunc disagrees.
func MakeMTLSRequestAuthenticator(caPool *x509.CertPool, verifier mtls.VerifierFunc) bahamut.RequestAuthenticator {
return mtls.NewMTLSRequestAuthenticator(defaultOption(caPool, verifier))
}

// MakeMTLSAuthorizer returns a bahamut.Authorizer that will allow requests
// presenting a client certificate signed by a CA present in the given caPool
// unless the given mtls.VerifierFunc disagrees.
func MakeMTLSAuthorizer(caPool *x509.CertPool, verifier mtls.VerifierFunc, ignoredIdentities []elemental.Identity) bahamut.Authorizer {
v, d, mf, mc := defaultOption(caPool, verifier)
return mtls.NewMTLSAuthorizer(v, d, ignoredIdentities, mf, mc)
}

// MakeRequestAuthenticatorBypasser returns a bahamut.RequestAuthenticator that returns bahamut.ActionOK
// if the request is for one of the given identities. Otherwise it returns bahamut.Continue.
func MakeRequestAuthenticatorBypasser(identities []elemental.Identity) bahamut.RequestAuthenticator {
return simple.NewAuthenticator(makeBypasserFunc(identities), nil)
}

// MakeSessionAuthenticatorBypasser returns a bahamut.SessionAuthenticator that returns bahamut.ActionOK
// if the request is for one of the given identities. Otherwise it returns bahamut.Continue.
func MakeSessionAuthenticatorBypasser(identities []elemental.Identity) bahamut.RequestAuthenticator {
return simple.NewAuthenticator(makeBypasserFunc(identities), nil)
}

// MakeAuthorizerBypasser returns a bahamut.Authorizer that returns bahamut.ActionOK
// if the request is on one of the given identities. Otherwise it returns bahamut.Continue.
func MakeAuthorizerBypasser(identities []elemental.Identity) bahamut.Authorizer {
return simple.NewAuthorizer(makeBypasserFunc(identities))
}

func makeBypasserFunc(identities []elemental.Identity) func(ctx bahamut.Context) (bahamut.AuthAction, error) {

return func(ctx bahamut.Context) (bahamut.AuthAction, error) {
for _, i := range identities {
if ctx.Request().Identity.IsEqual(i) {
return bahamut.AuthActionOK, nil
}
}
return bahamut.AuthActionContinue, nil
}
}

func defaultOption(caPool *x509.CertPool, verifier mtls.VerifierFunc) (x509.VerifyOptions, mtls.DeciderFunc, mtls.VerifierFunc, mtls.CertificateCheckMode) {

return x509.VerifyOptions{
Roots: caPool,
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
},
func(a bahamut.AuthAction, c bahamut.Context, s bahamut.Session) bahamut.AuthAction {
switch a {
case bahamut.AuthActionKO:
return bahamut.AuthActionContinue
case bahamut.AuthActionOK:
if token.FromRequest(c.Request()) != "" {
return bahamut.AuthActionContinue
}
return bahamut.AuthActionOK
default:
panic("should not reach here")
}
},
verifier,
mtls.CertificateCheckModeHeaderOnly
}
49 changes: 45 additions & 4 deletions pkgs/bootstrap/clients.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,13 @@ import (
"fmt"
"time"

"go.aporeto.io/a3s/pkgs/api"
"go.aporeto.io/a3s/pkgs/authenticator"
"go.aporeto.io/a3s/pkgs/authlib"
"go.aporeto.io/a3s/pkgs/authorizer"
"go.aporeto.io/a3s/pkgs/conf"
"go.aporeto.io/a3s/pkgs/permissions"
"go.aporeto.io/a3s/pkgs/sharder"
"go.aporeto.io/a3s/pkgs/token"
"go.aporeto.io/bahamut"
"go.aporeto.io/elemental"
"go.aporeto.io/manipulate"
Expand Down Expand Up @@ -56,7 +59,7 @@ func MakeNATSClient(cfg conf.NATSConf) bahamut.PubSubClient {
// MakeMongoManipulator returns a configured mongo manipulator.
// This function is not meant to be used outside of the platform. It will fatal
// anytime it feels like it.
func MakeMongoManipulator(cfg conf.MongoConf, hasher sharder.Hasher, additionalOptions ...manipmongo.Option) manipulate.TransactionalManipulator {
func MakeMongoManipulator(cfg conf.MongoConf, hasher sharder.Hasher, model elemental.ModelManager, additionalOptions ...manipmongo.Option) manipulate.TransactionalManipulator {

var consistency manipulate.ReadConsistency
switch cfg.MongoConsistency {
Expand All @@ -79,8 +82,7 @@ func MakeMongoManipulator(cfg conf.MongoConf, hasher sharder.Hasher, additionalO
manipmongo.OptionCredentials(cfg.MongoUser, cfg.MongoPassword, cfg.MongoAuthDB),
manipmongo.OptionConnectionPoolLimit(cfg.MongoPoolSize),
manipmongo.OptionDefaultReadConsistencyMode(consistency),
manipmongo.OptionTranslateKeysFromModelManager(api.Manager()),
manipmongo.OptionSharder(sharder.New(hasher)),
manipmongo.OptionTranslateKeysFromModelManager(model),
manipmongo.OptionDefaultRetryFunc(func(i manipulate.RetryInfo) error {
info := i.(manipmongo.RetryInfo)
zap.L().Debug("mongo manipulator retry",
Expand All @@ -95,6 +97,13 @@ func MakeMongoManipulator(cfg conf.MongoConf, hasher sharder.Hasher, additionalO
additionalOptions...,
)

if hasher != nil {
opts = append(
opts,
manipmongo.OptionSharder(sharder.New(hasher)),
)
}

tlscfg, err := cfg.TLSConfig()
if err != nil {
zap.L().Fatal("Unable to prepare TLS config for mongodb", zap.Error(err))
Expand Down Expand Up @@ -185,3 +194,35 @@ func MakeA3SManipulator(ctx context.Context, a3sConfig conf.A3SClientConf) (mani

return m, nil
}

// MakeA3SRemoteAuth is a convenience function that will return
// ready to user Authenticator and Authorizers for a bahamut server.
// It uses the given manipulator to talk to the instance of a3s.
func MakeA3SRemoteAuth(
ctx context.Context,
m manipulate.Manipulator,
requiredIssuer string,
requiredAudience string,
) (*authenticator.Authenticator, authorizer.Authorizer, error) {

jwks, err := token.NewRemoteJWKS(
ctx,
maniphttp.ExtractClient(m),
fmt.Sprintf("%s/.well-known/jwks.json", maniphttp.ExtractEndpoint(m)),
)
if err != nil {
return nil, nil, fmt.Errorf("unable to retrieve a3s JWT: %w", err)
}

return authenticator.New(
jwks,
requiredIssuer,
requiredAudience,
),
authorizer.NewRemote(
ctx,
m,
permissions.NewRemoteRetriever(m),
),
nil
}
8 changes: 6 additions & 2 deletions pkgs/importing/hash.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,12 @@ func sanitize(obj elemental.AttributeSpecifiable, manager elemental.ModelManager
// If the type is a ref, we recursively sanitize the nested object.
if spec.Type == "ref" {

fmt.Printf("%#v\n\n", v)
m, err := sanitize(v.(elemental.AttributeSpecifiable), manager)
vv := manager.IdentifiableFromString(spec.SubType)
if err := mapstructure.Decode(v, vv); err != nil {
return nil, err
}

m, err := sanitize(vv.(elemental.AttributeSpecifiable), manager)
if err != nil {
return nil, err
}
Expand Down

0 comments on commit 1caf6fe

Please sign in to comment.