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.
master
Harshavardhana 5 years ago committed by GitHub
parent db3dbcce3a
commit 0bfd20a8e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 34
      browser/app/js/browser/Login.js
  2. 24
      browser/app/js/browser/OpenIDLogin.js
  3. 57
      browser/app/js/browser/OpenIDLoginButton.js
  4. 29
      browser/app/js/browser/utils.js
  5. 7
      browser/app/less/inc/login.less
  6. 86
      browser/ui-assets.go
  7. 6
      cmd/config/identity/openid/help.go
  8. 9
      cmd/config/identity/openid/jwt.go
  9. 2
      cmd/web-handlers.go
  10. 40
      docs/bucket/notifications/README.md
  11. 8
      docs/compression/README.md
  12. 8
      docs/logging/README.md
  13. 2
      docs/sts/README.md
  14. 24
      docs/sts/client-grants.md
  15. 13
      docs/sts/keycloak.md
  16. 81
      docs/sts/web-identity.go
  17. 54
      docs/sts/web-identity.md
  18. 2
      docs/sts/wso2.md

@ -16,16 +16,13 @@
import React from "react"
import { connect } from "react-redux"
import classNames from "classnames"
import logo from "../../img/logo.svg"
import Alert from "../alert/Alert"
import * as actionsAlert from "../alert/actions"
import InputGroup from "./InputGroup"
import web from "../web"
import { Redirect, Link } from "react-router-dom"
import qs from "query-string"
import storage from "local-storage-fallback"
import history from "../history"
import OpenIDLoginButton from './OpenIDLoginButton'
export class Login extends React.Component {
constructor(props) {
@ -33,7 +30,8 @@ export class Login extends React.Component {
this.state = {
accessKey: "",
secretKey: "",
discoveryDoc: {}
discoveryDoc: {},
clientId: ""
}
}
@ -88,8 +86,9 @@ export class Login extends React.Component {
}
componentDidMount() {
web.GetDiscoveryDoc().then(({ DiscoveryDoc }) => {
web.GetDiscoveryDoc().then(({ DiscoveryDoc, clientId }) => {
this.setState({
clientId,
discoveryDoc: DiscoveryDoc
})
})
@ -107,6 +106,8 @@ export class Login extends React.Component {
let alertBox = <Alert {...alert} onDismiss={clearAlert} />
// Make sure you don't show a fading out alert box on the initial web-page load.
if (!alert.message) alertBox = ""
const showOpenID = Boolean(this.state.discoveryDoc && this.state.discoveryDoc.authorization_endpoint)
return (
<div className="login">
{alertBox}
@ -139,13 +140,24 @@ export class Login extends React.Component {
<i className="fas fa-sign-in-alt" />
</button>
</form>
{this.state.discoveryDoc &&
this.state.discoveryDoc.authorization_endpoint && (
{showOpenID && (
<div className="openid-login">
<div className="or">or</div>
<a href={"/login/openid"} className="btn openid-btn">
Log in with OpenID
</a>
{
this.state.clientId ? (
<OpenIDLoginButton
className="btn openid-btn"
clientId={this.state.clientId}
authorizationEndpoint={this.state.discoveryDoc.authorization_endpoint}
>
Log in with OpenID
</OpenIDLoginButton>
) : (
<Link to={"/login/openid"} className="btn openid-btn">
Log in with OpenID
</Link>
)
}
</div>
)}
</div>

@ -26,6 +26,7 @@ import qs from "query-string"
import { getRandomString } from "../utils"
import storage from "local-storage-fallback"
import jwtDecode from "jwt-decode"
import { buildOpenIDAuthURL, OPEN_ID_NONCE_KEY } from './utils'
export class OpenIDLogin extends React.Component {
constructor(props) {
@ -58,20 +59,17 @@ export class OpenIDLogin extends React.Component {
if (this.state.discoveryDoc && this.state.discoveryDoc.authorization_endpoint) {
const redirectURI = window.location.href.split("#")[0]
var params = new URLSearchParams()
params.set("response_type", "id_token")
params.set("scope", "openid")
params.set("client_id", this.state.clientID)
params.set("redirect_uri", redirectURI)
// Store nonce in localstorage to check again after the redirect
const nonce = getRandomString(16)
params.set("nonce", nonce)
storage.setItem("openIDKey", nonce)
const authURL = `${
this.state.discoveryDoc.authorization_endpoint
}?${params.toString()}`
storage.setItem(OPEN_ID_NONCE_KEY, nonce)
const authURL = buildOpenIDAuthURL(
this.state.discoveryDoc.authorization_endpoint,
redirectURI,
this.state.clientID,
nonce
)
window.location = authURL
}
}
@ -99,13 +97,13 @@ export class OpenIDLogin extends React.Component {
if (values.id_token) {
// Check nonce on the token to prevent replay attacks
const tokenJSON = jwtDecode(values.id_token)
if (storage.getItem("openIDKey") !== tokenJSON.nonce) {
if (storage.getItem(OPEN_ID_NONCE_KEY) !== tokenJSON.nonce) {
this.props.showAlert("danger", "Invalid auth token")
return
}
web.LoginSTS({ token: values.id_token }).then(() => {
storage.removeItem("openIDKey")
storage.removeItem(OPEN_ID_NONCE_KEY)
this.forceUpdate()
return
})

@ -0,0 +1,57 @@
/*
* MinIO Cloud Storage (C) 2019 MinIO, Inc.
*
* 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.
*/
import React from "react"
import { getRandomString } from "../utils"
import storage from "local-storage-fallback"
import { buildOpenIDAuthURL, OPEN_ID_NONCE_KEY } from './utils'
export class OpenIDLoginButton extends React.Component {
constructor(props) {
super(props)
this.handleClick = this.handleClick.bind(this)
}
handleClick(event) {
event.stopPropagation()
const { authorizationEndpoint, clientId } = this.props
let redirectURI = window.location.href.split("#")[0]
if (redirectURI.endsWith('/')) {
redirectURI += 'openid'
} else {
redirectURI += '/openid'
}
// Store nonce in localstorage to check again after the redirect
const nonce = getRandomString(16)
storage.setItem(OPEN_ID_NONCE_KEY, nonce)
const authURL = buildOpenIDAuthURL(authorizationEndpoint, redirectURI, clientId, nonce)
window.location = authURL
}
render() {
const { children, className } = this.props
return (
<div onClick={this.handleClick} className={className}>
{children}
</div>
)
}
}
export default OpenIDLoginButton

@ -0,0 +1,29 @@
/*
* MinIO Cloud Storage (C) 2019 MinIO, Inc.
*
* 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.
*/
export const OPEN_ID_NONCE_KEY = 'openIDKey'
export const buildOpenIDAuthURL = (authorizationEndpoint, redirectURI, clientID, nonce) => {
const params = new URLSearchParams()
params.set("response_type", "id_token")
params.set("scope", "openid")
params.set("client_id", clientID)
params.set("redirect_uri", redirectURI)
params.set("nonce", nonce)
return `${authorizationEndpoint}?${params.toString()}`
}

@ -101,14 +101,15 @@
.openid-btn {
display: inline-block;
color: @link-color;
margin-top: 30px;
border-width: 1px;
border-style: solid;
opacity: 0.6;
font-size: 14px;
&:hover {
color: @link-color;
opacity: 1;
cursor: pointer;
}
}
@ -118,7 +119,7 @@
align-items: center;
color:grey;
}
.or:after,
.or:before {
content: "";
@ -136,4 +137,4 @@ input:-webkit-autofill {
-webkit-box-shadow:0 0 0 50px #002a37 inset !important;
-webkit-text-fill-color: @white !important;
caret-color: white;
}
}

File diff suppressed because one or more lines are too long

@ -26,6 +26,12 @@ var (
Description: `OpenID discovery documented endpoint. eg: "https://accounts.google.com/.well-known/openid-configuration"`,
Type: "url",
},
config.HelpKV{
Key: ClientID,
Description: `The client identifier of the authenticating party at the identity provider`,
Type: "string",
Optional: true,
},
config.HelpKV{
Key: ClaimPrefix,
Description: `OpenID JWT claim namespace prefix. eg: "customer"`,

@ -42,6 +42,7 @@ type Config struct {
URL *xnet.URL `json:"url,omitempty"`
ClaimPrefix string `json:"claimPrefix,omitempty"`
DiscoveryDoc DiscoveryDoc
ClientID string
publicKeys map[string]crypto.PublicKey
transport *http.Transport
closeRespFn func(io.ReadCloser)
@ -116,6 +117,7 @@ func GetDefaultExpiration(dsecs string) (time.Duration, error) {
if err != nil {
return 0, auth.ErrInvalidDuration
}
// The duration, in seconds, of the role session.
// The value can range from 900 seconds (15 minutes)
// to 12 hours.
@ -208,8 +210,10 @@ const (
JwksURL = "jwks_url"
ConfigURL = "config_url"
ClaimPrefix = "claim_prefix"
ClientID = "client_id"
EnvIdentityOpenIDState = "MINIO_IDENTITY_OPENID_STATE"
EnvIdentityOpenIDClientID = "MINIO_IDENTITY_OPENID_CLIENT_ID"
EnvIdentityOpenIDJWKSURL = "MINIO_IDENTITY_OPENID_JWKS_URL"
EnvIdentityOpenIDURL = "MINIO_IDENTITY_OPENID_CONFIG_URL"
EnvIdentityOpenIDClaimPrefix = "MINIO_IDENTITY_OPENID_CLAIM_PREFIX"
@ -268,6 +272,10 @@ var (
Key: ConfigURL,
Value: "",
},
config.KV{
Key: ClientID,
Value: "",
},
config.KV{
Key: ClaimPrefix,
Value: "",
@ -301,6 +309,7 @@ func LookupConfig(kvs config.KVS, transport *http.Transport, closeRespFn func(io
c = Config{
ClaimPrefix: env.Get(EnvIdentityOpenIDClaimPrefix, kvs.Get(ClaimPrefix)),
publicKeys: make(map[string]crypto.PublicKey),
ClientID: env.Get(EnvIdentityOpenIDClientID, kvs.Get(ClientID)),
transport: transport,
closeRespFn: closeRespFn,
}

@ -1909,12 +1909,14 @@ func presignedGet(host, bucket, object string, expiry int64, creds auth.Credenti
type DiscoveryDocResp struct {
DiscoveryDoc openid.DiscoveryDoc
UIVersion string `json:"uiVersion"`
ClientID string `json:"clientId"`
}
// GetDiscoveryDoc - returns parsed value of OpenID discovery document
func (web *webAPIHandlers) GetDiscoveryDoc(r *http.Request, args *WebGenericArgs, reply *DiscoveryDocResp) error {
if globalOpenIDConfig.DiscoveryDoc.AuthEndpoint != "" {
reply.DiscoveryDoc = globalOpenIDConfig.DiscoveryDoc
reply.ClientID = globalOpenIDConfig.ClientID
}
reply.UIVersion = browser.UIVersion
return nil

@ -57,7 +57,7 @@ To update the configuration, use `mc admin config get notify_amqp` command to ge
```sh
$ mc admin config get myminio/ notify_amqp
notify_amqp:1 delivery_mode="0" exchange_type="" no_wait="off" queue_dir="" queue_limit="0" state="off" url="" auto_deleted="off" durable="off" exchange="" internal="off" mandatory="off" routing_key=""
notify_amqp:1 delivery_mode="0" exchange_type="" no_wait="off" queue_dir="" queue_limit="0" url="" auto_deleted="off" durable="off" exchange="" internal="off" mandatory="off" routing_key=""
```
Use `mc admin config set` command to update the configuration for the deployment.Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:amqp` at start-up if there were no errors.
@ -65,7 +65,7 @@ Use `mc admin config set` command to update the configuration for the deployment
An example configuration for RabbitMQ is shown below:
```sh
$ mc admin config set myminio/ notify_amqp:1 exchange="bucketevents" exchange_type="fanout" mandatory="false" no_wait="false" state="on" url="amqp://myuser:mypassword@localhost:5672" auto_deleted="false" delivery_mode="0" durable="false" internal="false" routing_key="bucketlogs"
$ mc admin config set myminio/ notify_amqp:1 exchange="bucketevents" exchange_type="fanout" mandatory="false" no_wait="false" url="amqp://myuser:mypassword@localhost:5672" auto_deleted="false" delivery_mode="0" durable="false" internal="false" routing_key="bucketlogs"
```
MinIO supports all the exchanges available in [RabbitMQ](https://www.rabbitmq.com/). For this setup, we are using `fanout` exchange.
@ -163,13 +163,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_mqtt
notify_mqtt:1 broker="" password="" queue_dir="" queue_limit="0" reconnect_interval="0s" state="off" keep_alive_interval="0s" qos="0" topic="" username=""
notify_mqtt:1 broker="" password="" queue_dir="" queue_limit="0" reconnect_interval="0s" keep_alive_interval="0s" qos="0" topic="" username=""
```
Use `mc admin config set` command to update the configuration for the deployment. Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:mqtt` at start-up if there were no errors.
```sh
$ mc admin config set myminio notify_mqtt:1 broker="tcp://localhost:1883" password="" queue_dir="" queue_limit="0" reconnect_interval="0s" state="on" keep_alive_interval="0s" qos="1" topic="minio" username=""
$ mc admin config set myminio notify_mqtt:1 broker="tcp://localhost:1883" password="" queue_dir="" queue_limit="0" reconnect_interval="0s" keep_alive_interval="0s" qos="1" topic="minio" username=""
```
MinIO supports any MQTT server that supports MQTT 3.1 or 3.1.1 and can connect to them over TCP, TLS, or a Websocket connection using `tcp://`, `tls://`, or `ws://` respectively as the scheme for the broker url. See the [Go Client](http://www.eclipse.org/paho/clients/golang/) documentation for more information.
@ -276,13 +276,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_elasticsearch
notify_elasticsearch:1 queue_limit="0" state="off" url="" format="namespace" index="" queue_dir=""
notify_elasticsearch:1 queue_limit="0" url="" format="namespace" index="" queue_dir=""
```
Use `mc admin config set` command to update the configuration for the deployment. Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:elasticsearch` at start-up if there were no errors.
```sh
$ mc admin config set myminio notify_elasticsearch:1 queue_limit="0" state="on" url="http://127.0.0.1:9200" format="namespace" index="minio_events" queue_dir=""
$ mc admin config set myminio notify_elasticsearch:1 queue_limit="0" url="http://127.0.0.1:9200" format="namespace" index="minio_events" queue_dir=""
```
Note that, you can add as many Elasticsearch server endpoint configurations as needed by providing an identifier (like "1" in the example above) for the Elasticsearch instance and an object of per-server configuration parameters.
@ -417,13 +417,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_redis
notify_redis:1 address="" format="namespace" key="" password="" queue_dir="" queue_limit="0" state="off"
notify_redis:1 address="" format="namespace" key="" password="" queue_dir="" queue_limit="0"
```
Use `mc admin config set` command to update the configuration for the deployment.Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:redis` at start-up if there were no errors.
```sh
$ mc admin config set myminio/ notify_redis:1 address="127.0.0.1:6379" format="namespace" key="bucketevents" password="yoursecret" queue_dir="" queue_limit="0" state="on"
$ mc admin config set myminio/ notify_redis:1 address="127.0.0.1:6379" format="namespace" key="bucketevents" password="yoursecret" queue_dir="" queue_limit="0"
```
Note that, you can add as many Redis server endpoint configurations as needed by providing an identifier (like "1" in the example above) for the Redis instance and an object of per-server configuration parameters.
@ -486,13 +486,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_nats
notify_nats:1 password="yoursecret" streaming_max_pub_acks_in_flight="10" subject="" address="0.0.0.0:4222" state="on" token="" username="yourusername" ping_interval="0" queue_limit="0" secure="off" streaming_async="on" queue_dir="" streaming_cluster_id="test-cluster" streaming_enable="on"
notify_nats:1 password="yoursecret" streaming_max_pub_acks_in_flight="10" subject="" address="0.0.0.0:4222" token="" username="yourusername" ping_interval="0" queue_limit="0" secure="off" streaming_async="on" queue_dir="" streaming_cluster_id="test-cluster" streaming_enable="on"
```
Use `mc admin config set` command to update the configuration for the deployment.Restart MinIO server to reflect config changes. `bucketevents` is the subject used by NATS in this example.
```sh
$ mc admin config set myminio notify_nats:1 password="yoursecret" streaming_max_pub_acks_in_flight="10" subject="" address="0.0.0.0:4222" state="on" token="" username="yourusername" ping_interval="0" queue_limit="0" secure="off" streaming_async="on" queue_dir="" streaming_cluster_id="test-cluster" streaming_enable="on"
$ mc admin config set myminio notify_nats:1 password="yoursecret" streaming_max_pub_acks_in_flight="10" subject="" address="0.0.0.0:4222" token="" username="yourusername" ping_interval="0" queue_limit="0" secure="off" streaming_async="on" queue_dir="" streaming_cluster_id="test-cluster" streaming_enable="on"
```
MinIO server also supports [NATS Streaming mode](http://nats.io/documentation/streaming/nats-streaming-intro/) that offers additional functionality like `At-least-once-delivery`, and `Publisher rate limiting`. To configure MinIO server to send notifications to NATS Streaming server, update the MinIO server configuration file as follows:
@ -682,13 +682,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio notify_postgres
notify_postgres:1 password="" port="" queue_dir="" connection_string="" host="" queue_limit="0" state="off" table="" username="" database="" format="namespace"
notify_postgres:1 password="" port="" queue_dir="" connection_string="" host="" queue_limit="0" table="" username="" database="" format="namespace"
```
Use `mc admin config set` command to update the configuration for the deployment. Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:postgresql` at start-up if there were no errors.
```sh
$ mc admin config set myminio notify_postgres:1 password="password" port="5432" queue_dir="" connection_string="sslmode=disable" host="127.0.0.1" queue_limit="0" state="on" table="bucketevents" username="postgres" database="minio_events" format="namespace"
$ mc admin config set myminio notify_postgres:1 password="password" port="5432" queue_dir="" connection_string="sslmode=disable" host="127.0.0.1" queue_limit="0" table="bucketevents" username="postgres" database="minio_events" format="namespace"
```
Note that, you can add as many PostgreSQL server endpoint configurations as needed by providing an identifier (like "1" in the example above) for the PostgreSQL instance and an object of per-server configuration parameters.
@ -771,13 +771,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_mysql
notify_mysql:1 table="" database="" format="namespace" password="" port="" queue_dir="" queue_limit="0" state="off" username="" dsn_string="" host=""
notify_mysql:1 table="" database="" format="namespace" password="" port="" queue_dir="" queue_limit="0" username="" dsn_string="" host=""
```
Use `mc admin config set` command to update the configuration for the deployment. Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:mysql` at start-up if there were no errors.
```sh
$ mc admin config set myminio notify_mysql:1 table="minio_images" database="miniodb" format="namespace" password="" port="3306" queue_dir="" queue_limit="0" state="on" username="root" dsn_string="" host="172.17.0.1"
$ mc admin config set myminio notify_mysql:1 table="minio_images" database="miniodb" format="namespace" password="" port="3306" queue_dir="" queue_limit="0" username="root" dsn_string="" host="172.17.0.1"
```
Note that, you can add as many MySQL server endpoint configurations as needed by providing an identifier (like "1" in the example above) for the MySQL instance and an object of per-server configuration parameters.
@ -840,13 +840,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_kafka
notify_kafka:1 tls_skip_verify="off" state="off" queue_dir="" queue_limit="0" sasl_enable="off" sasl_password="" sasl_username="" tls_client_auth="0" tls_enable="off" brokers="" topic=""
notify_kafka:1 tls_skip_verify="off" queue_dir="" queue_limit="0" sasl_enable="off" sasl_password="" sasl_username="" tls_client_auth="0" tls_enable="off" brokers="" topic=""
```
Use `mc admin config set` command to update the configuration for the deployment. Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:kafka` at start-up if there were no errors.`bucketevents` is the topic used by kafka in this example.
```sh
$ mc admin config set myminio notify_kafka:1 tls_skip_verify="off" state="on" queue_dir="" queue_limit="0" sasl_enable="off" sasl_password="" sasl_username="" tls_client_auth="0" tls_enable="off" brokers="localhost:9092,localhost:9093" topic="bucketevents"
$ mc admin config set myminio notify_kafka:1 tls_skip_verify="off" queue_dir="" queue_limit="0" sasl_enable="off" sasl_password="" sasl_username="" tls_client_auth="0" tls_enable="off" brokers="localhost:9092,localhost:9093" topic="bucketevents"
```
### Step 3: Enable bucket notification using MinIO client
@ -947,13 +947,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_webhook
notify_webhook:1 queue_limit="0" state="off" endpoint="" queue_dir=""
notify_webhook:1 queue_limit="0" endpoint="" queue_dir=""
```
Use `mc admin config set` command to update the configuration for the deployment. Here the endpoint is the server listening for webhook notifications. Save the settings and restart the MinIO server for changes to take effect. Note that the endpoint needs to be live and reachable when you restart your MinIO server.
```sh
$ mc admin config set myminio notify_webhook:1 queue_limit="0" state="on" endpoint="http://localhost:3000" queue_dir=""
$ mc admin config set myminio notify_webhook:1 queue_limit="0" endpoint="http://localhost:3000" queue_dir=""
```
### Step 2: Enable bucket notification using MinIO client
@ -1026,13 +1026,13 @@ To update the configuration, use `mc admin config get` command to get the curren
```sh
$ mc admin config get myminio/ notify_nsq
notify_nsq:1 nsqd_address="" queue_dir="" queue_limit="0" state="off" tls_enable="off" tls_skip_verify="off" topic=""
notify_nsq:1 nsqd_address="" queue_dir="" queue_limit="0" tls_enable="off" tls_skip_verify="off" topic=""
```
Use `mc admin config set` command to update the configuration for the deployment. Restart the MinIO server to put the changes into effect. The server will print a line like `SQS ARNs: arn:minio:sqs::1:nsq` at start-up if there were no errors.
```sh
$ mc admin config set myminio notify_nsq:1 nsqd_address="127.0.0.1:4150" queue_dir="" queue_limit="0" state="on" tls_enable="off" tls_skip_verify="on" topic="minio"
$ mc admin config set myminio notify_nsq:1 nsqd_address="127.0.0.1:4150" queue_dir="" queue_limit="0" tls_enable="off" tls_skip_verify="on" topic="minio"
```
Note that, you can add as many NSQ daemon endpoint configurations as needed by providing an identifier (like "1" in the example above) for the NSQ instance and an object of per-server configuration parameters.

@ -18,17 +18,17 @@ Compression can be enabled by updating the `compress` config settings for MinIO
```
$ mc admin config get myminio compression
compression extensions=".txt,.log,.csv,.json,.tar,.xml,.bin" mime_types="text/*,application/json,application/xml" state="off"```
compression extensions=".txt,.log,.csv,.json,.tar,.xml,.bin" mime_types="text/*,application/json,application/xml"```
Default config includes most common highly compressible content extensions and mime-types.
```
$ mc admin config set myminio compression extensions=".pdf" mime_types="application/pdf" state="on"
$ mc admin config set myminio compression extensions=".pdf" mime_types="application/pdf"
```
To enable compression for all content, except for types listed below:
To enable compression for all content, with default extensions and mime-types.
```
~ mc admin config set myminio compression extensions="" mime_types="" state="on"
~ mc admin config set myminio compression
```
The compression settings may also be set through environment variables. When set, environment variables override the defined `compress` config settings in the server config.

@ -16,11 +16,11 @@ HTTP target logs to a generic HTTP endpoint in JSON format and is not enabled by
Assuming `mc` is already [configured](https://docs.min.io/docs/minio-client-quickstart-guide.html)
```
mc admin config get myminio/ logger_webhook
logger_webhook:target1 auth_token="" endpoint="" state="off"
logger_webhook:target1 auth_token="" endpoint=""
```
```
mc admin config set myminio logger_webhook:target1 auth_token="" endpoint="http://endpoint:port/path" state="on"
mc admin config set myminio logger_webhook:target1 auth_token="" endpoint="http://endpoint:port/path"
mc admin service restart myminio
```
@ -38,11 +38,11 @@ minio server /mnt/data
Assuming `mc` is already [configured](https://docs.min.io/docs/minio-client-quickstart-guide.html)
```
mc admin config get myminio/ audit_webhook
audit_webhook:target1 auth_token="" endpoint="" state="off"
audit_webhook:target1 auth_token="" endpoint=""
```
```
mc admin config set myminio audit_webhook:target1 auth_token="" endpoint="http://endpoint:port/path" state="on"
mc admin config set myminio audit_webhook:target1 auth_token="" endpoint="http://endpoint:port/path"
mc admin service restart myminio
```

@ -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
```

@ -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
$ 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 policy_opa
policy_opa auth_token="" state="on" url="http://localhost:8181/v1/data/httpapi/authz"
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"
mc admin config get myminio policy_opa
policy_opa url="http://localhost:8181/v1/data/httpapi/authz" auth_token=
```
Testing with an example

@ -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/
```

@ -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)

@ -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`
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/`
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
- Set openid configuration and restart MinIO
```
mc admin config set myminio identity_openid config_url="<CONFIG_URL>" client_id="<client_identifier>"
```
```
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

@ -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
```

Loading…
Cancel
Save