Compare commits

..

No commits in common. "a82fe46fa3f758413a331c5f706f2ae0ac40a661" and "35717429a21bd2cc9f8c4a7729a58b4a0ad22f83" have entirely different histories.

19 changed files with 83 additions and 351 deletions

View File

@ -7,6 +7,3 @@
/bin /bin
/.bouncer-token /.bouncer-token
/.env /.env
/misc/k8s
/misc/k6s
/misc/grafterm

1
go.mod
View File

@ -14,7 +14,6 @@ require (
github.com/go-chi/chi/v5 v5.0.8 github.com/go-chi/chi/v5 v5.0.8
github.com/jedib0t/go-pretty/v6 v6.4.6 github.com/jedib0t/go-pretty/v6 v6.4.6
github.com/mitchellh/mapstructure v1.4.1 github.com/mitchellh/mapstructure v1.4.1
github.com/oklog/ulid/v2 v2.1.0
github.com/ory/dockertest/v3 v3.10.0 github.com/ory/dockertest/v3 v3.10.0
github.com/prometheus/client_golang v1.16.0 github.com/prometheus/client_golang v1.16.0
github.com/qri-io/jsonschema v0.2.1 github.com/qri-io/jsonschema v0.2.1

3
go.sum
View File

@ -410,8 +410,6 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E= github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/oklog/ulid/v2 v2.1.0 h1:+9lhoxAP56we25tyYETBBY1YLA2SaoLvUFgrP2miPJU=
github.com/oklog/ulid/v2 v2.1.0/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@ -436,7 +434,6 @@ github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuh
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4= github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg= github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0= github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=

View File

@ -3,11 +3,8 @@ package admin
import ( import (
"context" "context"
"forge.cadoles.com/cadoles/bouncer/internal/integration"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"forge.cadoles.com/cadoles/bouncer/internal/setup" "forge.cadoles.com/cadoles/bouncer/internal/setup"
"github.com/pkg/errors" "github.com/pkg/errors"
"gitlab.com/wpetit/goweb/logger"
) )
func (s *Server) initRepositories(ctx context.Context) error { func (s *Server) initRepositories(ctx context.Context) error {
@ -55,34 +52,3 @@ func (s *Server) initProxyRepository(ctx context.Context) error {
return nil return nil
} }
func (s *Server) initPrivateKey(ctx context.Context) error {
localKey, err := jwk.LoadOrGenerate(string(s.serverConfig.Auth.PrivateKey), jwk.DefaultKeySize)
if err != nil {
return errors.WithStack(err)
}
ctx = integration.WithPrivateKey(ctx, localKey)
key, err := integration.RunOnKeyLoad(ctx, s.integrations)
if err != nil {
return errors.WithStack(err)
}
if key != nil {
s.privateKey = key
} else {
s.privateKey = localKey
}
logger.Info(ctx, "using private key", logger.F("keyID", s.privateKey.KeyID()))
publicKeys, err := jwk.PublicKeySet(s.privateKey)
if err != nil {
return errors.WithStack(err)
}
s.publicKeys = publicKeys
return nil
}

View File

@ -35,9 +35,6 @@ type Server struct {
bootstrapConfig config.BootstrapConfig bootstrapConfig config.BootstrapConfig
proxyRepository store.ProxyRepository proxyRepository store.ProxyRepository
layerRepository store.LayerRepository layerRepository store.LayerRepository
privateKey jwk.Key
publicKeys jwk.Set
} }
func (s *Server) Start(ctx context.Context) (<-chan net.Addr, <-chan error) { func (s *Server) Start(ctx context.Context) (<-chan net.Addr, <-chan error) {
@ -70,15 +67,6 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
return return
} }
if err := s.initPrivateKey(ctx); err != nil {
errs <- errors.WithStack(err)
return
}
ctx = integration.WithPrivateKey(ctx, s.privateKey)
ctx = integration.WithPublicKeySet(ctx, s.publicKeys)
if err := integration.RunOnStartup(ctx, s.integrations); err != nil { if err := integration.RunOnStartup(ctx, s.integrations); err != nil {
errs <- errors.WithStack(err) errs <- errors.WithStack(err)
@ -108,6 +96,20 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
} }
}() }()
key, err := jwk.LoadOrGenerate(string(s.serverConfig.Auth.PrivateKey), jwk.DefaultKeySize)
if err != nil {
errs <- errors.WithStack(err)
return
}
keys, err := jwk.PublicKeySet(key)
if err != nil {
errs <- errors.WithStack(err)
return
}
router := chi.NewRouter() router := chi.NewRouter()
if s.serverConfig.HTTP.UseRealIP { if s.serverConfig.HTTP.UseRealIP {
@ -158,7 +160,7 @@ func (s *Server) run(parentCtx context.Context, addrs chan net.Addr, errs chan e
router.Route("/api/v1", func(r chi.Router) { router.Route("/api/v1", func(r chi.Router) {
r.Group(func(r chi.Router) { r.Group(func(r chi.Router) {
r.Use(auth.Middleware( r.Use(auth.Middleware(
jwt.NewAuthenticator(s.publicKeys, string(s.serverConfig.Auth.Issuer), jwt.DefaultAcceptableSkew), jwt.NewAuthenticator(keys, string(s.serverConfig.Auth.Issuer), jwt.DefaultAcceptableSkew),
)) ))
r.Route("/proxies", func(r chi.Router) { r.Route("/proxies", func(r chi.Router) {

View File

@ -5,7 +5,9 @@ import (
"strings" "strings"
"forge.cadoles.com/cadoles/bouncer/internal/admin" "forge.cadoles.com/cadoles/bouncer/internal/admin"
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
"forge.cadoles.com/cadoles/bouncer/internal/command/common" "forge.cadoles.com/cadoles/bouncer/internal/command/common"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"forge.cadoles.com/cadoles/bouncer/internal/setup" "forge.cadoles.com/cadoles/bouncer/internal/setup"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
@ -19,6 +21,11 @@ const (
func RunCommand() *cli.Command { func RunCommand() *cli.Command {
flags := append( flags := append(
common.Flags(), common.Flags(),
&cli.BoolFlag{
Name: flagPrintDefaultToken,
Usage: "Generate and print a default writer token in console at startup",
Value: true,
},
) )
return &cli.Command{ return &cli.Command{
@ -42,6 +49,22 @@ func RunCommand() *cli.Command {
defer flushSentry() defer flushSentry()
if printDefaultToken := ctx.Bool(flagPrintDefaultToken); printDefaultToken {
key, err := jwk.Generate(jwk.DefaultKeySize)
if err != nil {
return errors.Wrap(err, "could not generate default key")
}
token, err := jwt.GenerateToken(ctx.Context, key, string(conf.Admin.Auth.Issuer), "default-admin", jwt.Role(jwt.RoleWriter))
if err != nil {
return errors.WithStack(err)
}
logger.SetLevel(logger.LevelInfo)
logger.Info(ctx.Context, "default writer token", logger.F("token", token))
logger.SetLevel(logger.Level(conf.Logger.Level))
}
integrations, err := setup.SetupIntegrations(ctx.Context, conf) integrations, err := setup.SetupIntegrations(ctx.Context, conf)
if err != nil { if err != nil {
return errors.Wrap(err, "could not setup integrations") return errors.Wrap(err, "could not setup integrations")

View File

@ -9,14 +9,10 @@ type IntegrationsConfig struct {
func NewDefaultIntegrationsConfig() IntegrationsConfig { func NewDefaultIntegrationsConfig() IntegrationsConfig {
return IntegrationsConfig{ return IntegrationsConfig{
Kubernetes: KubernetesConfig{ Kubernetes: KubernetesConfig{
Enabled: false, Enabled: false,
WriterTokenSecret: "", WriterTokenSecret: "",
WriterTokenSecretNamespace: "", ReaderTokenSecret: "",
ReaderTokenSecretNamespace: "", LockTimeout: *NewInterpolatedDuration(30 * time.Second),
PrivateKeySecret: "",
PrivateKeySecretNamespace: "",
ReaderTokenSecret: "",
LockTimeout: *NewInterpolatedDuration(30 * time.Second),
}, },
} }
} }
@ -27,7 +23,5 @@ type KubernetesConfig struct {
WriterTokenSecretNamespace InterpolatedString `yaml:"writerTokenSecretNamespace"` WriterTokenSecretNamespace InterpolatedString `yaml:"writerTokenSecretNamespace"`
ReaderTokenSecret InterpolatedString `yaml:"readerTokenSecret"` ReaderTokenSecret InterpolatedString `yaml:"readerTokenSecret"`
ReaderTokenSecretNamespace InterpolatedString `yaml:"readerTokenSecretNamespace"` ReaderTokenSecretNamespace InterpolatedString `yaml:"readerTokenSecretNamespace"`
PrivateKeySecret InterpolatedString `yaml:"privateKeySecret"`
PrivateKeySecretNamespace InterpolatedString `yaml:"privateKeySecretNamespace"`
LockTimeout InterpolatedDuration `yaml:"lockTimeout"` LockTimeout InterpolatedDuration `yaml:"lockTimeout"`
} }

View File

@ -1,49 +0,0 @@
package integration
import (
"context"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"github.com/pkg/errors"
)
var (
ErrNotFound = errors.New("not found")
)
type contextKey string
const (
ctxPublicKeySet contextKey = "public-key-set"
ctxPrivateKey contextKey = "private-key"
)
func CtxPublicKeySet(ctx context.Context) (jwk.Set, error) {
return ctxValue[jwk.Set](ctx, ctxPublicKeySet)
}
func WithPublicKeySet(ctx context.Context, set jwk.Set) context.Context {
return context.WithValue(ctx, ctxPublicKeySet, set)
}
func CtxPrivateKey(ctx context.Context) (jwk.Key, error) {
return ctxValue[jwk.Key](ctx, ctxPrivateKey)
}
func WithPrivateKey(ctx context.Context, key jwk.Key) context.Context {
return context.WithValue(ctx, ctxPrivateKey, key)
}
func ctxValue[T any](ctx context.Context, key contextKey) (T, error) {
raw := ctx.Value(key)
if raw == nil {
return *new(T), errors.WithStack(ErrNotFound)
}
value, ok := raw.(T)
if !ok {
return *new(T), errors.Errorf("unexpected value type '%T'", raw)
}
return value, nil
}

View File

@ -3,7 +3,6 @@ package integration
import ( import (
"context" "context"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -16,11 +15,6 @@ type OnStartup interface {
OnStartup(ctx context.Context) error OnStartup(ctx context.Context) error
} }
type OnKeyLoad interface {
Integration
OnKeyLoad(ctx context.Context) (jwk.Key, error)
}
func RunOnStartup(ctx context.Context, integrations []Integration) error { func RunOnStartup(ctx context.Context, integrations []Integration) error {
for _, it := range integrations { for _, it := range integrations {
onStartup, ok := it.(OnStartup) onStartup, ok := it.(OnStartup)
@ -35,23 +29,3 @@ func RunOnStartup(ctx context.Context, integrations []Integration) error {
return nil return nil
} }
func RunOnKeyLoad(ctx context.Context, integrations []Integration) (jwk.Key, error) {
for _, it := range integrations {
onKeyLoad, ok := it.(OnKeyLoad)
if !ok {
continue
}
key, err := onKeyLoad.OnKeyLoad(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
if key != nil {
return key, nil
}
}
return nil, nil
}

View File

@ -2,7 +2,8 @@ package kubernetes
import ( import (
"context" "context"
"encoding/json" "crypto"
"fmt"
"os" "os"
"forge.cadoles.com/cadoles/bouncer/internal/auth/jwt" "forge.cadoles.com/cadoles/bouncer/internal/auth/jwt"
@ -27,38 +28,6 @@ type Integration struct {
Options *Options Options *Options
} }
// OnKeyLoad implements integration.OnKeyLoad.
func (i *Integration) OnKeyLoad(ctx context.Context) (jwk.Key, error) {
locker := i.Options.Locker
timeout := i.Options.LockTimeout
var key jwk.Key
err := locker.WithLock(ctx, "bouncer-kubernetes-onkeyload", timeout, func(ctx context.Context) error {
client, err := i.getClient()
if err != nil {
return errors.WithStack(err)
}
if i.Options.PrivateKeySecret != "" {
sharedPrivateKey, err := i.getSharedPrivateKey(ctx, client, i.Options.PrivateKeySecretNamespace, i.Options.PrivateKeySecret)
if err != nil {
return errors.WithStack(err)
}
if sharedPrivateKey != nil {
key = sharedPrivateKey
}
}
return nil
})
if err != nil {
return nil, errors.WithStack(err)
}
return key, nil
}
// Integration implements integration.OnStartup. // Integration implements integration.OnStartup.
func (i *Integration) Integration() {} func (i *Integration) Integration() {}
@ -67,7 +36,12 @@ func (i *Integration) OnStartup(ctx context.Context) error {
locker := i.Options.Locker locker := i.Options.Locker
timeout := i.Options.LockTimeout timeout := i.Options.LockTimeout
err := locker.WithLock(ctx, "bouncer-kubernetes-onstartup", timeout, func(ctx context.Context) error { err := locker.WithLock(ctx, "bouncer-kubernetes-onstartup", timeout, func(ctx context.Context) error {
client, err := i.getClient() config, err := rest.InClusterConfig()
if err != nil {
return errors.WithStack(err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
@ -93,20 +67,6 @@ func (i *Integration) OnStartup(ctx context.Context) error {
return nil return nil
} }
func (i *Integration) getClient() (*kubernetes.Clientset, error) {
config, err := rest.InClusterConfig()
if err != nil {
return nil, errors.WithStack(err)
}
client, err := kubernetes.NewForConfig(config)
if err != nil {
return nil, errors.WithStack(err)
}
return client, nil
}
const ( const (
annotationPublicKey = "bouncer.cadoles.com/public-key" annotationPublicKey = "bouncer.cadoles.com/public-key"
) )
@ -130,6 +90,23 @@ func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.
logger.Debug(ctx, "generating new token") logger.Debug(ctx, "generating new token")
key, err := jwk.LoadOrGenerate(i.Options.PrivateKey, jwk.DefaultKeySize)
if err != nil {
return errors.WithStack(err)
}
publicKey, err := key.PublicKey()
if err != nil {
return errors.WithStack(err)
}
publicKeyThumbprint, err := publicKey.Thumbprint(crypto.SHA256)
if err != nil {
return errors.WithStack(err)
}
publicKeyHash := fmt.Sprintf("%x", publicKeyThumbprint)
alreadyExists := true alreadyExists := true
secret, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{}) secret, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil { if err != nil {
@ -140,23 +117,8 @@ func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.
} }
} }
privateKey, err := integration.CtxPrivateKey(ctx)
if err != nil {
return errors.WithStack(err)
}
keySet, err := integration.CtxPublicKeySet(ctx)
if err != nil {
return errors.WithStack(err)
}
publicKeyThumbprint, err := getKeySetThumbprint(keySet)
if err != nil {
return errors.WithStack(err)
}
if !alreadyExists { if !alreadyExists {
token, err := jwt.GenerateToken(ctx, privateKey, i.Options.Issuer, subject, role) token, err := jwt.GenerateToken(ctx, key, i.Options.Issuer, subject, role)
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
@ -166,7 +128,7 @@ func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.
ObjectMeta: metav1.ObjectMeta{ ObjectMeta: metav1.ObjectMeta{
Name: name, Name: name,
Annotations: map[string]string{ Annotations: map[string]string{
annotationPublicKey: publicKeyThumbprint, annotationPublicKey: publicKeyHash,
}, },
}, },
StringData: map[string]string{ StringData: map[string]string{
@ -181,8 +143,8 @@ func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.
} }
} else { } else {
existingPublicKeyHash, exists := secret.Annotations[annotationPublicKey] existingPublicKeyHash, exists := secret.Annotations[annotationPublicKey]
if !exists || publicKeyThumbprint != existingPublicKeyHash { if !exists || publicKeyHash != existingPublicKeyHash {
token, err := jwt.GenerateToken(ctx, privateKey, i.Options.Issuer, subject, role) token, err := jwt.GenerateToken(ctx, key, i.Options.Issuer, subject, role)
if err != nil { if err != nil {
return errors.WithStack(err) return errors.WithStack(err)
} }
@ -195,7 +157,7 @@ func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.
secret.Annotations = make(map[string]string) secret.Annotations = make(map[string]string)
} }
secret.Annotations[annotationPublicKey] = publicKeyThumbprint secret.Annotations[annotationPublicKey] = publicKeyHash
logger.Info(ctx, "updating token secret") logger.Info(ctx, "updating token secret")
@ -210,66 +172,6 @@ func (i *Integration) upsertTokenSecret(ctx context.Context, client *kubernetes.
return nil return nil
} }
func (i *Integration) getSharedPrivateKey(ctx context.Context, client *kubernetes.Clientset, namespace string, name string) (jwk.Key, error) {
if namespace == "" {
defaultNamespace, err := i.getCurrentNamespace()
if err != nil {
return nil, errors.WithStack(err)
}
namespace = defaultNamespace
}
ctx = logger.With(ctx,
logger.F("secretNamespace", namespace),
logger.F("secretName", name),
)
logger.Debug(ctx, "searching shared private key from secret")
secret, err := client.CoreV1().Secrets(namespace).Get(ctx, name, metav1.GetOptions{})
if err != nil && !k8serr.IsNotFound(err) {
return nil, errors.WithStack(err)
}
rawPrivateKey, exists := secret.Data["key"]
if exists && len(rawPrivateKey) != 0 {
key, err := jwk.ParseKey(rawPrivateKey)
if err != nil {
return nil, errors.WithStack(err)
}
return key, nil
}
localKey, err := integration.CtxPrivateKey(ctx)
if err != nil {
return nil, errors.WithStack(err)
}
rawLocalKey, err := json.Marshal(localKey)
if err != nil {
return nil, errors.WithStack(err)
}
secret = &v1.Secret{
Type: v1.SecretTypeOpaque,
ObjectMeta: metav1.ObjectMeta{
Name: name,
},
Data: map[string][]byte{
"key": rawLocalKey,
},
}
if _, err := client.CoreV1().Secrets(namespace).Create(ctx, secret, metav1.CreateOptions{}); err != nil {
return nil, errors.WithStack(err)
}
return localKey, nil
}
func (i *Integration) getCurrentNamespace() (string, error) { func (i *Integration) getCurrentNamespace() (string, error) {
namespace, err := os.ReadFile(namespaceFile) namespace, err := os.ReadFile(namespaceFile)
if err != nil { if err != nil {
@ -289,5 +191,4 @@ func NewIntegration(funcs ...OptionFunc) *Integration {
var ( var (
_ integration.OnStartup = &Integration{} _ integration.OnStartup = &Integration{}
_ integration.OnKeyLoad = &Integration{}
) )

View File

@ -1,41 +0,0 @@
package kubernetes
import (
"bytes"
"crypto"
"crypto/sha256"
"fmt"
"slices"
"forge.cadoles.com/cadoles/bouncer/internal/jwk"
"github.com/pkg/errors"
)
func getKeySetThumbprint(set jwk.Set) (string, error) {
data := make([][]byte, 0, set.Len())
for i := 0; i < set.Len(); i++ {
key, exists := set.Key(i)
if !exists {
continue
}
thumbprint, err := key.Thumbprint(crypto.SHA256)
if err != nil {
return "", errors.WithStack(err)
}
data = append(data, thumbprint)
}
slices.SortFunc(data, bytes.Compare)
hash := sha256.New()
for _, d := range data {
if _, err := hash.Write(d); err != nil {
return "", errors.WithStack(err)
}
}
return fmt.Sprintf("%x", hash.Sum(nil)), nil
}

View File

@ -12,8 +12,7 @@ type Options struct {
WriterTokenSecretNamespace string WriterTokenSecretNamespace string
ReaderTokenSecret string ReaderTokenSecret string
ReaderTokenSecretNamespace string ReaderTokenSecretNamespace string
PrivateKeySecret string PrivateKey string
PrivateKeySecretNamespace string
Issuer string Issuer string
Locker lock.Locker Locker lock.Locker
LockTimeout time.Duration LockTimeout time.Duration
@ -27,8 +26,7 @@ func NewOptions(funcs ...OptionFunc) *Options {
WriterTokenSecretNamespace: "", WriterTokenSecretNamespace: "",
ReaderTokenSecret: "", ReaderTokenSecret: "",
ReaderTokenSecretNamespace: "", ReaderTokenSecretNamespace: "",
PrivateKeySecret: "", PrivateKey: "",
PrivateKeySecretNamespace: "",
Issuer: "", Issuer: "",
Locker: memory.NewLocker(), Locker: memory.NewLocker(),
LockTimeout: 30 * time.Second, LockTimeout: 30 * time.Second,
@ -64,15 +62,9 @@ func WithReaderTokenSecretNamespace(namespace string) OptionFunc {
} }
} }
func WithPrivateKeySecret(secretName string) OptionFunc { func WithPrivateKey(privateKeyFile string) OptionFunc {
return func(opts *Options) { return func(opts *Options) {
opts.PrivateKeySecret = secretName opts.PrivateKey = privateKeyFile
}
}
func WithPrivateKeySecretNamespace(namespace string) OptionFunc {
return func(opts *Options) {
opts.PrivateKeySecretNamespace = namespace
} }
} }

View File

@ -10,7 +10,6 @@ import (
"github.com/lestrrat-go/jwx/v2/jwa" "github.com/lestrrat-go/jwx/v2/jwa"
"github.com/lestrrat-go/jwx/v2/jwk" "github.com/lestrrat-go/jwx/v2/jwk"
"github.com/lestrrat-go/jwx/v2/jws" "github.com/lestrrat-go/jwx/v2/jws"
"github.com/oklog/ulid/v2"
"github.com/pkg/errors" "github.com/pkg/errors"
) )
@ -24,9 +23,8 @@ type (
) )
var ( var (
FromRaw = jwk.FromRaw FromRaw = jwk.FromRaw
NewSet = jwk.NewSet NewSet = jwk.NewSet
ParseKey = jwk.ParseKey
) )
const AlgorithmKey = jwk.AlgorithmKey const AlgorithmKey = jwk.AlgorithmKey
@ -97,12 +95,6 @@ func Generate(size int) (jwk.Key, error) {
return nil, errors.WithStack(err) return nil, errors.WithStack(err)
} }
keyID := ulid.Make().String()
if err := key.Set(jwk.KeyIDKey, keyID); err != nil {
return nil, errors.WithStack(err)
}
return key, nil return key, nil
} }

View File

@ -35,9 +35,8 @@ func setupKubernetesIntegration(ctx context.Context, conf *config.Config) (*kube
kubernetes.WithReaderTokenSecretNamespace(string(conf.Integrations.Kubernetes.ReaderTokenSecretNamespace)), kubernetes.WithReaderTokenSecretNamespace(string(conf.Integrations.Kubernetes.ReaderTokenSecretNamespace)),
kubernetes.WithWriterTokenSecret(string(conf.Integrations.Kubernetes.WriterTokenSecret)), kubernetes.WithWriterTokenSecret(string(conf.Integrations.Kubernetes.WriterTokenSecret)),
kubernetes.WithWriterTokenSecretNamespace(string(conf.Integrations.Kubernetes.WriterTokenSecretNamespace)), kubernetes.WithWriterTokenSecretNamespace(string(conf.Integrations.Kubernetes.WriterTokenSecretNamespace)),
kubernetes.WithPrivateKeySecret(string(conf.Integrations.Kubernetes.PrivateKeySecret)),
kubernetes.WithPrivateKeySecretNamespace(string(conf.Integrations.Kubernetes.PrivateKeySecretNamespace)),
kubernetes.WithIssuer(string(conf.Admin.Auth.Issuer)), kubernetes.WithIssuer(string(conf.Admin.Auth.Issuer)),
kubernetes.WithPrivateKey(string(conf.Admin.Auth.PrivateKey)),
kubernetes.WithLocker(locker), kubernetes.WithLocker(locker),
kubernetes.WithLockTimeout(time.Duration(conf.Integrations.Kubernetes.LockTimeout)), kubernetes.WithLockTimeout(time.Duration(conf.Integrations.Kubernetes.LockTimeout)),
) )

View File

@ -0,0 +1 @@
{"d":"JuBw5OsGv3rPgVczxUgtJ6iUQ41LQu4Xpu-t8IKI_z8r-BZBlbndxidPmRlGZASLGL3rhY4qw6_ScFxakrMpCreO1RMU0kqtz--N48BXFnW5tEgr1voyyKP__bPssQNn6PgkoyAd11es7MEKlBff_DtGrcSkVRgU0zDZB-vIU0aNEIZPNw0icbYqc1u_QQNPpBU9cw6P33WHhzvfCVAkZKRszwznhiPM08n1vjpiA7e1kQ8a6OC4IFZBvohkmpmyOq1g1OLRABQ83YPCjGjCAejO-jEWkbLksp6rAl_YYpCvfBAjFV76JuZq4eh5IU82LsSfi3PGYBkhxWuLY779XQ","dp":"gljHOQowGK7fVn2DJizWtgRIDJuKpKnoX2PWNJUbm2WZwcEPZalAkxn7Y-w_reLVJZuRpfKEUMS-Tn3-CwI1ZjCHPqMPTXcoG0Pe2E-Z88jOs9lW4XSOASiiM980VIvkV1xCxDJkN3NsDFQ9j9kRGnKuMnsucCW3AKaU917hXNU","dq":"mqY19JcEBDnzS70_XkAsOKqPzemOScax66b-4N6zrsgeLVlRjHffY9uCAgBWzlxOidRdQN8q23ZJB4fqsKB2w00Iw7Jxx94IoAKGjKDT5iB48Y_kdKLAwSHRTXsqA9GG3po_H_JpP_EqX4TDBYtqQZuBD_tACP9HbLYMi_V2YU8","e":"AQAB","kty":"RSA","n":"sam0X0BGcuFwX8z3Wde8cv2o_zl6A9ghpkT0tCjw8qH3GNWrbAqzncSWdHBzoChBgAbuTOVs-ixYC0KeUhwFdc8Ul-jmKJWFaS8kIr3y4EH62-vLgMuIKfaxbsyUG6KMkJfnftge1jPO4ccddNej9msxcqTxu37dcgstutwtd6QkS9p5RrNbDBc8-Z7SQ4TuxJfP8msXRnCPJ-I44yszGdQF1Np2DXakJHVn8PBrDh3iSFwORw8jxNS4oS0OlBl5aSc0t5XkkaNcSU2a50SKts290w54fl6MPJ1sLnnznLy4uu37-nrfEUvqRLDZL9B1F82RM1dtLIIiN4gnSrMlpQ","p":"wOmFPhAT_wXWzMuwtEdYIer3-CiOWxFKpFL09eEJkJ29MIUchEaoiJaUAghqPxM48llfOVaUaLbFVxmo5U3fyjNMaP-nHMUBwojutykMK-gC2R3J4bQgFWfKbGSL7M7UsextAvpq9iiOuR0LNE-xTfCgPIxHVdPZskO3yx0DkjM","q":"68OGRb0tLRjb_PpkGctcSjEz_vvcyjzxGL-fn4_h4GCw98Xrj6Y4rZ4lfWWRSeDohSvdd-ICSlxvxkQOIOcA0H7jyJcBC0KDs4hX5BRGJNDri3QX0ry4_F1ptAdbfiFgQGqCfMRCr7L60Tfd_6tLczvny7eEBKQNGdj6dLfhgMc","qi":"DFwixyxUDf0REPLLa8hOKieRL95_AH9rbYWzStBOdSjKWra5l0reD6a4bbvAYvl0e8qCcRI6S8Nzpz0BYm4sJL7poVOnjxqvBY3Q9Ppf4Mq8lW39pOCJcqOHIvvYHsMjTC5uwp7Yg2p0GvxuUibbyNL1PXf6WZ_szVP_oSMrCXA"}

View File

@ -20,7 +20,7 @@ admin:
debug: false debug: false
auth: auth:
issuer: http://127.0.0.1:8081 issuer: http://127.0.0.1:8081
privateKey: /var/lib/bouncer/admin-key.json privateKey: /etc/bouncer/admin-key.json
metrics: metrics:
enabled: true enabled: true
endpoint: /.bouncer/metrics endpoint: /.bouncer/metrics
@ -44,4 +44,3 @@ integrations:
enabled: true enabled: true
writerTokenSecret: ${BOUNCER_WRITER_TOKEN_SECRET} writerTokenSecret: ${BOUNCER_WRITER_TOKEN_SECRET}
readerTokenSecret: ${BOUNCER_READER_TOKEN_SECRET} readerTokenSecret: ${BOUNCER_READER_TOKEN_SECRET}
privateKeySecret: ${BOUNCER_PRIVATE_KEY_SECRET}

View File

@ -10,10 +10,10 @@ configMapGenerator:
- name: bouncer-admin-config - name: bouncer-admin-config
files: files:
- ./files/config.yml - ./files/config.yml
- ./files/admin-key.json
- name: bouncer-admin-bootstrap - name: bouncer-admin-bootstrap
- name: bouncer-admin-env - name: bouncer-admin-env
literals: literals:
- BOUNCER_LOG_LEVEL=2 - BOUNCER_LOG_LEVEL=2
- BOUNCER_WRITER_TOKEN_SECRET=bouncer-admin-writer-token - BOUNCER_WRITER_TOKEN_SECRET=bouncer-admin-writer-token
- BOUNCER_READER_TOKEN_SECRET=bouncer-admin-reader-token - BOUNCER_READER_TOKEN_SECRET=bouncer-admin-reader-token
- BOUNCER_PRIVATE_KEY_SECRET=bouncer-admin-private-key

View File

@ -23,10 +23,6 @@ spec:
containers: containers:
- name: bouncer-admin - name: bouncer-admin
image: bouncer image: bouncer
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
command: command:
[ [
"bouncer", "bouncer",
@ -50,8 +46,6 @@ spec:
name: bouncer-admin-config name: bouncer-admin-config
- mountPath: /etc/bouncer/bootstrap.d - mountPath: /etc/bouncer/bootstrap.d
name: bouncer-admin-bootstrap name: bouncer-admin-bootstrap
- mountPath: /var/lib/bouncer
name: bouncer-admin-var
volumes: volumes:
- name: bouncer-admin-config - name: bouncer-admin-config
configMap: configMap:
@ -59,7 +53,3 @@ spec:
- name: bouncer-admin-bootstrap - name: bouncer-admin-bootstrap
configMap: configMap:
name: bouncer-admin-bootstrap name: bouncer-admin-bootstrap
- name: bouncer-admin-var
emptyDir:
sizeLimit: 10Mi
medium: Memory

View File

@ -21,10 +21,6 @@ spec:
containers: containers:
- name: bouncer-server - name: bouncer-server
image: bouncer image: bouncer
securityContext:
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 1000
command: command:
[ [
"bouncer", "bouncer",