mirror of
https://github.com/minio/minio.git
synced 2025-11-10 14:09:48 -05:00
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:
@@ -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.
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user