Skip to content

Commit 1e7f272

Browse files
authored
MCS service account authentication with Mkube (#166)
`MCS` will authenticate against `Mkube`using bearer tokens via HTTP `Authorization` header. The user will provide this token once in the login form, MCS will validate it against Mkube (list tenants) and if valid will generate and return a new MCS sessions with encrypted claims (the user Service account token will be inside the JWT in the data field) Kubernetes The provided `JWT token` corresponds to the `Kubernetes service account` that `Mkube` will use to run tasks on behalf of the user, ie: list, create, edit, delete tenants, storage class, etc. Development If you are running mcs in your local environment and wish to make request to `Mkube` you can set `MCS_M3_HOSTNAME`, if the environment variable is not present by default `MCS` will use `"http://m3:8787"`, additionally you will need to set the `MCS_MKUBE_ADMIN_ONLY=on` variable to make MCS display the Mkube UI Extract the Service account token and use it with MCS For local development you can use the jwt associated to the `m3-sa` service account, you can get the token running the following command in your terminal: ``` kubectl get secret $(kubectl get serviceaccount m3-sa -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode ``` Then run the mcs server ``` MCS_M3_HOSTNAME=http://localhost:8787 MCS_MKUBE_ADMIN_ONLY=on ./mcs server ``` Self-signed certificates and Custom certificate authority for Mkube If Mkube uses TLS with a self-signed certificate, or a certificate issued by a custom certificate authority you can add those certificates usinng the `MCS_M3_SERVER_TLS_CA_CERTIFICATE` env variable ```` MCS_M3_SERVER_TLS_CA_CERTIFICATE=cert1.pem,cert2.pem,cert3.pem ./mcs server ````
1 parent 1aec2d8 commit 1e7f272

36 files changed

+1529
-384
lines changed

docs/mcs_service_account_mkube.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# MCS service account authentication with Mkube
2+
3+
`MCS` will authenticate against `Mkube`using bearer tokens via HTTP `Authorization` header. The user will provide this token once
4+
in the login form, MCS will validate it against Mkube (list tenants) and if valid will generate and return a new MCS sessions
5+
with encrypted claims (the user Service account token will be inside the JWT in the data field)
6+
7+
# Kubernetes
8+
9+
The provided `JWT token` corresponds to the `Kubernetes service account` that `Mkube` will use to run tasks on behalf of the
10+
user, ie: list, create, edit, delete tenants, storage class, etc.
11+
12+
# Development
13+
14+
If you are running mcs in your local environment and wish to make request to `Mkube` you can set `MCS_M3_HOSTNAME`, if
15+
the environment variable is not present by default `MCS` will use `"http://m3:8787"`, additionally you will need to set the
16+
`MCS_MKUBE_ADMIN_ONLY=on` variable to make MCS display the Mkube UI
17+
18+
## Extract the Service account token and use it with MCS
19+
20+
For local development you can use the jwt associated to the `m3-sa` service account, you can get the token running
21+
the following command in your terminal:
22+
23+
```
24+
kubectl get secret $(kubectl get serviceaccount m3-sa -o jsonpath="{.secrets[0].name}") -o jsonpath="{.data.token}" | base64 --decode
25+
```
26+
27+
Then run the mcs server
28+
29+
```
30+
MCS_M3_HOSTNAME=http://localhost:8787 MCS_MKUBE_ADMIN_ONLY=on ./mcs server
31+
```
32+
33+
# Self-signed certificates and Custom certificate authority for Mkube
34+
35+
If Mkube uses TLS with a self-signed certificate, or a certificate issued by a custom certificate authority you can add those
36+
certificates usinng the `MCS_M3_SERVER_TLS_CA_CERTIFICATE` env variable
37+
38+
````
39+
MCS_M3_SERVER_TLS_CA_CERTIFICATE=cert1.pem,cert2.pem,cert3.pem ./mcs server
40+
````

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ require (
1818
github.com/json-iterator/go v1.1.9
1919
github.com/minio/cli v1.22.0
2020
github.com/minio/mc v0.0.0-20200515235434-3b479cf92ed6
21-
github.com/minio/minio v0.0.0-20200516011754-9cac385aecdb
22-
github.com/minio/minio-go/v6 v6.0.56-0.20200502013257-a81c8c13cc3f
21+
github.com/minio/minio v0.0.0-20200603201854-5686a7e27319
22+
github.com/minio/minio-go/v6 v6.0.56
2323
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
2424
github.com/satori/go.uuid v1.2.0
2525
github.com/stretchr/testify v1.5.1

go.sum

Lines changed: 45 additions & 5 deletions
Large diffs are not rendered by default.

models/login_details.go

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

models/login_mkube_request.go

Lines changed: 81 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/acl/config.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ import (
2222
"github.com/minio/minio/pkg/env"
2323
)
2424

25-
// GetOperatorOnly gets mcs operator mode status set on env variable
26-
//or default one
27-
func GetOperatorOnly() string {
28-
return strings.ToLower(env.Get(McsOperatorOnly, "off"))
25+
// GetOperatorOnly gets MCS mkube admin mode status set on env variable
26+
// or default one
27+
func GetOperatorOnly() bool {
28+
return strings.ToLower(env.Get(McsmKubeAdminOnly, "off")) == "on"
2929
}

pkg/acl/const.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@
1717
package acl
1818

1919
const (
20-
McsOperatorOnly = "MCS_OPERATOR_ONLY"
20+
McsmKubeAdminOnly = "MCS_MKUBE_ADMIN_ONLY"
2121
)

pkg/acl/endpoints.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -286,7 +286,7 @@ func actionsStringToActionSet(actions []string) iampolicy.ActionSet {
286286
func GetAuthorizedEndpoints(actions []string) []string {
287287
rangeTake := endpointRules
288288

289-
if operatorOnly == "on" {
289+
if operatorOnly {
290290
rangeTake = operatorRules
291291
}
292292

pkg/acl/endpoints_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ func TestGetAuthorizedEndpoints(t *testing.T) {
106106
}
107107

108108
func TestOperatorOnlyEndpoints(t *testing.T) {
109-
operatorOnly = "on"
109+
operatorOnly = true
110110

111111
tests := []endpoint{
112112
{

pkg/auth/jwt.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,11 @@ import (
2626
"fmt"
2727
"io"
2828
"log"
29+
"net/http"
2930
"strings"
3031

3132
jwtgo "github.com/dgrijalva/jwt-go"
33+
"github.com/go-openapi/swag"
3234
xjwt "github.com/minio/mcs/pkg/auth/jwt"
3335
"github.com/minio/minio-go/v6/pkg/credentials"
3436
"github.com/minio/minio/cmd"
@@ -182,3 +184,42 @@ func decrypt(data []byte) ([]byte, error) {
182184
}
183185
return plaintext, nil
184186
}
187+
188+
// GetTokenFromRequest returns a token from a http Request
189+
// either defined on a cookie `token` or on Authorization header.
190+
//
191+
// Authorization Header needs to be like "Authorization Bearer <jwt_token>"
192+
func GetTokenFromRequest(r *http.Request) (*string, error) {
193+
// Get Auth token
194+
var reqToken string
195+
196+
// Token might come either as a Cookie or as a Header
197+
// if not set in cookie, check if it is set on Header.
198+
tokenCookie, err := r.Cookie("token")
199+
if err != nil {
200+
headerToken := r.Header.Get("Authorization")
201+
// reqToken should come as "Bearer <token>"
202+
splitHeaderToken := strings.Split(headerToken, "Bearer")
203+
if len(splitHeaderToken) <= 1 {
204+
return nil, errNoAuthToken
205+
}
206+
reqToken = strings.TrimSpace(splitHeaderToken[1])
207+
} else {
208+
reqToken = strings.TrimSpace(tokenCookie.Value)
209+
}
210+
return swag.String(reqToken), nil
211+
}
212+
213+
func GetClaimsFromTokenInRequest(req *http.Request) (*DecryptedClaims, error) {
214+
sessionID, err := GetTokenFromRequest(req)
215+
if err != nil {
216+
return nil, err
217+
}
218+
// Perform decryption of the JWT, if MCS is able to decrypt the JWT that means a valid session
219+
// was used in the first place to get it
220+
claims, err := JWTAuthenticate(*sessionID)
221+
if err != nil {
222+
return nil, err
223+
}
224+
return claims, nil
225+
}

0 commit comments

Comments
 (0)