mirror of
https://github.com/minio/minio.git
synced 2025-11-25 03:56:17 -05:00
kms: replace KES client implementation with minio/kes (#12207)
This commit replaces the custom KES client implementation with the KES SDK from https://github.com/minio/kes The SDK supports multi-server client load-balancing and requests retry out of the box. Therefore, this change reduces the overall complexity within the MinIO server and there is no need to maintain two separate client implementations. Signed-off-by: Andreas Auernhammer <aead@mail.de>
This commit is contained in:
committed by
GitHub
parent
1692bab609
commit
d8eb7d3e15
@@ -19,14 +19,25 @@ package certs
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// GetRootCAs - returns all the root CAs into certPool
|
||||
// at the input certsCADir
|
||||
func GetRootCAs(certsCAsDir string) (*x509.CertPool, error) {
|
||||
// GetRootCAs loads all X.509 certificates at the given path and adds them
|
||||
// to the list of system root CAs, if available. The returned CA pool
|
||||
// is a conjunction of the system root CAs and the certificate(s) at
|
||||
// the given path.
|
||||
//
|
||||
// If path is a regular file, LoadCAs simply adds it to the CA pool
|
||||
// if the file contains a valid X.509 certificate
|
||||
//
|
||||
// If the path points to a directory, LoadCAs iterates over all top-level
|
||||
// files within the directory and adds them to the CA pool if they contain
|
||||
// a valid X.509 certificate.
|
||||
func GetRootCAs(path string) (*x509.CertPool, error) {
|
||||
rootCAs, _ := loadSystemRoots()
|
||||
if rootCAs == nil {
|
||||
// In some systems system cert pool is not supported
|
||||
@@ -35,23 +46,48 @@ func GetRootCAs(certsCAsDir string) (*x509.CertPool, error) {
|
||||
rootCAs = x509.NewCertPool()
|
||||
}
|
||||
|
||||
fis, err := ioutil.ReadDir(certsCAsDir)
|
||||
// Open the file path and check whether its a regular file
|
||||
// or a directory.
|
||||
f, err := os.Open(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return rootCAs, nil
|
||||
}
|
||||
if errors.Is(err, os.ErrPermission) {
|
||||
return rootCAs, nil
|
||||
}
|
||||
if err != nil {
|
||||
return rootCAs, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
stat, err := f.Stat()
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) || os.IsPermission(err) {
|
||||
// Return success if CA's directory is missing or permission denied.
|
||||
return rootCAs, nil
|
||||
}
|
||||
return rootCAs, err
|
||||
}
|
||||
|
||||
// Load all custom CA files.
|
||||
for _, fi := range fis {
|
||||
caCert, err := ioutil.ReadFile(path.Join(certsCAsDir, fi.Name()))
|
||||
if err == nil {
|
||||
rootCAs.AppendCertsFromPEM(caCert)
|
||||
// In case of a file add it to the root CAs.
|
||||
if !stat.IsDir() {
|
||||
bytes, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return rootCAs, err
|
||||
}
|
||||
// ignore files which are not readable.
|
||||
if !rootCAs.AppendCertsFromPEM(bytes) {
|
||||
return rootCAs, fmt.Errorf("cert: %q does not contain a valid X.509 PEM-encoded certificate", path)
|
||||
}
|
||||
return rootCAs, nil
|
||||
}
|
||||
|
||||
// Otherwise iterate over the files in the directory
|
||||
// and add each on to the root CAs.
|
||||
files, err := f.Readdirnames(0)
|
||||
if err != nil {
|
||||
return rootCAs, err
|
||||
}
|
||||
for _, file := range files {
|
||||
bytes, err := ioutil.ReadFile(filepath.Join(path, file))
|
||||
if err == nil { // ignore files which are not readable.
|
||||
rootCAs.AppendCertsFromPEM(bytes)
|
||||
}
|
||||
}
|
||||
return rootCAs, nil
|
||||
}
|
||||
|
||||
136
pkg/kms/kes.go
Normal file
136
pkg/kms/kes.go
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright (c) 2015-2021 MinIO, Inc.
|
||||
//
|
||||
// This file is part of MinIO Object Storage stack
|
||||
//
|
||||
// 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 kms
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
|
||||
"github.com/minio/kes"
|
||||
)
|
||||
|
||||
// Config contains various KMS-related configuration
|
||||
// parameters - like KMS endpoints or authentication
|
||||
// credentials.
|
||||
type Config struct {
|
||||
// Endpoints contains a list of KMS server
|
||||
// HTTP endpoints.
|
||||
Endpoints []string
|
||||
|
||||
// DefaultKeyID is the key ID used when
|
||||
// no explicit key ID is specified for
|
||||
// a cryptographic operation.
|
||||
DefaultKeyID string
|
||||
|
||||
// Certificate is the client TLS certificate
|
||||
// to authenticate to KMS via mTLS.
|
||||
Certificate tls.Certificate
|
||||
|
||||
// RootCAs is a set of root CA certificates
|
||||
// to verify the KMS server TLS certificate.
|
||||
RootCAs *x509.CertPool
|
||||
}
|
||||
|
||||
// NewWithConfig returns a new KMS using the given
|
||||
// configuration.
|
||||
func NewWithConfig(config Config) (KMS, error) {
|
||||
if len(config.Endpoints) == 0 {
|
||||
return nil, errors.New("kms: no server endpoints")
|
||||
}
|
||||
var endpoints = make([]string, len(config.Endpoints)) // Copy => avoid being affect by any changes to the original slice
|
||||
copy(endpoints, config.Endpoints)
|
||||
|
||||
client := kes.NewClientWithConfig("", &tls.Config{
|
||||
MinVersion: tls.VersionTLS12,
|
||||
Certificates: []tls.Certificate{config.Certificate},
|
||||
RootCAs: config.RootCAs,
|
||||
})
|
||||
client.Endpoints = endpoints
|
||||
return &kesClient{
|
||||
client: client,
|
||||
defaultKeyID: config.DefaultKeyID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
type kesClient struct {
|
||||
defaultKeyID string
|
||||
client *kes.Client
|
||||
}
|
||||
|
||||
var _ KMS = (*kesClient)(nil) // compiler check
|
||||
|
||||
// Stat returns the current KES status containing a
|
||||
// list of KES endpoints and the default key ID.
|
||||
func (c *kesClient) Stat() (Status, error) {
|
||||
var endpoints = make([]string, len(c.client.Endpoints))
|
||||
copy(endpoints, c.client.Endpoints)
|
||||
return Status{
|
||||
Name: "KES",
|
||||
Endpoints: endpoints,
|
||||
DefaultKey: c.defaultKeyID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// CreateKey tries to create a new key at the KMS with the
|
||||
// given key ID.
|
||||
//
|
||||
// If the a key with the same keyID already exists then
|
||||
// CreateKey returns kes.ErrKeyExists.
|
||||
func (c *kesClient) CreateKey(keyID string) error {
|
||||
return c.client.CreateKey(context.Background(), keyID)
|
||||
}
|
||||
|
||||
// GenerateKey generates a new data encryption key using
|
||||
// the key at the KES server referenced by the key ID.
|
||||
//
|
||||
// The default key ID will be used if keyID is empty.
|
||||
//
|
||||
// The context is associated and tied to the generated DEK.
|
||||
// The same context must be provided when the generated
|
||||
// key should be decrypted.
|
||||
func (c *kesClient) GenerateKey(keyID string, ctx Context) (DEK, error) {
|
||||
if keyID == "" {
|
||||
keyID = c.defaultKeyID
|
||||
}
|
||||
ctxBytes, err := ctx.MarshalText()
|
||||
if err != nil {
|
||||
return DEK{}, err
|
||||
}
|
||||
dek, err := c.client.GenerateKey(context.Background(), keyID, ctxBytes)
|
||||
if err != nil {
|
||||
return DEK{}, nil
|
||||
}
|
||||
return DEK{
|
||||
KeyID: keyID,
|
||||
Plaintext: dek.Plaintext,
|
||||
Ciphertext: dek.Ciphertext,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecryptKey decrypts the ciphertext with the key at the KES
|
||||
// server referenced by the key ID. The context must match the
|
||||
// context value used to generate the ciphertext.
|
||||
func (c *kesClient) DecryptKey(keyID string, ciphertext []byte, ctx Context) ([]byte, error) {
|
||||
ctxBytes, err := ctx.MarshalText()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.client.Decrypt(context.Background(), keyID, ciphertext, ctxBytes)
|
||||
}
|
||||
Reference in New Issue
Block a user