Support for deploying minio/console with IDP integration (#221)

This commit is contained in:
Lenin Alevski
2020-08-02 23:45:54 -07:00
committed by GitHub
parent cd547e9425
commit c3e34dc220
6 changed files with 619 additions and 1 deletions

View File

@@ -51,6 +51,9 @@ type CreateTenantRequest struct {
// encryption
Encryption *EncryptionConfiguration `json:"encryption,omitempty"`
// idp
Idp *IdpConfiguration `json:"idp,omitempty"`
// image
Image string `json:"image,omitempty"`
@@ -88,6 +91,10 @@ func (m *CreateTenantRequest) Validate(formats strfmt.Registry) error {
res = append(res, err)
}
if err := m.validateIdp(formats); err != nil {
res = append(res, err)
}
if err := m.validateName(formats); err != nil {
res = append(res, err)
}
@@ -128,6 +135,24 @@ func (m *CreateTenantRequest) validateEncryption(formats strfmt.Registry) error
return nil
}
func (m *CreateTenantRequest) validateIdp(formats strfmt.Registry) error {
if swag.IsZero(m.Idp) { // not required
return nil
}
if m.Idp != nil {
if err := m.Idp.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("idp")
}
return err
}
}
return nil
}
func (m *CreateTenantRequest) validateName(formats strfmt.Registry) error {
if err := validate.Required("name", "body", m.Name); err != nil {

299
models/idp_configuration.go Normal file
View File

@@ -0,0 +1,299 @@
// Code generated by go-swagger; DO NOT EDIT.
// This file is part of MinIO Console Server
// Copyright (c) 2020 MinIO, Inc.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
package models
// This file was generated by the swagger tool.
// Editing this file might prove futile when you re-run the swagger generate command
import (
"github.com/go-openapi/errors"
"github.com/go-openapi/strfmt"
"github.com/go-openapi/swag"
"github.com/go-openapi/validate"
)
// IdpConfiguration idp configuration
//
// swagger:model idpConfiguration
type IdpConfiguration struct {
// active directory
ActiveDirectory *IdpConfigurationActiveDirectory `json:"active_directory,omitempty"`
// oidc
Oidc *IdpConfigurationOidc `json:"oidc,omitempty"`
}
// Validate validates this idp configuration
func (m *IdpConfiguration) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateActiveDirectory(formats); err != nil {
res = append(res, err)
}
if err := m.validateOidc(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IdpConfiguration) validateActiveDirectory(formats strfmt.Registry) error {
if swag.IsZero(m.ActiveDirectory) { // not required
return nil
}
if m.ActiveDirectory != nil {
if err := m.ActiveDirectory.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("active_directory")
}
return err
}
}
return nil
}
func (m *IdpConfiguration) validateOidc(formats strfmt.Registry) error {
if swag.IsZero(m.Oidc) { // not required
return nil
}
if m.Oidc != nil {
if err := m.Oidc.Validate(formats); err != nil {
if ve, ok := err.(*errors.Validation); ok {
return ve.ValidateName("oidc")
}
return err
}
}
return nil
}
// MarshalBinary interface implementation
func (m *IdpConfiguration) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IdpConfiguration) UnmarshalBinary(b []byte) error {
var res IdpConfiguration
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IdpConfigurationActiveDirectory idp configuration active directory
//
// swagger:model IdpConfigurationActiveDirectory
type IdpConfigurationActiveDirectory struct {
// group name attribute
GroupNameAttribute string `json:"group_name_attribute,omitempty"`
// group search base dn
GroupSearchBaseDn string `json:"group_search_base_dn,omitempty"`
// group search filter
GroupSearchFilter string `json:"group_search_filter,omitempty"`
// server insecure
ServerInsecure bool `json:"server_insecure,omitempty"`
// skip ssl verification
SkipSslVerification bool `json:"skip_ssl_verification,omitempty"`
// url
// Required: true
URL *string `json:"url"`
// user search filter
// Required: true
UserSearchFilter *string `json:"user_search_filter"`
// username format
// Required: true
UsernameFormat *string `json:"username_format"`
}
// Validate validates this idp configuration active directory
func (m *IdpConfigurationActiveDirectory) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateURL(formats); err != nil {
res = append(res, err)
}
if err := m.validateUserSearchFilter(formats); err != nil {
res = append(res, err)
}
if err := m.validateUsernameFormat(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IdpConfigurationActiveDirectory) validateURL(formats strfmt.Registry) error {
if err := validate.Required("active_directory"+"."+"url", "body", m.URL); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationActiveDirectory) validateUserSearchFilter(formats strfmt.Registry) error {
if err := validate.Required("active_directory"+"."+"user_search_filter", "body", m.UserSearchFilter); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationActiveDirectory) validateUsernameFormat(formats strfmt.Registry) error {
if err := validate.Required("active_directory"+"."+"username_format", "body", m.UsernameFormat); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *IdpConfigurationActiveDirectory) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IdpConfigurationActiveDirectory) UnmarshalBinary(b []byte) error {
var res IdpConfigurationActiveDirectory
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}
// IdpConfigurationOidc idp configuration oidc
//
// swagger:model IdpConfigurationOidc
type IdpConfigurationOidc struct {
// client id
// Required: true
ClientID *string `json:"client_id"`
// secret id
// Required: true
SecretID *string `json:"secret_id"`
// url
// Required: true
URL *string `json:"url"`
}
// Validate validates this idp configuration oidc
func (m *IdpConfigurationOidc) Validate(formats strfmt.Registry) error {
var res []error
if err := m.validateClientID(formats); err != nil {
res = append(res, err)
}
if err := m.validateSecretID(formats); err != nil {
res = append(res, err)
}
if err := m.validateURL(formats); err != nil {
res = append(res, err)
}
if len(res) > 0 {
return errors.CompositeValidationError(res...)
}
return nil
}
func (m *IdpConfigurationOidc) validateClientID(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"client_id", "body", m.ClientID); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationOidc) validateSecretID(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"secret_id", "body", m.SecretID); err != nil {
return err
}
return nil
}
func (m *IdpConfigurationOidc) validateURL(formats strfmt.Registry) error {
if err := validate.Required("oidc"+"."+"url", "body", m.URL); err != nil {
return err
}
return nil
}
// MarshalBinary interface implementation
func (m *IdpConfigurationOidc) MarshalBinary() ([]byte, error) {
if m == nil {
return nil, nil
}
return swag.WriteJSON(m)
}
// UnmarshalBinary interface implementation
func (m *IdpConfigurationOidc) UnmarshalBinary(b []byte) error {
var res IdpConfigurationOidc
if err := swag.ReadJSON(b, &res); err != nil {
return err
}
*m = res
return nil
}

View File

@@ -250,7 +250,7 @@ const Login = ({ classes, userLoggedIn }: ILoginProps) => {
</Typography>
<Button
component={"a"}
href={loginStrategy.redirect}
href={loginStrategy.redirect.replace("%5BHOSTNAME%5D", window.location.hostname)}
type="submit"
fullWidth
variant="contained"

View File

@@ -395,6 +395,57 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
Env: []corev1.EnvVar{},
},
}
idpEnabled := false
// Enable IDP (Active Directory) for MinIO
if params.Body.Idp != nil && params.Body.Idp.ActiveDirectory != nil {
url := *params.Body.Idp.ActiveDirectory.URL
userNameFormat := *params.Body.Idp.ActiveDirectory.UsernameFormat
userSearchFilter := *params.Body.Idp.ActiveDirectory.UserSearchFilter
tlsSkipVerify := params.Body.Idp.ActiveDirectory.SkipSslVerification
serverInsecure := params.Body.Idp.ActiveDirectory.ServerInsecure
groupSearchDN := params.Body.Idp.ActiveDirectory.GroupSearchBaseDn
groupSearchFilter := params.Body.Idp.ActiveDirectory.GroupSearchFilter
groupNameAttribute := params.Body.Idp.ActiveDirectory.GroupNameAttribute
if url != "" && userNameFormat != "" && userSearchFilter != "" {
// CONSOLE_LDAP_ENABLED
idpEnabled = true
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_SERVER_ADDR",
Value: userNameFormat,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_USERNAME_FORMAT",
Value: userNameFormat,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER",
Value: userSearchFilter,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_USERNAME_SEARCH_FILTER",
Value: userSearchFilter,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_GROUP_SEARCH_BASE_DN",
Value: groupSearchDN,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_GROUP_SEARCH_FILTER",
Value: groupSearchFilter,
}, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_GROUP_NAME_ATTRIBUTE",
Value: groupNameAttribute,
})
if tlsSkipVerify {
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_TLS_SKIP_VERIFY",
Value: "on",
})
}
if serverInsecure {
minInst.Spec.Env = append(minInst.Spec.Env, corev1.EnvVar{
Name: "MINIO_IDENTITY_LDAP_SERVER_INSECURE",
Value: "on",
})
}
}
}
// operator request AutoCert feature
encryption := false
@@ -468,6 +519,7 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
minInst.Spec.KES.Image = params.Body.Encryption.Image
}
// Secret to store KES server TLS certificates
// TODO check if AutoCert it's already configured
serverTLSCrt, err := base64.StdEncoding.DecodeString(*params.Body.Encryption.Server.Crt)
if err != nil {
return nil, err
@@ -689,6 +741,28 @@ func getTenantCreatedResponse(session *models.Principal, params admin_api.Create
"CONSOLE_SECRET_KEY": []byte(consoleSecret),
},
}
// Enable IDP (Open ID Connect) for console
if !idpEnabled && params.Body.Idp != nil && params.Body.Idp.Oidc != nil {
url := *params.Body.Idp.Oidc.URL
clientID := *params.Body.Idp.Oidc.ClientID
secretID := *params.Body.Idp.Oidc.SecretID
if url != "" && clientID != "" && secretID != "" {
instanceSecret.Data["CONSOLE_IDP_URL"] = []byte(url)
instanceSecret.Data["CONSOLE_IDP_CLIENT_ID"] = []byte(clientID)
instanceSecret.Data["CONSOLE_IDP_SECRET"] = []byte(secretID)
consoleScheme := "http"
consolePort := 9090
if minInst.Spec.RequestAutoCert {
consoleScheme = "https"
consolePort = 9443
}
// https://[HOSTNAME]:9443 will be replaced by javascript in the browser to use the actual hostname
// assigned to Console, eg: https://localhost:9443
instanceSecret.Data["CONSOLE_IDP_CALLBACK"] = []byte(fmt.Sprintf("%s://[HOSTNAME]:%d/oauth_callback", consoleScheme, consolePort))
}
}
_, err = clientset.CoreV1().Secrets(ns).Create(context.Background(), &instanceSecret, metav1.CreateOptions{})
if err != nil {
return nil, err

View File

@@ -2036,6 +2036,10 @@ func init() {
"type": "object",
"$ref": "#/definitions/encryptionConfiguration"
},
"idp": {
"type": "object",
"$ref": "#/definitions/idpConfiguration"
},
"image": {
"type": "string"
},
@@ -2227,6 +2231,64 @@ func init() {
}
}
},
"idpConfiguration": {
"type": "object",
"properties": {
"active_directory": {
"type": "object",
"required": [
"url",
"username_format",
"user_search_filter"
],
"properties": {
"group_name_attribute": {
"type": "string"
},
"group_search_base_dn": {
"type": "string"
},
"group_search_filter": {
"type": "string"
},
"server_insecure": {
"type": "boolean"
},
"skip_ssl_verification": {
"type": "boolean"
},
"url": {
"type": "string"
},
"user_search_filter": {
"type": "string"
},
"username_format": {
"type": "string"
}
}
},
"oidc": {
"type": "object",
"required": [
"url",
"client_id",
"secret_id"
],
"properties": {
"client_id": {
"type": "string"
},
"secret_id": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
}
},
"listBucketEventsResponse": {
"type": "object",
"properties": {
@@ -5244,6 +5306,59 @@ func init() {
}
}
},
"IdpConfigurationActiveDirectory": {
"type": "object",
"required": [
"url",
"username_format",
"user_search_filter"
],
"properties": {
"group_name_attribute": {
"type": "string"
},
"group_search_base_dn": {
"type": "string"
},
"group_search_filter": {
"type": "string"
},
"server_insecure": {
"type": "boolean"
},
"skip_ssl_verification": {
"type": "boolean"
},
"url": {
"type": "string"
},
"user_search_filter": {
"type": "string"
},
"username_format": {
"type": "string"
}
}
},
"IdpConfigurationOidc": {
"type": "object",
"required": [
"url",
"client_id",
"secret_id"
],
"properties": {
"client_id": {
"type": "string"
},
"secret_id": {
"type": "string"
},
"url": {
"type": "string"
}
}
},
"NodeSelectorTermMatchExpressionsItems0": {
"description": "A node selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
"type": "object",
@@ -5808,6 +5923,10 @@ func init() {
"type": "object",
"$ref": "#/definitions/encryptionConfiguration"
},
"idp": {
"type": "object",
"$ref": "#/definitions/idpConfiguration"
},
"image": {
"type": "string"
},
@@ -5999,6 +6118,64 @@ func init() {
}
}
},
"idpConfiguration": {
"type": "object",
"properties": {
"active_directory": {
"type": "object",
"required": [
"url",
"username_format",
"user_search_filter"
],
"properties": {
"group_name_attribute": {
"type": "string"
},
"group_search_base_dn": {
"type": "string"
},
"group_search_filter": {
"type": "string"
},
"server_insecure": {
"type": "boolean"
},
"skip_ssl_verification": {
"type": "boolean"
},
"url": {
"type": "string"
},
"user_search_filter": {
"type": "string"
},
"username_format": {
"type": "string"
}
}
},
"oidc": {
"type": "object",
"required": [
"url",
"client_id",
"secret_id"
],
"properties": {
"client_id": {
"type": "string"
},
"secret_id": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
}
},
"listBucketEventsResponse": {
"type": "object",
"properties": {

View File

@@ -1810,6 +1810,9 @@ definitions:
type: object
additionalProperties:
type: string
idp:
type: object
$ref: "#/definitions/idpConfiguration"
tls:
type: object
$ref: "#/definitions/tlsConfiguration"
@@ -1828,6 +1831,46 @@ definitions:
key:
type: string
idpConfiguration:
type: object
properties:
oidc:
type: object
required:
- url
- client_id
- secret_id
properties:
url:
type: string
client_id:
type: string
secret_id:
type: string
active_directory:
type: object
required:
- url
- username_format
- user_search_filter
properties:
url:
type: string
username_format:
type: string
user_search_filter:
type: string
group_search_base_dn:
type: string
group_search_filter:
type: string
group_name_attribute:
type: string
skip_ssl_verification:
type: boolean
server_insecure:
type: boolean
encryptionConfiguration:
type: object
properties: