Skip to content

Commit 0e9830f

Browse files
authored
Add API Token Authentication (#15)
* Remove caching of keys Signed-off-by: Jan Steffen <[email protected]> * to be rotated manually Signed-off-by: Jan Steffen <[email protected]> * Refactor Signed-off-by: Jan Steffen <[email protected]> * refactor Signed-off-by: Jan Steffen <[email protected]> * Refactor Signed-off-by: Jan Steffen <[email protected]> * Remove unused Signed-off-by: Jan Steffen <[email protected]> * Refactor and add scopes Signed-off-by: Jan Steffen <[email protected]> * Add simple scope validation Signed-off-by: Jan Steffen <[email protected]> * Test unauthorized scopes Signed-off-by: Jan Steffen <[email protected]> * Add tests Signed-off-by: Jan Steffen <[email protected]> * Fix cmd Signed-off-by: Jan Steffen <[email protected]> * Remove unused flag Signed-off-by: Jan Steffen <[email protected]> * Adjust readme Signed-off-by: Jan Steffen <[email protected]> * Implement new interface Signed-off-by: Jan Steffen <[email protected]> * Furhter implement API tokens Signed-off-by: Jan Steffen <[email protected]> * Implement endpoint Signed-off-by: Jan Steffen <[email protected]> * Test scope check Signed-off-by: Jan Steffen <[email protected]> * Add validation for admin role Signed-off-by: Jan Steffen <[email protected]> * Remove deprecated runners and enhance test suite run Signed-off-by: Jan Steffen <[email protected]> * Simpify Signed-off-by: Jan Steffen <[email protected]> * Add mapping Signed-off-by: Jan Steffen <[email protected]> * Remove flag Signed-off-by: Jan Steffen <[email protected]> * Add server implementation Signed-off-by: Jan Steffen <[email protected]> * Make it optional Signed-off-by: Jan Steffen <[email protected]> * Revert "Make it optional" This reverts commit 65dec47. Signed-off-by: Jan Steffen <[email protected]> * Fix bug Signed-off-by: Jan Steffen <[email protected]> * Return user if already exists Signed-off-by: Jan Steffen <[email protected]> * Auth all verbs Signed-off-by: Jan Steffen <[email protected]> * Adjusted validators Signed-off-by: Jan Steffen <[email protected]>
1 parent 2eabaf4 commit 0e9830f

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+4117
-1802
lines changed

api/gateway/messages.proto

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
// Copyright 2021 Monoskope Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
syntax = "proto3";
16+
17+
// This file follows google's gRPC naming conventions:
18+
// https://cloud.google.com/apis/design/naming_convention
19+
20+
import "google/protobuf/timestamp.proto";
21+
import "google/protobuf/duration.proto";
22+
import "validate/validate.proto";
23+
24+
option go_package = "github.com/finleap-connect/monoskope/pkg/api/gateway";
25+
26+
package gateway;
27+
28+
message UpstreamAuthenticationRequest {
29+
// callback_url is the URL where the authorization code
30+
// will be redirected to by the upstream IDP
31+
string callback_url = 1 [ (validate.rules).string.uri = true ];
32+
}
33+
34+
message UpstreamAuthenticationResponse {
35+
// upstream_idp_redirect is the URL of the IDP to authenticate against
36+
string upstream_idp_redirect = 1 [ (validate.rules).string.uri = true ];
37+
// state is the encoded, server-side nonced state containing the callback.
38+
// This has to be presented to the server along with the actual m8
39+
// AuthenticationRequest.
40+
string state = 2;
41+
}
42+
43+
message AuthenticationRequest {
44+
// code is the auth code received by the IDP
45+
string code = 1;
46+
// state is the encoded, nonced AuthState
47+
string state = 2;
48+
}
49+
50+
message AuthenticationResponse {
51+
// access_token is a JWT to authenticate against the m8 API
52+
string access_token = 1;
53+
// expiry is the timestamp when the token expires
54+
google.protobuf.Timestamp expiry = 2;
55+
// username is the username known the m8 control plane
56+
string username = 3;
57+
}
58+
59+
message AuthState {
60+
// callback_url is the url to send the auth token response too
61+
string callback_url = 1 [ (validate.rules).string.uri = true ];
62+
}
63+
64+
// AuthInformation is the response to an AuthState message.
65+
// It contains the URL
66+
message AuthInformation {
67+
// auth_code_url is the URL of the IDP to authenticate against
68+
string auth_code_url = 1 [ (validate.rules).string.uri = true ];
69+
// state is the encoded, nonced AuthState
70+
string state = 2;
71+
}
72+
73+
// AuthCode is the request to exchange the auth code received from
74+
// an upstream identity provider against a token issued by m8 for
75+
// authentication.
76+
message AuthCode {
77+
// code is the auth code received by the IDP
78+
string code = 1;
79+
// state is the encoded, nonced AuthState
80+
string state = 2;
81+
// callback_url is the url to send the auth token response too
82+
string callback_url = 3 [ (validate.rules).string.uri = true ];
83+
}
84+
85+
// AuthResponse is the response to an AuthCode message.
86+
// It contains a JWT to authenticate against the m8 API.
87+
message AuthResponse {
88+
// access_token is a JWT to authenticate against the m8 API
89+
string access_token = 1;
90+
// expiry is the timestamp when the token expires
91+
google.protobuf.Timestamp expiry = 2;
92+
// username is the username known the m8 control plane
93+
string username = 3;
94+
}
95+
96+
// ClusterAuthTokenRequest is send in order to retrieve an auth token valid to
97+
// authenticate against a certain cluster with a specific role.
98+
message ClusterAuthTokenRequest {
99+
// Unique identifier of the cluster (UUID 128-bit number)
100+
string cluster_id = 1 [ (validate.rules).string.uuid = true ];
101+
// Kubernetes role name
102+
string role = 2
103+
[ (validate.rules).string = {pattern : "^[a-z0-9-]+$", max_bytes : 60} ];
104+
}
105+
106+
// ClusterAuthTokenResponse contains an auth token valid to
107+
// authenticate against a certain cluster with a specific role.
108+
message ClusterAuthTokenResponse {
109+
// JWT to authenticate against a K8s cluster
110+
string access_token = 1;
111+
// Timestamp when the token expires
112+
google.protobuf.Timestamp expiry = 2;
113+
}
114+
115+
// APITokenRequest is send in order to retrieve an API token valid to
116+
// authenticate against Monoskope and authorize specific scopes.
117+
message APITokenRequest {
118+
// Scope the resulting token is issued for
119+
repeated AuthorizationScope authorization_scopes = 1;
120+
121+
// Duration for which the issued token will be valid
122+
google.protobuf.Duration validity = 2;
123+
124+
oneof user {
125+
// Unique identifier of an existing user (UUID 128-bit number)
126+
string user_id = 3 [ (validate.rules).string.uuid = true ];
127+
// Name of the user the token is valid for (not necessarily a real user)
128+
string username = 4;
129+
}
130+
}
131+
132+
// APITokenResponse is the answer to an APITokenRequest
133+
// containing a JWT to authenticate against the m8 API.
134+
message APITokenResponse {
135+
// JWT to authenticate against the m8 API
136+
string access_token = 1;
137+
// Timestamp when the token expires
138+
google.protobuf.Timestamp expiry = 2;
139+
}
140+
141+
// AuthorizationScope is an enum defining the available API scopes.
142+
enum AuthorizationScope {
143+
NONE = 0; // Dummy to prevent accidents
144+
API = 1; // Read-write for the complete API
145+
WRITE_SCIM = 2; // Read-write for endpoints under "/scim"
146+
WRITE_K8SOPERATOR = 3; // Read-write for K8sOperator endpoints
147+
}

api/gateway/service.proto

Lines changed: 19 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -17,55 +17,37 @@ syntax = "proto3";
1717
// This file follows google's gRPC naming conventions:
1818
// https://cloud.google.com/apis/design/naming_convention
1919

20-
import "google/protobuf/timestamp.proto";
21-
import "validate/validate.proto";
20+
import "api/gateway/messages.proto";
2221

2322
option go_package = "github.com/finleap-connect/monoskope/pkg/api/gateway";
2423

2524
package gateway;
2625

2726
// API of the Monoskope Gateway.
2827
service Gateway {
29-
rpc GetAuthInformation(AuthState) returns (AuthInformation);
30-
rpc ExchangeAuthCode(AuthCode) returns (AuthResponse);
28+
rpc GetAuthInformation(AuthState) returns (AuthInformation) {
29+
option deprecated = true;
30+
};
31+
rpc ExchangeAuthCode(AuthCode) returns (AuthResponse) {
32+
option deprecated = true;
33+
};
34+
35+
// PrepareAuthentication returns the URL to call to authenticate against the
36+
// upstream IDP
37+
rpc RequestUpstreamAuthentication(UpstreamAuthenticationRequest)
38+
returns (UpstreamAuthenticationResponse);
39+
// RequestAuthentication is called to exchange the authorization code with the
40+
// upstream IDP and to authenticate with the m8 control plane
41+
rpc RequestAuthentication(AuthenticationRequest)
42+
returns (AuthenticationResponse);
3143
}
3244

3345
// ClusterAuth is the API to request token for cluster authentication from
3446
service ClusterAuth {
3547
rpc GetAuthToken(ClusterAuthTokenRequest) returns (ClusterAuthTokenResponse);
3648
}
3749

38-
message AuthState { string callback_url = 1; }
39-
40-
message AuthInformation {
41-
string auth_code_url = 1;
42-
string state = 2;
43-
}
44-
45-
message AuthCode {
46-
string code = 1;
47-
string state = 2;
48-
string callback_url = 3;
49-
}
50-
51-
message AuthResponse {
52-
string access_token = 1;
53-
google.protobuf.Timestamp expiry = 2;
54-
string username = 3;
55-
}
56-
57-
// ClusterAuthTokenRequest is send in order to retrieve an auth token valid to
58-
// authenticate against a certain cluster with a specific role
59-
message ClusterAuthTokenRequest {
60-
// Unique identifier of the cluster (UUID 128-bit number)
61-
string cluster_id = 1 [(validate.rules).string.uuid = true];
62-
// Kubernetes role name
63-
string role = 2 [(validate.rules).string = {pattern: "^[a-z]+$", max_bytes: 60}];
64-
}
65-
66-
// ClusterAuthTokenResponse contains an auth token valid to
67-
// authenticate against a certain cluster with a specific role
68-
message ClusterAuthTokenResponse {
69-
string access_token = 1;
70-
google.protobuf.Timestamp expiry = 2;
50+
// APIToken is the API to request API tokens with
51+
service APIToken {
52+
rpc RequestAPIToken(APITokenRequest) returns (APITokenResponse);
7153
}

build/package/helm/gateway/README.md

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ Monoskope Gateway
99
| Key | Type | Default | Description |
1010
|-----|------|---------|-------------|
1111
| affinity | object | `{}` | |
12-
| auth.identityProviderName | string | `""` | The identifier of the issuer, e.g. DEX or whatever identifies your identities upstream |
1312
| auth.identityProviderURL | string | `""` | The URL of the issuer to use for OIDC |
1413
| auth.redirectUris | list | `["http://localhost:8000","http://localhost:18000"]` | The allowed redirect URIs for authentication flow |
1514
| auth.scopes | list | `["openid","profile","email"]` | Additional scopes to request from upstream IDP |
@@ -25,11 +24,10 @@ Monoskope Gateway
2524
| image.repository | string | `"ghcr.io/finleap-connect/monoskope/gateway"` | |
2625
| image.tag | string | `""` | Overrides the image tag whose default is the chart appVersion. |
2726
| imagePullSecrets | list | `[]` | |
28-
| k8sTokenValidity | string | `"10s"` | Duration for which issued K8s auth tokens are valid |
27+
| k8sTokenValidity | string | `"360s"` | Duration for which issued K8s auth tokens are valid |
2928
| keepAlive | bool | `false` | |
30-
| keySecret | object | `{"name":"","validity":"24h"}` | The secret containing private key for signing JWTs. Must contain tls.key containing the private key for signing and tls.crt containing public key for verification. |
29+
| keySecret | object | `{"name":""}` | The secret containing private key for signing JWTs. Must contain tls.key containing the private key for signing and tls.crt containing public key for verification. |
3130
| keySecret.name | string | `""` | Name of the secret to be used by the gateway, required |
32-
| keySecret.validity | string | `"24h"` | How long to cache public key's |
3331
| labels | object | `{}` | |
3432
| livenessProbe.enabled | bool | `true` | |
3533
| livenessProbe.failureThreshold | int | `10` | |

build/package/helm/gateway/templates/deployment.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,11 +96,9 @@ spec:
9696
- --http-api-addr=:8081
9797
- --metrics-addr=:9102
9898
- {{ (printf "--query-handler-api-addr=%s-%s:%v" (.Values.queryHandler.prefix | default .Release.Name ) .Values.queryHandler.host .Values.queryHandler.port ) }}
99-
- --identity-provider-name={{ required "A valid .Values.auth.identityProviderName entry is required!" .Values.auth.identityProviderName }}
10099
- --identity-provider-url={{ required "A valid .Values.auth.identityProviderURL entry is required!" .Values.auth.identityProviderURL }}
101100
- --scopes={{ join "," .Values.auth.scopes }}
102101
- --redirect-uris={{ join "," .Values.auth.redirectUris }}
103-
- --key-cache-duration={{ .Values.keySecret.validity }}
104102
- --k8s-token-validity={{ .Values.k8sTokenValidity }}
105103
- --auth-token-validity={{ .Values.authTokenValidity }}
106104
- --gateway-url={{ required "A valid .Values.auth.selfURL entry is required!" .Values.auth.selfURL }}

build/package/helm/gateway/values.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ keepAlive: false
8787
auth:
8888
# -- The URL of the issuer to Gateway itself
8989
selfURL: ""
90-
# -- The identifier of the issuer, e.g. DEX or whatever identifies your identities upstream
91-
identityProviderName: ""
9290
# -- The URL of the issuer to use for OIDC
9391
identityProviderURL: ""
9492
# -- Additional scopes to request from upstream IDP
@@ -112,11 +110,9 @@ oidcSecret:
112110
keySecret:
113111
# -- Name of the secret to be used by the gateway, required
114112
name: ""
115-
# -- How long to cache public key's
116-
validity: 24h
117113

118114
# -- Duration for which issued K8s auth tokens are valid
119-
k8sTokenValidity: 10s
115+
k8sTokenValidity: 360s
120116

121117
# -- Duration for which issued Monoskope auth tokens are valid
122118
authTokenValidity: 12h

build/package/helm/monoskope/README.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ Monoskope implements the management and operation of tenants, users and their ro
1313
| file://../eventstore | eventstore | 0.0.1-local |
1414
| file://../gateway | gateway | 0.0.1-local |
1515
| file://../queryhandler | queryhandler | 0.0.1-local |
16-
| https://charts.bitnami.com/bitnami | rabbitmq | 8.17.0 |
16+
| https://charts.bitnami.com/bitnami | rabbitmq | 8.24.3 |
1717
| https://charts.cockroachdb.com/ | cockroachdb | 6.1.2 |
1818
| https://getambassador.io | ambassador | 6.7.11 |
1919

@@ -81,7 +81,6 @@ Monoskope implements the management and operation of tenants, users and their ro
8181
| eventstore.storeDatabase.configSecret | string | `"m8-db-client-config"` | |
8282
| eventstore.storeDatabase.tlsSecret | string | `"m8-db-client-auth-cert"` | |
8383
| fullnameOverride | string | `""` | |
84-
| gateway.auth.identityProviderName | string | `""` | The identifier of the issuer, e.g. DEX or whatever identifies your identities upstream |
8584
| gateway.auth.identityProviderURL | string | `""` | The URL of the issuer to use for OIDC |
8685
| gateway.auth.selfURL | string | `""` | The URL of the issuer to Gateway itself |
8786
| gateway.enabled | bool | `true` | |
@@ -123,7 +122,6 @@ Monoskope implements the management and operation of tenants, users and their ro
123122
| rabbitmq.extraConfiguration | string | `"load_definitions = /app/rabbitmq-definitions.json\nauth_mechanisms.1 = EXTERNAL\nssl_cert_login_from = common_name\nssl_options.depth = 2"` | |
124123
| rabbitmq.extraPlugins | string | `"rabbitmq_auth_mechanism_ssl"` | |
125124
| rabbitmq.image.pullPolicy | string | `"Always"` | |
126-
| rabbitmq.image.tag | string | `"3.8.9"` | |
127125
| rabbitmq.loadDefinition.enabled | bool | `true` | |
128126
| rabbitmq.loadDefinition.existingSecret | string | `"m8-rabbitmq-load-definition"` | |
129127
| rabbitmq.metrics.enabled | bool | `false` | |

build/package/helm/monoskope/templates/ambassador/mappings/gateway.yaml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,5 +78,21 @@ spec:
7878
rewrite: /keys
7979
service: {{.Release.Name}}-gateway:{{.Values.gateway.service.httpApiPort}}
8080
---
81+
apiVersion: getambassador.io/v1
82+
kind: Mapping
83+
metadata:
84+
name: {{ include "monoskope.fullname" . }}-gateway-apitoken-mapping
85+
namespace: {{ .Release.Namespace }}
86+
labels:
87+
{{- include "monoskope.labels" . | nindent 4 }}
88+
{{- with (.Values.labels | default .Values.global.labels) }}
89+
{{- toYaml . | nindent 4 }}
90+
{{- end }}
91+
spec:
92+
grpc: true
93+
timeout_ms: 20000
94+
prefix: /gateway.APIToken/
95+
rewrite: /gateway.APIToken/
96+
service: {{.Release.Name}}-gateway:{{.Values.gateway.service.grpcApiPort}}
8197
{{- end }}
8298
{{- end }}

build/package/helm/monoskope/templates/gateway/cert-auth.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,7 @@ metadata:
1010
{{- end }}
1111
spec:
1212
secretName: {{ .Values.pki.authentication.keySecretName }}
13-
duration: {{ .Values.pki.certificates.duration }}
14-
renewBefore: {{ .Values.pki.certificates.renewBefore }}
13+
duration: 876000h # 100y cause this is to be rotated manually
1514
privateKey:
1615
rotationPolicy: Always
1716
issuerRef:

build/package/helm/monoskope/values.yaml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,6 @@ gateway:
4949
auth:
5050
# -- The URL of the issuer to Gateway itself
5151
selfURL: ""
52-
# -- The identifier of the issuer, e.g. DEX or whatever identifies your identities upstream
53-
identityProviderName: ""
5452
# -- The URL of the issuer to use for OIDC
5553
identityProviderURL: ""
5654
# -- The secret containing private key for signing JWTs.

0 commit comments

Comments
 (0)