Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
- (Feature) (Platform) Envoy Cache Introduction
- (Feature) (Platform) OpenID Integration - API Extension
- (Feature) Windows Platform CLI
- (Feature) (Platform) Auth User Creation

## [1.2.48](https://github.com/arangodb/kube-arangodb/tree/1.2.48) (2025-05-08)
- (Maintenance) Extend Documentation
Expand Down
4 changes: 4 additions & 0 deletions docs/cli/arangodb_operator_integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,12 @@ Flags:
--integration.config.v1.internal Defones if Internal access to service config.v1 is enabled (Env: INTEGRATION_CONFIG_V1_INTERNAL) (default true)
--integration.config.v1.module strings Module in the reference <name>=<abs path> (Env: INTEGRATION_CONFIG_V1_MODULE)
--integration.envoy.auth.v3 Enable EnvoyAuthV3 Integration Service (Env: INTEGRATION_ENVOY_AUTH_V3)
--integration.envoy.auth.v3.database.endpoint string Endpoint of ArangoDB (Env: INTEGRATION_ENVOY_AUTH_V3_DATABASE_ENDPOINT)
--integration.envoy.auth.v3.database.port int Port of ArangoDB (Env: INTEGRATION_ENVOY_AUTH_V3_DATABASE_PORT) (default 8529)
--integration.envoy.auth.v3.database.proto string Proto of the ArangoDB endpoint (Env: INTEGRATION_ENVOY_AUTH_V3_DATABASE_PROTO) (default "http")
--integration.envoy.auth.v3.extensions.cookie.jwt Defines if Cookie JWT extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_COOKIE_JWT) (default true)
--integration.envoy.auth.v3.extensions.jwt Defines if JWT extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_JWT) (default true)
--integration.envoy.auth.v3.extensions.users.create Defines if UserCreation extension is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTENSIONS_USERS_CREATE)
--integration.envoy.auth.v3.external Defones if External access to service envoy.auth.v3 is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_EXTERNAL)
--integration.envoy.auth.v3.internal Defones if Internal access to service envoy.auth.v3 is enabled (Env: INTEGRATION_ENVOY_AUTH_V3_INTERNAL) (default true)
--integration.scheduler.v1 SchedulerV1 Integration (Env: INTEGRATION_SCHEDULER_V1)
Expand Down
11 changes: 10 additions & 1 deletion integrations/envoy/auth/v3/impl/auth_bearer/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,15 @@ func New(configuration pbImplEnvoyAuthV3Shared.Configuration) (pbImplEnvoyAuthV3
var z impl

z.configuration = configuration
z.authClient = cache.NewObject[pbAuthenticationV1.AuthenticationV1Client](configuration.GetAuthClientFetcher)

z.cache = cache.NewCache[pbImplEnvoyAuthV3Shared.Token, pbImplEnvoyAuthV3Shared.ResponseAuth](func(ctx context.Context, in pbImplEnvoyAuthV3Shared.Token) (pbImplEnvoyAuthV3Shared.ResponseAuth, error) {
resp, err := z.configuration.AuthClient.Validate(ctx, &pbAuthenticationV1.ValidateRequest{
client, err := z.authClient.Get(ctx)
if err != nil {
return pbImplEnvoyAuthV3Shared.ResponseAuth{}, err
}

resp, err := client.Validate(ctx, &pbAuthenticationV1.ValidateRequest{
Token: string(in),
})
if err != nil {
Expand Down Expand Up @@ -70,6 +77,8 @@ type impl struct {
configuration pbImplEnvoyAuthV3Shared.Configuration

cache cache.Cache[pbImplEnvoyAuthV3Shared.Token, pbImplEnvoyAuthV3Shared.ResponseAuth]

authClient cache.Object[pbAuthenticationV1.AuthenticationV1Client]
}

func (p impl) Handle(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, current *pbImplEnvoyAuthV3Shared.Response) error {
Expand Down
12 changes: 10 additions & 2 deletions integrations/envoy/auth/v3/impl/auth_cookie/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,15 +36,21 @@ import (
const JWTAuthorizationCookieName = "X-ArangoDB-Token-JWT"

func New(configuration pbImplEnvoyAuthV3Shared.Configuration) (pbImplEnvoyAuthV3Shared.AuthHandler, bool) {
if !configuration.Extensions.JWT {
if !configuration.Extensions.CookieJWT {
return nil, false
}

var z impl

z.configuration = configuration
z.authClient = cache.NewObject[pbAuthenticationV1.AuthenticationV1Client](configuration.GetAuthClientFetcher)
z.cache = cache.NewCache[pbImplEnvoyAuthV3Shared.Token, pbImplEnvoyAuthV3Shared.ResponseAuth](func(ctx context.Context, in pbImplEnvoyAuthV3Shared.Token) (pbImplEnvoyAuthV3Shared.ResponseAuth, error) {
resp, err := z.configuration.AuthClient.Validate(ctx, &pbAuthenticationV1.ValidateRequest{
client, err := z.authClient.Get(ctx)
if err != nil {
return pbImplEnvoyAuthV3Shared.ResponseAuth{}, err
}

resp, err := client.Validate(ctx, &pbAuthenticationV1.ValidateRequest{
Token: string(in),
})
if err != nil {
Expand Down Expand Up @@ -72,6 +78,8 @@ type impl struct {
configuration pbImplEnvoyAuthV3Shared.Configuration

cache cache.Cache[pbImplEnvoyAuthV3Shared.Token, pbImplEnvoyAuthV3Shared.ResponseAuth]

authClient cache.Object[pbAuthenticationV1.AuthenticationV1Client]
}

func (p impl) Handle(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, current *pbImplEnvoyAuthV3Shared.Response) error {
Expand Down
2 changes: 2 additions & 0 deletions integrations/envoy/auth/v3/impl/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/impl/auth_required"
"github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/impl/pass_mode"
"github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/impl/required"
"github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/impl/users"
pbImplEnvoyAuthV3Shared "github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/shared"
)

Expand All @@ -36,5 +37,6 @@ func Factory() pbImplEnvoyAuthV3Shared.Factory {
auth_cookie.New,
auth_required.New,
pass_mode.New,
users.New,
)
}
11 changes: 10 additions & 1 deletion integrations/envoy/auth/v3/impl/pass_mode/impl.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ func New(configuration pbImplEnvoyAuthV3Shared.Configuration) (pbImplEnvoyAuthV3
var z impl

z.configuration = configuration
z.authClient = cache.NewObject(configuration.GetAuthClientFetcher)
z.cache = cache.NewHashCache[*pbImplEnvoyAuthV3Shared.ResponseAuth, pbImplEnvoyAuthV3Shared.Token](z.Token, pbImplEnvoyAuthV3Shared.DefaultTTL)

return z, true
Expand All @@ -51,6 +52,8 @@ type impl struct {
configuration pbImplEnvoyAuthV3Shared.Configuration

cache cache.HashCache[*pbImplEnvoyAuthV3Shared.ResponseAuth, pbImplEnvoyAuthV3Shared.Token]

authClient cache.Object[pbAuthenticationV1.AuthenticationV1Client]
}

func (p impl) Handle(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, current *pbImplEnvoyAuthV3Shared.Response) error {
Expand Down Expand Up @@ -141,7 +144,13 @@ func (p impl) Token(ctx context.Context, in *pbImplEnvoyAuthV3Shared.ResponseAut
if in == nil {
return "", errors.Errorf("Nil is not allowed")
}
resp, err := p.configuration.AuthClient.CreateToken(ctx, &pbAuthenticationV1.CreateTokenRequest{

client, err := p.authClient.Get(ctx)
if err != nil {
return "", err
}

resp, err := client.CreateToken(ctx, &pbAuthenticationV1.CreateTokenRequest{
Lifetime: durationpb.New(pbImplEnvoyAuthV3Shared.DefaultLifetime),
User: util.NewType(in.User),
Roles: in.Roles,
Expand Down
116 changes: 116 additions & 0 deletions integrations/envoy/auth/v3/impl/users/impl.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//
// DISCLAIMER
//
// Copyright 2025 ArangoDB GmbH, Cologne, Germany
//
// 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
//
// http://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.
//
// Copyright holder is ArangoDB GmbH, Cologne, Germany
//

package users

import (
"context"
"fmt"
"time"

pbEnvoyAuthV3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
"k8s.io/apimachinery/pkg/util/uuid"

"github.com/arangodb/go-driver/v2/arangodb"
"github.com/arangodb/go-driver/v2/arangodb/shared"
"github.com/arangodb/go-driver/v2/connection"

pbAuthenticationV1 "github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition"
pbImplEnvoyAuthV3Shared "github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/shared"
"github.com/arangodb/kube-arangodb/pkg/util"
"github.com/arangodb/kube-arangodb/pkg/util/cache"
operatorHTTP "github.com/arangodb/kube-arangodb/pkg/util/http"
)

func New(configuration pbImplEnvoyAuthV3Shared.Configuration) (pbImplEnvoyAuthV3Shared.AuthHandler, bool) {
if !configuration.Extensions.UsersCreate {
return nil, false
}

i := &impl{
authClient: cache.NewObject[pbAuthenticationV1.AuthenticationV1Client](configuration.GetAuthClientFetcher),
}

i.userClient = cache.NewObject(func(ctx context.Context) (arangodb.ClientUsers, time.Duration, error) {
ac, err := i.authClient.Get(ctx)
if err != nil {
return nil, 0, err
}

client := arangodb.NewClient(connection.NewHttpConnection(connection.HttpConfiguration{
Authentication: pbAuthenticationV1.NewRootRequestModifier(ac),
Endpoint: connection.NewRoundRobinEndpoints([]string{
fmt.Sprintf("%s://%s:%d", configuration.Database.Proto, configuration.Database.Endpoint, configuration.Database.Port),
}),
ContentType: connection.ApplicationJSON,
ArangoDBConfig: connection.ArangoDBConfiguration{},
Transport: operatorHTTP.RoundTripperWithShortTransport(operatorHTTP.WithTransportTLS(operatorHTTP.Insecure)),
}))

return client, 24 * time.Hour, nil
})

i.users = cache.NewCache[string, arangodb.User](func(ctx context.Context, in string) (arangodb.User, error) {
client, err := i.userClient.Get(ctx)
if err != nil {
return nil, err
}

if user, err := client.User(ctx, in); err == nil {
return user, nil
} else {
if !shared.IsNotFound(err) {
return nil, err
}
}

if user, err := client.CreateUser(ctx, in, &arangodb.UserOptions{
Password: string(uuid.NewUUID()),
Active: util.NewType(true),
}); err != nil {
return user, nil
} else {
if !shared.IsConflict(err) {
return nil, err
}
}

return client.User(ctx, in)
}, 24*time.Hour)

return i, true
}

type impl struct {
authClient cache.Object[pbAuthenticationV1.AuthenticationV1Client]
userClient cache.Object[arangodb.ClientUsers]

users cache.Cache[string, arangodb.User]
}

func (i *impl) Handle(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, current *pbImplEnvoyAuthV3Shared.Response) error {
if !current.Authenticated() {
return nil
}

_, err := i.users.Get(ctx, current.User.User)

return err
}
28 changes: 23 additions & 5 deletions integrations/envoy/auth/v3/shared/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,38 @@
package shared

import (
"github.com/arangodb/go-driver/v2/arangodb"
"context"
"time"

pbAuthenticationV1 "github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition"
ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc"
)

type Configuration struct {
AuthClient pbAuthenticationV1.AuthenticationV1Client
Address string

ArangoDBClient arangodb.Client
Database ConfigurationDatabase

Extensions ConfigurationExtensions
}

type ConfigurationDatabase struct {
Proto string
Endpoint string
Port int
}

type ConfigurationExtensions struct {
JWT bool
CookieJWT bool
JWT bool
CookieJWT bool
UsersCreate bool
}

func (c Configuration) GetAuthClientFetcher(ctx context.Context) (pbAuthenticationV1.AuthenticationV1Client, time.Duration, error) {
client, _, err := ugrpc.NewGRPCClient(ctx, pbAuthenticationV1.NewAuthenticationV1Client, c.Address)
if err != nil {
return nil, 0, err
}

return client, time.Hour, nil
}
17 changes: 6 additions & 11 deletions pkg/integrations/envoy_auth_v3.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,9 @@ import (

"github.com/spf13/cobra"

pbAuthenticationV1 "github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition"
pbImplEnvoyAuthV3 "github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3"
pbImplEnvoyAuthV3Shared "github.com/arangodb/kube-arangodb/integrations/envoy/auth/v3/shared"
"github.com/arangodb/kube-arangodb/pkg/util/errors"
ugrpc "github.com/arangodb/kube-arangodb/pkg/util/grpc"
"github.com/arangodb/kube-arangodb/pkg/util/svc"
)

Expand All @@ -55,6 +53,10 @@ func (a *envoyAuthV3) Register(cmd *cobra.Command, fs FlagEnvHandler) error {
return errors.Errors(
fs.BoolVar(&a.config.Extensions.JWT, "extensions.jwt", true, "Defines if JWT extension is enabled"),
fs.BoolVar(&a.config.Extensions.CookieJWT, "extensions.cookie.jwt", true, "Defines if Cookie JWT extension is enabled"),
fs.BoolVar(&a.config.Extensions.UsersCreate, "extensions.users.create", false, "Defines if UserCreation extension is enabled"),
fs.StringVar(&a.config.Database.Endpoint, "database.endpoint", "", "Endpoint of ArangoDB"),
fs.StringVar(&a.config.Database.Proto, "database.proto", "http", "Proto of the ArangoDB endpoint"),
fs.IntVar(&a.config.Database.Port, "database.port", 8529, "Port of ArangoDB"),
)
}

Expand All @@ -66,14 +68,7 @@ func (a *envoyAuthV3) Handler(ctx context.Context, cmd *cobra.Command) (svc.Hand
return nil, err
}

c, _, err := ugrpc.NewGRPCClient(ctx, pbAuthenticationV1.NewAuthenticationV1Client, v)
if err != nil {
return nil, err
}

cfg := a.config

cfg.AuthClient = c
a.config.Address = v

return pbImplEnvoyAuthV3.New(cfg), nil
return pbImplEnvoyAuthV3.New(a.config), nil
}
48 changes: 48 additions & 0 deletions pkg/integrations/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,9 @@ type FlagEnvHandler interface {
Uint16Var(p *uint16, name string, value uint16, usage string) error
Uint16(name string, value uint16, usage string) error

IntVar(p *int, name string, value int, usage string) error
Int(name string, value int, usage string) error

DurationVar(p *time.Duration, name string, value time.Duration, usage string) error
Duration(name string, value time.Duration, usage string) error
}
Expand Down Expand Up @@ -257,6 +260,44 @@ func (f flagEnvHandler) Uint16(name string, value uint16, usage string) error {
return nil
}

func (f flagEnvHandler) IntVar(p *int, name string, value int, usage string) error {
v, err := parseEnvToInt(f.getEnv(name), value)
if err != nil {
return err
}

fname := f.name(name)

f.fs.IntVar(p, fname, v, f.varDesc(name, usage))

if !f.visible {
if err := f.fs.MarkHidden(fname); err != nil {
return err
}
}

return nil
}

func (f flagEnvHandler) Int(name string, value int, usage string) error {
v, err := parseEnvToInt(f.getEnv(name), value)
if err != nil {
return err
}

fname := f.name(name)

f.fs.Int(fname, v, f.varDesc(name, usage))

if !f.visible {
if err := f.fs.MarkHidden(fname); err != nil {
return err
}
}

return nil
}

func (f flagEnvHandler) varDesc(name string, dest string) string {
return fmt.Sprintf("%s (Env: %s)", dest, f.getEnv(name))
}
Expand Down Expand Up @@ -306,6 +347,13 @@ func parseEnvToUint16(env string, def uint16) (uint16, error) {
})
}

func parseEnvToInt(env string, def int) (int, error) {
return parseEnvToType(env, def, func(in string) (int, error) {
v, err := strconv.ParseInt(in, 10, 64)
return int(v), err
})
}

func parseEnvToBool(env string, def bool) (bool, error) {
return parseEnvToType(env, def, strconv.ParseBool)
}
Expand Down
Loading