This PR adds the following features:
- Allow user to provide its own keypair certificates for enable TLS in
MinIO
- Allow user to configure data encryption at rest in MinIO with KES
- Removes JWT schema for login and instead Console authentication will use
encrypted session tokens
Enable TLS between client and MinIO with user provided certificates
Instead of using AutoCert feature now the user can provide `cert` and
`key` via `tls` object, values must be valid `x509.Certificate`
formatted files encoded in `base64`
Enable encryption at rest configuring KES
User can deploy KES via Console/Operator by defining the encryption
object, AutoCert must be enabled or custom certificates for KES must be
provided, KES support 3 KMS backends: `Vault`, `AWS KMS` and `Gemalto`,
previous configuration of the KMS is necessary.
eg of body request for create-tenant
```
{
"name": "honeywell",
"access_key": "minio",
"secret_key": "minio123",
"enable_mcs": false,
"enable_ssl": false,
"service_name": "honeywell",
"zones": [
{
"name": "honeywell-zone-1",
"servers": 1,
"volumes_per_server": 4,
"volume_configuration": {
"size": 256000000,
"storage_class": "vsan-default-storage-policy"
}
}
],
"namespace": "default",
"tls": {
"tls.crt": "",
"tls.key": ""
},
"encryption": {
"server": {
"tls.crt": "",
"tls.key": ""
},
"client": {
"tls.crt": "",
"tls.key": ""
},
"vault": {
"endpoint": "http://vault:8200",
"prefix": "",
"approle": {
"id": "",
"secret": ""
}
}
}
}
```
169 lines
5.6 KiB
Go
169 lines
5.6 KiB
Go
// This file is part of MinIO Kubernetes Cloud
|
|
// Copyright (c) 2019 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 cluster
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"net"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/minio/minio/pkg/env"
|
|
)
|
|
|
|
var (
|
|
errCantDetermineMinIOImage = errors.New("can't determine MinIO Image")
|
|
errCantDetermineMCImage = errors.New("can't determine MC Image")
|
|
)
|
|
|
|
func GetK8sAPIServer() string {
|
|
// if console is running inside a k8s pod KUBERNETES_SERVICE_HOST and KUBERNETES_SERVICE_PORT will contain the k8s api server apiServerAddress
|
|
// if console is not running inside k8s by default will look for the k8s api server on localhost:8001 (kubectl proxy)
|
|
// NOTE: using kubectl proxy is for local development only, since every request send to localhost:8001 will bypass service account authentication
|
|
// more info here: https://kubernetes.io/docs/tasks/access-application-cluster/access-cluster/#directly-accessing-the-rest-api
|
|
// you can override this using CONSOLE_K8S_API_SERVER, ie use the k8s cluster from `kubectl config view`
|
|
host, port := env.Get("KUBERNETES_SERVICE_HOST", ""), env.Get("KUBERNETES_SERVICE_PORT", "")
|
|
apiServerAddress := "http://localhost:8001"
|
|
if host != "" && port != "" {
|
|
apiServerAddress = "https://" + net.JoinHostPort(host, port)
|
|
}
|
|
return env.Get(ConsoleK8sAPIServer, apiServerAddress)
|
|
}
|
|
|
|
// If CONSOLE_K8S_API_SERVER_TLS_ROOT_CA is true console will load the certificate into the
|
|
// http.client rootCAs pool, this is useful for testing an k8s ApiServer or when working with self-signed certificates
|
|
func getK8sAPIServerTLSRootCA() string {
|
|
return strings.TrimSpace(env.Get(ConsoleK8SAPIServerTLSRootCA, ""))
|
|
}
|
|
|
|
// GetNsFromFile assumes console is running inside a k8s pod and extract the current namespace from the
|
|
// /var/run/secrets/kubernetes.io/serviceaccount/namespace file
|
|
func GetNsFromFile() string {
|
|
dat, err := ioutil.ReadFile("/var/run/secrets/kubernetes.io/serviceaccount/namespace")
|
|
if err != nil {
|
|
return "default"
|
|
}
|
|
return string(dat)
|
|
}
|
|
|
|
// This operation will run only once at console startup
|
|
var namespace = GetNsFromFile()
|
|
|
|
// Returns the namespace in which the controller is installed
|
|
func GetNs() string {
|
|
return env.Get(ConsoleNamespace, namespace)
|
|
}
|
|
|
|
// getLatestMinIOImage returns the latest docker image for MinIO if found on the internet
|
|
func getLatestMinIOImage(client HTTPClientI) (*string, error) {
|
|
resp, err := client.Get("https://dl.min.io/server/minio/release/linux-amd64/")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var re = regexp.MustCompile(`(?m)\.\/minio\.(RELEASE.*?Z)"`)
|
|
// look for a single match
|
|
matches := re.FindAllStringSubmatch(string(body), 1)
|
|
for i := range matches {
|
|
release := matches[i][1]
|
|
dockerImage := fmt.Sprintf("minio/minio:%s", release)
|
|
return &dockerImage, nil
|
|
}
|
|
return nil, errCantDetermineMinIOImage
|
|
}
|
|
|
|
var latestMinIOImage, errLatestMinIOImage = getLatestMinIOImage(
|
|
&HTTPClient{
|
|
Client: &http.Client{
|
|
Timeout: 15 * time.Second,
|
|
},
|
|
})
|
|
|
|
// GetMinioImage returns the image URL to be used when deploying a MinIO instance, if there is
|
|
// a preferred image to be used (configured via ENVIRONMENT VARIABLES) GetMinioImage will return that
|
|
// if not, GetMinioImage will try to obtain the image URL for the latest version of MinIO and return that
|
|
func GetMinioImage() (*string, error) {
|
|
image := strings.TrimSpace(env.Get(ConsoleMinioImage, ""))
|
|
// if there is a preferred image configured by the user we'll always return that
|
|
if image != "" {
|
|
return &image, nil
|
|
}
|
|
if errLatestMinIOImage != nil {
|
|
return nil, errLatestMinIOImage
|
|
}
|
|
return latestMinIOImage, nil
|
|
}
|
|
|
|
// GetLatestMinioImage returns the latest image URL on minio repository
|
|
func GetLatestMinioImage(client HTTPClientI) (*string, error) {
|
|
latestMinIOImage, err := getLatestMinIOImage(client)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return latestMinIOImage, nil
|
|
}
|
|
|
|
// getLatestMCImage returns the latest docker image for MC if found on the internet
|
|
func getLatestMCImage() (*string, error) {
|
|
// Create an http client with a 4 second timeout
|
|
client := http.Client{
|
|
Timeout: 4 * time.Second,
|
|
}
|
|
resp, err := client.Get("https://dl.min.io/client/mc/release/linux-amd64/")
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer resp.Body.Close()
|
|
|
|
body, err := ioutil.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
var re = regexp.MustCompile(`(?m)\.\/mc\.(RELEASE.*?Z)"`)
|
|
// look for a single match
|
|
matches := re.FindAllStringSubmatch(string(body), 1)
|
|
for i := range matches {
|
|
release := matches[i][1]
|
|
dockerImage := fmt.Sprintf("minio/mc:%s", release)
|
|
return &dockerImage, nil
|
|
}
|
|
return nil, errCantDetermineMCImage
|
|
}
|
|
|
|
var latestMCImage, errLatestMCImage = getLatestMCImage()
|
|
|
|
func GetMCImage() (*string, error) {
|
|
image := strings.TrimSpace(env.Get(ConsoleMCImage, ""))
|
|
// if there is a preferred image configured by the user we'll always return that
|
|
if image != "" {
|
|
return &image, nil
|
|
}
|
|
if errLatestMCImage != nil {
|
|
return nil, errLatestMCImage
|
|
}
|
|
return latestMCImage, nil
|
|
}
|