Skip to content

Commit fb2ac88

Browse files
authored
[Feature] [Gateway] ArangoDB AuthIntegration (#1715)
1 parent 81dfd87 commit fb2ac88

36 files changed

+792
-47
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
- (Feature) Integration Service TLS
2424
- (Feature) (Gateway) SNI and Authz support
2525
- (Maintenance) Bump Examples to ArangoDB 3.12
26+
- (Feature) (Gateway) ArangoDB JWT Auth Integration
2627

2728
## [1.2.42](https://github.com/arangodb/kube-arangodb/tree/1.2.42) (2024-07-23)
2829
- (Maintenance) Go 1.22.4 & Kubernetes 1.29.6 libraries

cmd/version.go

+2-5
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//
22
// DISCLAIMER
33
//
4-
// Copyright 2016-2022 ArangoDB GmbH, Cologne, Germany
4+
// Copyright 2016-2024 ArangoDB GmbH, Cologne, Germany
55
//
66
// Licensed under the Apache License, Version 2.0 (the "License");
77
// you may not use this file except in compliance with the License.
@@ -21,8 +21,6 @@
2121
package cmd
2222

2323
import (
24-
"fmt"
25-
2624
"github.com/spf13/cobra"
2725

2826
"github.com/arangodb/kube-arangodb/pkg/version"
@@ -38,6 +36,5 @@ var cmdVersion = &cobra.Command{
3836
}
3937

4038
func versionRun(cmd *cobra.Command, args []string) {
41-
v := version.GetVersionV1()
42-
println(fmt.Sprintf("Version: %s %s, Build: %s, Go: %s, Build Date: %s", v.Edition.Title(), v.Version, v.Build, v.GoVersion, v.BuildDate))
39+
println(version.GetVersionV1().String())
4340
}

docs/api/ArangoRoute.V1Alpha1.md

+13-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@ Deployment specifies the ArangoDeployment object name
1616

1717
***
1818

19+
### .spec.destination.authentication.type
20+
21+
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_spec_destination_authentication.go#L28)</sup>
22+
23+
***
24+
1925
### .spec.destination.path
2026

2127
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_spec_destination.go#L36)</sup>
@@ -131,6 +137,12 @@ UID keeps the information about object UID
131137

132138
***
133139

140+
### .status.target.authentication.type
141+
142+
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target_authentication.go#L26)</sup>
143+
144+
***
145+
134146
### .status.target.destinations\[int\].host
135147

136148
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target_destination.go#L38)</sup>
@@ -145,7 +157,7 @@ Type: `integer` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.
145157

146158
### .status.target.path
147159

148-
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target.go#L37)</sup>
160+
Type: `string` <sup>[\[ref\]](https://github.com/arangodb/kube-arangodb/blob/1.2.42/pkg/apis/networking/v1alpha1/route_status_target.go#L40)</sup>
149161

150162
Path specifies request path override
151163

integrations/envoy/auth/v3/check.go

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package v3
22+
23+
import (
24+
"context"
25+
26+
pbEnvoyAuthV3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
27+
)
28+
29+
type AuthResponse struct {
30+
Username string
31+
}
32+
33+
type AuthRequestFunc func(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, current *AuthResponse) (*AuthResponse, error)
34+
35+
func MergeAuthRequest(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, requests ...AuthRequestFunc) (*AuthResponse, error) {
36+
var resp *AuthResponse
37+
for _, r := range requests {
38+
if v, err := r(ctx, request, resp); err != nil {
39+
return nil, err
40+
} else {
41+
resp = v
42+
}
43+
}
44+
45+
return resp, nil
46+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
//
2+
// DISCLAIMER
3+
//
4+
// Copyright 2024 ArangoDB GmbH, Cologne, Germany
5+
//
6+
// Licensed under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
//
18+
// Copyright holder is ArangoDB GmbH, Cologne, Germany
19+
//
20+
21+
package v3
22+
23+
import (
24+
"context"
25+
26+
pbEnvoyAuthV3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
27+
28+
pbAuthenticationV1 "github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition"
29+
"github.com/arangodb/kube-arangodb/pkg/util/strings"
30+
)
31+
32+
func (i *impl) checkADBJWT(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest, current *AuthResponse) (*AuthResponse, error) {
33+
if current != nil {
34+
// Already authenticated
35+
return current, nil
36+
}
37+
if auth, ok := request.GetAttributes().GetRequest().GetHttp().GetHeaders()["authorization"]; ok {
38+
parts := strings.SplitN(auth, " ", 2)
39+
if len(parts) == 2 {
40+
if strings.ToLower(parts[0]) == "bearer" {
41+
resp, err := i.authClient.Validate(ctx, &pbAuthenticationV1.ValidateRequest{
42+
Token: parts[1],
43+
})
44+
if err != nil {
45+
logger.Err(err).Warn("Auth failure")
46+
return nil, nil
47+
}
48+
49+
if err == nil && resp.GetIsValid() {
50+
// All went fine!
51+
return &AuthResponse{
52+
Username: resp.GetDetails().GetUser(),
53+
}, nil
54+
}
55+
}
56+
}
57+
}
58+
59+
return nil, nil
60+
}

integrations/envoy/auth/v3/consts.go

+14
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,18 @@ package v3
2222

2323
const (
2424
Name = "envoy.auth.v3"
25+
26+
AuthConfigKeywordTrue = "true"
27+
AuthConfigKeywordFalse = "false"
28+
29+
AuthConfigNamespace = "platform.arangodb.com"
30+
AuthConfigAuthNamespace = "auth." + AuthConfigNamespace
31+
32+
AuthConfigTypeKey = AuthConfigNamespace + "/type"
33+
AuthConfigTypeValue = "ArangoDBPlatform"
34+
35+
AuthConfigAuthRequiredKey = AuthConfigAuthNamespace + "/required"
36+
37+
AuthUsernameHeader = "arangodb-platform-user"
38+
AuthAuthenticatedHeader = "arangodb-platform-authenticated"
2539
)

integrations/envoy/auth/v3/impl.go

+88-4
Original file line numberDiff line numberDiff line change
@@ -22,22 +22,32 @@ package v3
2222

2323
import (
2424
"context"
25+
"net/http"
2526

27+
corev3 "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
2628
pbEnvoyAuthV3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
2729
"google.golang.org/grpc"
2830

31+
pbAuthenticationV1 "github.com/arangodb/kube-arangodb/integrations/authentication/v1/definition"
32+
"github.com/arangodb/kube-arangodb/pkg/util"
33+
"github.com/arangodb/kube-arangodb/pkg/util/errors"
34+
"github.com/arangodb/kube-arangodb/pkg/util/errors/panics"
2935
"github.com/arangodb/kube-arangodb/pkg/util/svc"
3036
)
3137

32-
func New() svc.Handler {
33-
return &impl{}
38+
func New(authClient pbAuthenticationV1.AuthenticationV1Client) svc.Handler {
39+
return &impl{
40+
authClient: authClient,
41+
}
3442
}
3543

3644
var _ pbEnvoyAuthV3.AuthorizationServer = &impl{}
3745
var _ svc.Handler = &impl{}
3846

3947
type impl struct {
4048
pbEnvoyAuthV3.UnimplementedAuthorizationServer
49+
50+
authClient pbAuthenticationV1.AuthenticationV1Client
4151
}
4252

4353
func (i *impl) Name() string {
@@ -53,10 +63,84 @@ func (i *impl) Register(registrar *grpc.Server) {
5363
}
5464

5565
func (i *impl) Check(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest) (*pbEnvoyAuthV3.CheckResponse, error) {
56-
logger.Info("Request Received")
66+
resp, err := panics.RecoverO1(func() (*pbEnvoyAuthV3.CheckResponse, error) {
67+
return i.check(ctx, request)
68+
})
69+
70+
if err != nil {
71+
var v DeniedResponse
72+
if errors.As(err, &v) {
73+
return v.GetCheckResponse()
74+
}
75+
return nil, err
76+
}
77+
return resp, nil
78+
}
79+
80+
func (i *impl) check(ctx context.Context, request *pbEnvoyAuthV3.CheckRequest) (*pbEnvoyAuthV3.CheckResponse, error) {
81+
ext := request.GetAttributes().GetContextExtensions()
82+
83+
if v, ok := ext[AuthConfigTypeKey]; !ok || v != AuthConfigTypeValue {
84+
return nil, DeniedResponse{
85+
Code: http.StatusBadRequest,
86+
Message: &DeniedMessage{
87+
Message: "Auth plugin is not enabled for this request",
88+
},
89+
}
90+
}
91+
92+
authenticated, err := MergeAuthRequest(ctx, request, i.checkADBJWT)
93+
if err != nil {
94+
return nil, err
95+
}
96+
97+
if util.Optional(ext, AuthConfigAuthRequiredKey, AuthConfigKeywordFalse) == AuthConfigKeywordTrue && authenticated == nil {
98+
return nil, DeniedResponse{
99+
Code: http.StatusUnauthorized,
100+
Message: &DeniedMessage{
101+
Message: "Unauthorized",
102+
},
103+
}
104+
}
105+
106+
if authenticated != nil {
107+
return &pbEnvoyAuthV3.CheckResponse{
108+
HttpResponse: &pbEnvoyAuthV3.CheckResponse_OkResponse{
109+
OkResponse: &pbEnvoyAuthV3.OkHttpResponse{
110+
Headers: []*corev3.HeaderValueOption{
111+
{
112+
Header: &corev3.HeaderValue{
113+
Key: AuthUsernameHeader,
114+
Value: authenticated.Username,
115+
},
116+
AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD,
117+
},
118+
{
119+
Header: &corev3.HeaderValue{
120+
Key: AuthAuthenticatedHeader,
121+
Value: "true",
122+
},
123+
AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD,
124+
},
125+
},
126+
},
127+
},
128+
}, nil
129+
}
130+
57131
return &pbEnvoyAuthV3.CheckResponse{
58132
HttpResponse: &pbEnvoyAuthV3.CheckResponse_OkResponse{
59-
OkResponse: &pbEnvoyAuthV3.OkHttpResponse{},
133+
OkResponse: &pbEnvoyAuthV3.OkHttpResponse{
134+
Headers: []*corev3.HeaderValueOption{
135+
{
136+
Header: &corev3.HeaderValue{
137+
Key: AuthAuthenticatedHeader,
138+
Value: "false",
139+
},
140+
AppendAction: corev3.HeaderValueOption_OVERWRITE_IF_EXISTS_OR_ADD,
141+
},
142+
},
143+
},
60144
},
61145
}, nil
62146
}

0 commit comments

Comments
 (0)