Add client_id support for OpenID (#8579)

- One click OpenID authorization on Login page
- Add client_id help, config keys etc

Thanks to @egorkaru @ihostage for the
original work and testing.
This commit is contained in:
Harshavardhana
2019-11-29 21:37:42 -08:00
committed by GitHub
parent db3dbcce3a
commit 0bfd20a8e3
18 changed files with 324 additions and 152 deletions

View File

@@ -36,6 +36,7 @@ export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minio123
export MINIO_IDENTITY_OPENID_STATE="on"
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
minio server /mnt/data
```
@@ -49,6 +50,7 @@ export MINIO_ACCESS_KEY=aws_access_key
export MINIO_SECRET_KEY=aws_secret_key
export MINIO_IDENTITY_OPENID_STATE="on"
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
export MINIO_ETCD_ENDPOINTS=http://localhost:2379
minio gateway s3
```

View File

@@ -91,17 +91,19 @@ http://minio.cluster:9000?Action=AssumeRoleWithClientGrants&DurationSeconds=3600
## Testing
```
$ export MINIO_ACCESS_KEY=minio
$ export MINIO_SECRET_KEY=minio123
$ export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
$ export MINIO_POLICY_OPA_URL=http://localhost:8181/v1/data/httpapi/authz
$ minio server /mnt/export
export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minio123
export MINIO_IDENTITY_OPENID_STATE="on"
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
export MINIO_IDENTITY_OPENID_CLIENT_ID="7a243d56-1081-11ea-b1b9-0bad8bed6ca0"
export MINIO_POLICY_OPA_URL=http://localhost:8181/v1/data/httpapi/authz
minio server /mnt/export
$ mc admin config get myminio identity_openid
identity_openid config_url="https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration" state="on"
mc admin config get myminio identity_openid
identity_openid config_url="https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration"
$ mc admin config get myminio policy_opa
policy_opa auth_token="" state="on" url="http://localhost:8181/v1/data/httpapi/authz"
mc admin config get myminio policy_opa
policy_opa url="http://localhost:8181/v1/data/httpapi/authz" auth_token=
```
Testing with an example

View File

@@ -8,24 +8,22 @@ Keycloak is an open source Identity and Access Management solution aimed at mode
- Download and start Keycloak server by following the [installation guide](https://www.keycloak.org/docs/latest/getting_started/index.html) (finish upto section 3.4)
## 2. Configure Keycloak
- Go to Clients -> Click on account -> Settings -> Enable `Implicit Flow`, then Save.
- Go to Users -> Click on the user -> Attribute, add a new attribute `Key` is `policy`, `Value` is name of the policy in minio (ex: `readwrite`). Click Add and then Save.
- Go to Clients -> Click on `account` -> Settings, set `Valid Redirect URIs` to `*`, expand `Advanced Settings` and set `Access Token Lifespan` to `1 Hours`, then Save.
- Go to Clients -> Client on `account` -> Mappers -> Create, `Name` can be any text, `Mapper Type` is `User Attribute`, `User Attribute` is `policy`, `Token Claim Name` is `policy`, `Claim JSON Type` is `string`, then Save.
- Open http://localhost:8080/auth/realms/demo/.well-known/openid-configuration and see if it has `authorization_endpoint` and `jwks_uri`
## 3. Configure MinIO
```
$ export MINIO_ACCESS_KEY=minio
$ export MINIO_SECRET_KEY=minio123
$ minio server /mnt/export
```
Set `identity_openid` config and restart MinIO
Set `identity_openid` config with `config_url`, `client_id` and restart MinIO
```
~ mc admin config set myminio identity_openid config_url="http://localhost:8080/auth/realms/demo/.well-known/openid-configuration" state="on"
~ mc admin config set myminio identity_openid config_url="http://localhost:8080/auth/realms/demo/.well-known/openid-configuration" client_id="account"
```
Once successfully set restart the MinIO instance.
@@ -34,11 +32,10 @@ mc admin service restart myminio
```
## 4. Using WebIdentiy API
Client ID and Client Secret can be found by clicking any of the clients listed [here](http://localhost:8080/auth/admin/master/console/#/realms/demo/clients). If you have followed the above steps docs, the default Client ID will be `account` and Client Secret can be found under `Credentials` tab.
Client ID can be found by clicking any of the clients listed [here](http://localhost:8080/auth/admin/master/console/#/realms/demo/clients). If you have followed the above steps docs, the default Client ID will be `account`.
```
$ go run web-identity.go -cid account -csec e61cb282-745b-4113-bece-29b921c735f0 -auth-ep http://localhost:8080/auth/realms/demo/protocol/openid-connect/auth -token-ep http://localhost:8080/auth/realms/demo/protocol/openid-connect/token -port 8888
$ go run docs/sts/web-identity.go -cid account -csec 072e7f00-4289-469c-9ab2-bbe843c7f5a8 -config-ep "http://localhost:8080/auth/realms/demo/.well-known/openid-configuration" -port 8888
2018/12/26 17:49:36 listening on http://localhost:8888/
```

View File

@@ -22,6 +22,7 @@ import (
"context"
"crypto/rand"
"encoding/base64"
"encoding/json"
"encoding/xml"
"errors"
"flag"
@@ -32,7 +33,6 @@ import (
"time"
"golang.org/x/oauth2"
googleOAuth2 "golang.org/x/oauth2/google"
"github.com/minio/minio-go/v6"
"github.com/minio/minio-go/v6/pkg/credentials"
@@ -75,41 +75,89 @@ func randomState() string {
}
var (
stsEndpoint string
authEndpoint string
tokenEndpoint string
clientID string
clientSecret string
port int
stsEndpoint string
configEndpoint string
clientID string
clientSec string
port int
)
// DiscoveryDoc - parses the output from openid-configuration
// for example https://accounts.google.com/.well-known/openid-configuration
type DiscoveryDoc struct {
Issuer string `json:"issuer,omitempty"`
AuthEndpoint string `json:"authorization_endpoint,omitempty"`
TokenEndpoint string `json:"token_endpoint,omitempty"`
UserInfoEndpoint string `json:"userinfo_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
JwksURI string `json:"jwks_uri,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
IDTokenSigningAlgValuesSupported []string `json:"id_token_signing_alg_values_supported,omitempty"`
ScopesSupported []string `json:"scopes_supported,omitempty"`
TokenEndpointAuthMethods []string `json:"token_endpoint_auth_methods_supported,omitempty"`
ClaimsSupported []string `json:"claims_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
}
func parseDiscoveryDoc(ustr string) (DiscoveryDoc, error) {
d := DiscoveryDoc{}
req, err := http.NewRequest(http.MethodGet, ustr, nil)
if err != nil {
return d, err
}
clnt := http.Client{
Transport: http.DefaultTransport,
}
resp, err := clnt.Do(req)
if err != nil {
return d, err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return d, err
}
dec := json.NewDecoder(resp.Body)
if err = dec.Decode(&d); err != nil {
return d, err
}
return d, nil
}
func init() {
flag.StringVar(&stsEndpoint, "sts-ep", "http://localhost:9000", "STS endpoint")
flag.StringVar(&authEndpoint, "auth-ep", googleOAuth2.Endpoint.AuthURL, "Auth endpoint")
flag.StringVar(&tokenEndpoint, "token-ep", googleOAuth2.Endpoint.TokenURL, "Token endpoint")
flag.StringVar(&configEndpoint, "config-ep",
"https://accounts.google.com/.well-known/openid-configuration",
"OpenID discovery document endpoint")
flag.StringVar(&clientID, "cid", "", "Client ID")
flag.StringVar(&clientSecret, "csec", "", "Client secret")
flag.StringVar(&clientSec, "csec", "", "Client Secret")
flag.IntVar(&port, "port", 8080, "Port")
}
func main() {
flag.Parse()
if clientID == "" || clientSecret == "" {
if clientID == "" || clientSec == "" {
flag.PrintDefaults()
return
}
ddoc, err := parseDiscoveryDoc(configEndpoint)
if err != nil {
fmt.Println(err)
return
}
ctx := context.Background()
config := oauth2.Config{
ClientID: clientID,
ClientSecret: clientSecret,
ClientSecret: clientSec,
Endpoint: oauth2.Endpoint{
AuthURL: authEndpoint,
TokenURL: tokenEndpoint,
AuthURL: ddoc.AuthEndpoint,
TokenURL: ddoc.TokenEndpoint,
},
RedirectURL: fmt.Sprintf("http://localhost:%v/oauth2/callback", port),
Scopes: []string{"openid", "profile", "email"},
RedirectURL: fmt.Sprintf("http://localhost:%d/oauth2/callback", port),
Scopes: []string{"openid"},
}
state := randomState()
@@ -138,6 +186,7 @@ func main() {
Expiry: int(oauth2Token.Expiry.Sub(time.Now().UTC()).Seconds()),
}, nil
}
sts, err := credentials.NewSTSWebIdentity(stsEndpoint, getWebTokenExpiry)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)

View File

@@ -91,13 +91,18 @@ http://minio.cluster:9000?Action=AssumeRoleWithWebIdentity&DurationSeconds=3600&
## Testing
```
$ export MINIO_ACCESS_KEY=minio
$ export MINIO_SECRET_KEY=minio123
$ export MINIO_IDENTITY_OPENID_CONFIG_URL=https://accounts.google.com/.well-known/openid-configuration
$ minio server /mnt/export
export MINIO_ACCESS_KEY=minio
export MINIO_SECRET_KEY=minio123
export MINIO_IDENTITY_OPENID_STATE="on"
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://accounts.google.com/.well-known/openid-configuration
minio server /mnt/export
```
$ mc admin config get myminio identity_openid
identity_openid config_url="https://accounts.google.com/.well-known/openid-configuration" state="on"
or using `mc`
```
mc admin config get myminio identity_openid
identity_openid config_url=https://accounts.google.com/.well-known/openid-configuration client_id=843351d4-1080-11ea-aa20-271ecba3924a
```
Testing with an example
@@ -120,20 +125,23 @@ $ go run web-identity.go -cid 204367807228-ok7601k6gj1pgge7m09h7d79co8p35xx.apps
## MinIO Browser
To support WebIdentity login on MinIO Browser
1. Set openid configuration and restart MinIO
```
mc admin config set myminio identity_openid jwks_url="<JWKS_URL>" config_url="<CONFIG_URL>"
mc admin service restart myminio
```
Sample URLs for Keycloak are
`config_url` - `http://localhost:8080/auth/realms/demo/.well-known/openid-configuration`,
`jwks_url` - `http://localhost:8080/auth/realms/demo/protocol/openid-connect/certs`
- Set openid configuration and restart MinIO
JWT token returned by the Identity Provider should include a custom claim for the policy, this is required to create a STS user in MinIO. The name of the custom claim could be either `policy` or `<NAMESPACE_PREFIX>policy`.
If there is no namespace then `policy_claim_prefix` can be ingored. For example if the custom claim name is `https://min.io/policy` then, `policy_claim_prefix` should be set as `https://min.io/`
```
mc admin config set myminio identity_openid config_url="<CONFIG_URL>" client_id="<client_identifier>"
```
2. Open MinIO Browser and click `Log in with OpenID`
3. Enter the `Client ID` obtained from Identity Provider and press ENTER
4. The user will be redirected to the Identity Provider login page
5. Upon successful login on Identity Provider page the user will be automatically logged into MinIO Browser
```
mc admin service restart myminio
```
Sample URLs for Keycloak are
`config_url` - `http://localhost:8080/auth/realms/demo/.well-known/openid-configuration`
JWT token returned by the Identity Provider should include a custom claim for the policy, this is required to create a STS user in MinIO. The name of the custom claim could be either `policy` or `<NAMESPACE_PREFIX>policy`. If there is no namespace then `claim_prefix` can be ingored. For example if the custom claim name is `https://min.io/policy` then, `claim_prefix` should be set as `https://min.io/`.
- Open MinIO Browser and click `Log in with OpenID`
- Enter the `Client ID` obtained from Identity Provider and press ENTER, if not you can set a `client_id` on server to avoid this step.
- The user will be redirected to the Identity Provider login page
- Upon successful login on Identity Provider page the user will be automatically logged into MinIO Browser

View File

@@ -69,7 +69,9 @@ Using the above `access_token` we can perform an STS request to MinIO to get tem
### 5. Setup MinIO with OpenID configuration URL
MinIO server expects environment variable for OpenID configuration url as `MINIO_IDENTITY_OPENID_CONFIG_URL`, this environment variable takes a single entry.
```
export MINIO_IDENTITY_OPENID_STATE="on"
export MINIO_IDENTITY_OPENID_CONFIG_URL=https://localhost:9443/oauth2/oidcdiscovery/.well-known/openid-configuration
export MINIO_IDENTITY_OPENID_CLIENT_ID="843351d4-1080-11ea-aa20-271ecba3924a"
minio server /mnt/data
```