Migrate config.json from config-dir to backend

This PR is the first set of changes to move the config
to the backend, the changes use the existing `config.json`
allows it to be migrated such that we can save it in on
backend disks.

In future releases, we will slowly migrate out of the
current architecture.

Fixes #6182
This commit is contained in:
Harshavardhana
2018-07-23 15:36:10 -07:00
parent 380524ae27
commit 1fb2e9ef95
46 changed files with 818 additions and 845 deletions

View File

@@ -18,6 +18,8 @@
package madmin
import (
"bytes"
"crypto/rand"
"encoding/json"
"errors"
"fmt"
@@ -26,6 +28,8 @@ import (
"net/http"
"github.com/minio/minio/pkg/quick"
"github.com/minio/sio"
"golang.org/x/crypto/argon2"
)
// NodeSummary - represents the result of an operation part of
@@ -43,13 +47,48 @@ type SetConfigResult struct {
Status bool `json:"status"`
}
// GetConfig - returns the config.json of a minio setup.
func (adm *AdminClient) GetConfig() ([]byte, error) {
// No TLS?
if !adm.secure {
return nil, fmt.Errorf("credentials/configuration cannot be retrieved over an insecure connection")
// EncryptServerConfigData - encrypts server config data.
func EncryptServerConfigData(password string, data []byte) ([]byte, error) {
salt := make([]byte, 32)
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
return nil, err
}
// derive an encryption key from the master key and the nonce
var key [32]byte
copy(key[:], argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32))
encrypted, err := sio.EncryptReader(bytes.NewReader(data), sio.Config{
Key: key[:]},
)
if err != nil {
return nil, err
}
edata, err := ioutil.ReadAll(encrypted)
return append(salt, edata...), err
}
// DecryptServerConfigData - decrypts server config data.
func DecryptServerConfigData(password string, data io.Reader) ([]byte, error) {
salt := make([]byte, 32)
if _, err := io.ReadFull(data, salt); err != nil {
return nil, err
}
// derive an encryption key from the master key and the nonce
var key [32]byte
copy(key[:], argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32))
decrypted, err := sio.DecryptReader(data, sio.Config{
Key: key[:]},
)
if err != nil {
return nil, err
}
return ioutil.ReadAll(decrypted)
}
// GetConfig - returns the config.json of a minio setup, incoming data is encrypted.
func (adm *AdminClient) GetConfig() ([]byte, error) {
// Execute GET on /minio/admin/v1/config to get config of a setup.
resp, err := adm.executeMethod("GET",
requestData{relPath: "/v1/config"})
@@ -61,19 +100,15 @@ func (adm *AdminClient) GetConfig() ([]byte, error) {
if resp.StatusCode != http.StatusOK {
return nil, httpRespToErrorResponse(resp)
}
defer resp.Body.Close()
// Return the JSON marshaled bytes to user.
return ioutil.ReadAll(resp.Body)
return DecryptServerConfigData(adm.secretAccessKey, resp.Body)
}
// SetConfig - set config supplied as config.json for the setup.
func (adm *AdminClient) SetConfig(config io.Reader) (r SetConfigResult, err error) {
const maxConfigJSONSize = 256 * 1024 // 256KiB
if !adm.secure { // No TLS?
return r, fmt.Errorf("credentials/configuration cannot be updated over an insecure connection")
}
// Read configuration bytes
configBuf := make([]byte, maxConfigJSONSize+1)
n, err := io.ReadFull(config, configBuf)
@@ -104,9 +139,14 @@ func (adm *AdminClient) SetConfig(config io.Reader) (r SetConfigResult, err erro
return r, errors.New("Duplicate key in json file: " + err.Error())
}
econfigBytes, err := EncryptServerConfigData(adm.secretAccessKey, configBytes)
if err != nil {
return r, err
}
reqData := requestData{
relPath: "/v1/config",
content: configBytes,
content: econfigBytes,
}
// Execute PUT on /minio/admin/v1/config to set config.

View File

@@ -153,9 +153,9 @@ func (d config) DeepDiff(c Config) ([]structs.Field, error) {
return fields, nil
}
// checkData - checks the validity of config data. Data should be of
// CheckData - checks the validity of config data. Data should be of
// type struct and contain a string type field called "Version".
func checkData(data interface{}) error {
func CheckData(data interface{}) error {
if !structs.IsStruct(data) {
return fmt.Errorf("interface must be struct type")
}
@@ -211,7 +211,7 @@ func LoadConfig(filename string, clnt *etcd.Client, data interface{}) (qc Config
// SaveConfig - saves given configuration data into given file as JSON.
func SaveConfig(data interface{}, filename string, clnt *etcd.Client) (err error) {
if err = checkData(data); err != nil {
if err = CheckData(data); err != nil {
return err
}
var qc Config
@@ -225,7 +225,7 @@ func SaveConfig(data interface{}, filename string, clnt *etcd.Client) (err error
// NewConfig loads config from etcd client if provided, otherwise loads from a local filename.
// fails when all else fails.
func NewConfig(data interface{}, clnt *etcd.Client) (cfg Config, err error) {
if err := checkData(data); err != nil {
if err := CheckData(data); err != nil {
return nil, err
}

View File

@@ -106,7 +106,7 @@ func TestSaveFailOnDir(t *testing.T) {
}
func TestCheckData(t *testing.T) {
err := checkData(nil)
err := CheckData(nil)
if err == nil {
t.Fatal("Unexpected should fail")
}
@@ -117,7 +117,7 @@ func TestCheckData(t *testing.T) {
Directories []string
}
saveMeBadNoVersion := myStructBadNoVersion{"guest", "nopassword", []string{"Work", "Documents", "Music"}}
err = checkData(&saveMeBadNoVersion)
err = CheckData(&saveMeBadNoVersion)
if err == nil {
t.Fatal("Unexpected should fail if Version is not set")
}
@@ -128,7 +128,7 @@ func TestCheckData(t *testing.T) {
Password string
}
saveMeBadVersionInt := myStructBadVersionInt{1, "guest", "nopassword"}
err = checkData(&saveMeBadVersionInt)
err = CheckData(&saveMeBadVersionInt)
if err == nil {
t.Fatal("Unexpected should fail if Version is integer")
}
@@ -141,7 +141,7 @@ func TestCheckData(t *testing.T) {
}
saveMeGood := myStructGood{"1", "guest", "nopassword", []string{"Work", "Documents", "Music"}}
err = checkData(&saveMeGood)
err = CheckData(&saveMeGood)
if err != nil {
t.Fatal(err)
}