Make unit testable cert parsing functions. (#3863)

This commit is contained in:
Bala FA 2017-03-09 08:50:01 +05:30 committed by Harshavardhana
parent 47ac410ab0
commit 8a9852220d
9 changed files with 420 additions and 245 deletions

View File

@ -803,7 +803,7 @@ func (adminAPI adminAPIHandlers) SetConfigHandler(w http.ResponseWriter, r *http
// Take a lock on minio/config.json. NB minio is a reserved // Take a lock on minio/config.json. NB minio is a reserved
// bucket name and wouldn't conflict with normal object // bucket name and wouldn't conflict with normal object
// operations. // operations.
configLock := globalNSMutex.NewNSLock(minioReservedBucket, globalMinioConfigFile) configLock := globalNSMutex.NewNSLock(minioReservedBucket, minioConfigFile)
configLock.Lock() configLock.Lock()
defer configLock.Unlock() defer configLock.Unlock()

View File

@ -1,5 +1,5 @@
/* /*
* Minio Cloud Storage, (C) 2015, 2016 Minio, Inc. * Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -19,120 +19,90 @@ package cmd
import ( import (
"crypto/x509" "crypto/x509"
"encoding/pem" "encoding/pem"
"errors" "fmt"
"io/ioutil" "io/ioutil"
"path/filepath" "path/filepath"
) )
// getCertsPath get certs path.
func getCertsPath() string {
return filepath.Join(getConfigDir(), globalMinioCertsDir)
}
// getCertFile must get cert file.
func getCertFile() string {
return filepath.Join(getCertsPath(), globalMinioCertFile)
}
// getKeyFile must get key file.
func getKeyFile() string {
return filepath.Join(getCertsPath(), globalMinioKeyFile)
}
// createCertsPath create certs path.
func createCertsPath() error {
rootCAsPath := filepath.Join(getCertsPath(), globalMinioCertsCADir)
return mkdirAll(rootCAsPath, 0700)
}
// getCAFiles must get the list of the CA certificates stored in minio config dir
func getCAFiles() (caCerts []string) {
CAsDir := filepath.Join(getCertsPath(), globalMinioCertsCADir)
if caFiles, err := ioutil.ReadDir(CAsDir); err == nil {
// Ignore any error.
for _, cert := range caFiles {
caCerts = append(caCerts, filepath.Join(CAsDir, cert.Name()))
}
}
return caCerts
}
// getSystemCertPool returns empty cert pool in case of error (windows)
func getSystemCertPool() *x509.CertPool {
pool, err := x509.SystemCertPool()
if err != nil {
pool = x509.NewCertPool()
}
return pool
}
// isCertFileExists verifies if cert file exists, returns true if
// found, false otherwise.
func isCertFileExists() bool {
return isFile(getCertFile())
}
// isKeyFileExists verifies if key file exists, returns true if found,
// false otherwise.
func isKeyFileExists() bool {
return isFile(getKeyFile())
}
// isSSL - returns true with both cert and key exists. // isSSL - returns true with both cert and key exists.
func isSSL() bool { func isSSL() bool {
return isCertFileExists() && isKeyFileExists() return isFile(getPublicCertFile()) && isFile(getPrivateKeyFile())
} }
// Reads certificated file and returns a list of parsed certificates. func parsePublicCertFile(certFile string) (certs []*x509.Certificate, err error) {
func readCertificateChain() ([]*x509.Certificate, error) { var bytes []byte
bytes, err := ioutil.ReadFile(getCertFile())
if err != nil { if bytes, err = ioutil.ReadFile(certFile); err != nil {
return nil, err return certs, err
} }
// Proceed to parse the certificates.
return parseCertificateChain(bytes)
}
// Parses certificate chain, returns a list of parsed certificates.
func parseCertificateChain(bytes []byte) ([]*x509.Certificate, error) {
var certs []*x509.Certificate
var block *pem.Block
current := bytes
// Parse all certs in the chain. // Parse all certs in the chain.
var block *pem.Block
var cert *x509.Certificate
current := bytes
for len(current) > 0 { for len(current) > 0 {
block, current = pem.Decode(current) if block, current = pem.Decode(current); block == nil {
if block == nil { err = fmt.Errorf("Could not read PEM block from file %s", certFile)
return nil, errors.New("Could not PEM block") return certs, err
} }
// Parse the decoded certificate.
cert, err := x509.ParseCertificate(block.Bytes) if cert, err = x509.ParseCertificate(block.Bytes); err != nil {
return certs, err
}
certs = append(certs, cert)
}
if len(certs) == 0 {
err = fmt.Errorf("Empty public certificate file %s", certFile)
}
return certs, err
}
// Reads certificate file and returns a list of parsed certificates.
func readCertificateChain() ([]*x509.Certificate, error) {
return parsePublicCertFile(getPublicCertFile())
}
func getRootCAs(certsCAsDir string) (*x509.CertPool, error) {
// Get all CA file names.
var caFiles []string
fis, err := ioutil.ReadDir(certsCAsDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
certs = append(certs, cert) for _, fi := range fis {
caFiles = append(caFiles, filepath.Join(certsCAsDir, fi.Name()))
} }
return certs, nil
}
// loadRootCAs fetches CA files provided in minio config and adds them to globalRootCAs
// Currently under Windows, there is no way to load system + user CAs at the same time
func loadRootCAs() {
caFiles := getCAFiles()
if len(caFiles) == 0 { if len(caFiles) == 0 {
return return nil, nil
} }
// Get system cert pool, and empty cert pool under Windows because it is not supported
globalRootCAs = getSystemCertPool() rootCAs, err := x509.SystemCertPool()
if err != nil {
// In some systems like Windows, system cert pool is not supported.
// Hence we create a new cert pool.
rootCAs = x509.NewCertPool()
}
// Load custom root CAs for client requests // Load custom root CAs for client requests
for _, caFile := range caFiles { for _, caFile := range caFiles {
caCert, err := ioutil.ReadFile(caFile) caCert, err := ioutil.ReadFile(caFile)
if err != nil { if err != nil {
fatalIf(err, "Unable to load a CA file") return rootCAs, err
} }
globalRootCAs.AppendCertsFromPEM(caCert)
rootCAs.AppendCertsFromPEM(caCert)
} }
return rootCAs, nil
}
// loadRootCAs fetches CA files provided in minio config and adds them to globalRootCAs
// Currently under Windows, there is no way to load system + user CAs at the same time
func loadRootCAs() (err error) {
globalRootCAs, err = getRootCAs(getCADir())
return err
} }

View File

@ -1,5 +1,5 @@
/* /*
* Minio Cloud Storage, (C) 2015, 2016 Minio, Inc. * Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -17,49 +17,86 @@
package cmd package cmd
import ( import (
"fmt"
"io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strings" "runtime"
"testing" "testing"
) )
// Make sure we have a valid certs path. func createTempFile(prefix, content string) (tempFile string, err error) {
func TestGetCertsPath(t *testing.T) { var tmpfile *os.File
path := getCertsPath()
if path == "" { if tmpfile, err = ioutil.TempFile("", prefix); err != nil {
t.Errorf("expected path to not be an empty string, got: '%s'", path) return tempFile, err
}
// Ensure it contains some sort of path separator.
if !strings.ContainsRune(path, os.PathSeparator) {
t.Errorf("expected path to contain file separator")
}
// It should also be an absolute path.
if !filepath.IsAbs(path) {
t.Errorf("expected path to be an absolute path")
} }
// This will error if something goes wrong, so just call it. if _, err = tmpfile.Write([]byte(content)); err != nil {
getCertsPath() return tempFile, err
}
if err = tmpfile.Close(); err != nil {
return tempFile, err
}
tempFile = tmpfile.Name()
return tempFile, err
} }
// Ensure that the certificate and key file getters contain their respective func TestParsePublicCertFile(t *testing.T) {
// file name and endings. tempFile1, err := createTempFile("public-cert-file", "")
func TestGetFiles(t *testing.T) { if err != nil {
file := getCertFile() t.Fatalf("Unable to create temporary file. %v", err)
if !strings.Contains(file, globalMinioCertFile) {
t.Errorf("CertFile does not contain %s", globalMinioCertFile)
} }
defer os.Remove(tempFile1)
file = getKeyFile() tempFile2, err := createTempFile("public-cert-file",
if !strings.Contains(file, globalMinioKeyFile) { `-----BEGIN CERTIFICATE-----
t.Errorf("KeyFile does not contain %s", globalMinioKeyFile) MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
AQkBFg50ZXN0c0BtaW5pby5pbzAeFw0xNjEwMTQxMTM0MjJaFw0xNzEwMTQxMTM0
MjJaMH8xCzAJBgNVBAYTAlpZMQ4wDAYDVQQIEwVNaW5pbzERMA8GA1UEBxMISW50
ZXJuZXQxDjAMBgNVBA-some-junk-Q4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF
TWluaW8xHTAbBgkqhkiG9w0BCQEWDnRlc3RzQG1pbmlvLmlvMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDwNUYB/Sj79WsUE8qnXzzh2glSzWxUE79sCOpQYK83
HWkrl5WxlG8ZxDR1IQV9Ex/lzigJu8G+KXahon6a+3n5GhNrYRe5kIXHQHz0qvv4
aMulqlnYpvSfC83aaO9GVBtwXS/O4Nykd7QBg4nZlazVmsGk7POOjhpjGShRsqpU
JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
M9ofSEt/bdRD
-----END CERTIFICATE-----`)
if err != nil {
t.Fatalf("Unable to create temporary file. %v", err)
} }
} defer os.Remove(tempFile2)
// Parses .crt file contents tempFile3, err := createTempFile("public-cert-file",
func TestParseCertificateChain(t *testing.T) { `-----BEGIN CERTIFICATE-----
// given MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
cert := `-----BEGIN CERTIFICATE----- WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
AQkBFg50ZXN0c0BtaW5pby5pbzAeFw0xNjEwMTQxMTM0MjJaFw0xNzEwMTQxMTM0
MjJaMH8xCzAJBgNVBAYTAlpZMQ4wDAYDVQQIEwVNaW5pbzERMA8GA1UEBxMISW50
ZXJuZXQxDjAMBgNVBAabababababaQ4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF
TWluaW8xHTAbBgkqhkiG9w0BCQEWDnRlc3RzQG1pbmlvLmlvMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDwNUYB/Sj79WsUE8qnXzzh2glSzWxUE79sCOpQYK83
HWkrl5WxlG8ZxDR1IQV9Ex/lzigJu8G+KXahon6a+3n5GhNrYRe5kIXHQHz0qvv4
aMulqlnYpvSfC83aaO9GVBtwXS/O4Nykd7QBg4nZlazVmsGk7POOjhpjGShRsqpU
JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
M9ofSEt/bdRD
-----END CERTIFICATE-----`)
if err != nil {
t.Fatalf("Unable to create temporary file. %v", err)
}
defer os.Remove(tempFile3)
tempFile4, err := createTempFile("public-cert-file",
`-----BEGIN CERTIFICATE-----
MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
@ -74,35 +111,149 @@ JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
M9ofSEt/bdRD M9ofSEt/bdRD
-----END CERTIFICATE-----` -----END CERTIFICATE-----`)
// when
certs, err := parseCertificateChain([]byte(cert))
// then
if err != nil { if err != nil {
t.Fatalf("Could not parse certificate: %s", err) t.Fatalf("Unable to create temporary file. %v", err)
}
defer os.Remove(tempFile4)
tempFile5, err := createTempFile("public-cert-file",
`-----BEGIN CERTIFICATE-----
MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
AQkBFg50ZXN0c0BtaW5pby5pbzAeFw0xNjEwMTQxMTM0MjJaFw0xNzEwMTQxMTM0
MjJaMH8xCzAJBgNVBAYTAlpZMQ4wDAYDVQQIEwVNaW5pbzERMA8GA1UEBxMISW50
ZXJuZXQxDjAMBgNVBAoTBU1pbmlvMQ4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF
TWluaW8xHTAbBgkqhkiG9w0BCQEWDnRlc3RzQG1pbmlvLmlvMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDwNUYB/Sj79WsUE8qnXzzh2glSzWxUE79sCOpQYK83
HWkrl5WxlG8ZxDR1IQV9Ex/lzigJu8G+KXahon6a+3n5GhNrYRe5kIXHQHz0qvv4
aMulqlnYpvSfC83aaO9GVBtwXS/O4Nykd7QBg4nZlazVmsGk7POOjhpjGShRsqpU
JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
M9ofSEt/bdRD
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
AQkBFg50ZXN0c0BtaW5pby5pbzAeFw0xNjEwMTQxMTM0MjJaFw0xNzEwMTQxMTM0
MjJaMH8xCzAJBgNVBAYTAlpZMQ4wDAYDVQQIEwVNaW5pbzERMA8GA1UEBxMISW50
ZXJuZXQxDjAMBgNVBAoTBU1pbmlvMQ4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF
TWluaW8xHTAbBgkqhkiG9w0BCQEWDnRlc3RzQG1pbmlvLmlvMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDwNUYB/Sj79WsUE8qnXzzh2glSzWxUE79sCOpQYK83
HWkrl5WxlG8ZxDR1IQV9Ex/lzigJu8G+KXahon6a+3n5GhNrYRe5kIXHQHz0qvv4
aMulqlnYpvSfC83aaO9GVBtwXS/O4Nykd7QBg4nZlazVmsGk7POOjhpjGShRsqpU
JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
M9ofSEt/bdRD
-----END CERTIFICATE-----`)
if err != nil {
t.Fatalf("Unable to create temporary file. %v", err)
}
defer os.Remove(tempFile5)
nonexistentErr := fmt.Errorf("open nonexistent-file: no such file or directory")
if runtime.GOOS == "windows" {
// Below concatenation is done to get rid of goline error
// "error strings should not be capitalized or end with punctuation or a newline"
nonexistentErr = fmt.Errorf("open nonexistent-file:" + " The system cannot find the file specified.")
} }
if len(certs) != 1 { testCases := []struct {
t.Fatalf("Expected number of certificates in chain was 1, actual: %d", len(certs)) certFile string
expectedResultLen int
expectedErr error
}{
{"nonexistent-file", 0, nonexistentErr},
{tempFile1, 0, fmt.Errorf("Empty public certificate file %s", tempFile1)},
{tempFile2, 0, fmt.Errorf("Could not read PEM block from file %s", tempFile2)},
{tempFile3, 0, fmt.Errorf("asn1: structure error: sequence tag mismatch")},
{tempFile4, 1, nil},
{tempFile5, 2, nil},
} }
if certs[0].Subject.CommonName != "Minio" { for _, testCase := range testCases {
t.Fatalf("Expected Subject.CommonName was Minio, actual: %s", certs[0].Subject.CommonName) certs, err := parsePublicCertFile(testCase.certFile)
if testCase.expectedErr == nil {
if err != nil {
t.Fatalf("error: expected = <nil>, got = %v", err)
}
} else if err == nil {
t.Fatalf("error: expected = %v, got = <nil>", testCase.expectedErr)
} else if testCase.expectedErr.Error() != err.Error() {
t.Fatalf("error: expected = %v, got = %v", testCase.expectedErr, err)
}
if len(certs) != testCase.expectedResultLen {
t.Fatalf("certs: expected = %v, got = %v", testCase.expectedResultLen, len(certs))
}
} }
} }
// Parses invalid .crt file contents and returns error func TestGetRootCAs(t *testing.T) {
func TestParseInvalidCertificateChain(t *testing.T) { emptydir, err := ioutil.TempDir("", "test-get-root-cas")
// given if err != nil {
cert := `This is now valid certificate` t.Fatalf("Unable create temp directory. %v", emptydir)
}
defer os.RemoveAll(emptydir)
// when dir1, err := ioutil.TempDir("", "test-get-root-cas")
_, err := parseCertificateChain([]byte(cert)) if err != nil {
t.Fatalf("Unable create temp directory. %v", dir1)
}
defer os.RemoveAll(dir1)
if err = os.Mkdir(filepath.Join(dir1, "empty-dir"), 0755); err != nil {
t.Fatalf("Unable create empty dir. %v", err)
}
// then dir2, err := ioutil.TempDir("", "test-get-root-cas")
if err == nil { if err != nil {
t.Fatalf("Expected error but none occurred") t.Fatalf("Unable create temp directory. %v", dir2)
}
defer os.RemoveAll(dir2)
if err = ioutil.WriteFile(filepath.Join(dir2, "empty-file"), []byte{}, 0644); err != nil {
t.Fatalf("Unable create test file. %v", err)
}
nonexistentErr := fmt.Errorf("open nonexistent-dir: no such file or directory")
if runtime.GOOS == "windows" {
// Below concatenation is done to get rid of goline error
// "error strings should not be capitalized or end with punctuation or a newline"
nonexistentErr = fmt.Errorf("open nonexistent-dir:" + " The system cannot find the file specified.")
}
err1 := fmt.Errorf("read %s: is a directory", filepath.Join(dir1, "empty-dir"))
if runtime.GOOS == "windows" {
// Below concatenation is done to get rid of goline error
// "error strings should not be capitalized or end with punctuation or a newline"
err1 = fmt.Errorf("read %s:"+" The handle is invalid.", filepath.Join(dir1, "empty-dir"))
}
testCases := []struct {
certCAsDir string
expectedErr error
}{
{"nonexistent-dir", nonexistentErr},
{dir1, err1},
{emptydir, nil},
{dir2, nil},
}
for _, testCase := range testCases {
_, err := getRootCAs(testCase.certCAsDir)
if testCase.expectedErr == nil {
if err != nil {
t.Fatalf("error: expected = <nil>, got = %v", err)
}
} else if err == nil {
t.Fatalf("error: expected = %v, got = <nil>", testCase.expectedErr)
} else if testCase.expectedErr.Error() != err.Error() {
t.Fatalf("error: expected = %v, got = %v", testCase.expectedErr, err)
}
} }
} }

139
cmd/config-dir.go Normal file
View File

@ -0,0 +1,139 @@
/*
* Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"path/filepath"
"sync"
homedir "github.com/minio/go-homedir"
"github.com/minio/mc/pkg/console"
)
const (
// Default minio configuration directory where below configuration files/directories are stored.
defaultMinioConfigDir = ".minio"
// Minio configuration file.
minioConfigFile = "config.json"
// Directory contains below files/directories for HTTPS configuration.
certsDir = "certs"
// Directory contains all CA certificates other than system defaults for HTTPS.
certsCADir = "CAs"
// Public certificate file for HTTPS.
publicCertFile = "public.crt"
// Private key file for HTTPS.
privateKeyFile = "private.key"
)
// ConfigDir - configuration directory with locking.
type ConfigDir struct {
sync.Mutex
dir string
}
// Set - saves given directory as configuration directory.
func (config *ConfigDir) Set(dir string) {
config.Lock()
defer config.Unlock()
config.dir = dir
}
// Get - returns current configuration directory.
func (config *ConfigDir) Get() string {
config.Lock()
defer config.Unlock()
return config.dir
}
func (config *ConfigDir) getCertsDir() string {
return filepath.Join(config.Get(), certsDir)
}
// GetCADir - returns certificate CA directory.
func (config *ConfigDir) GetCADir() string {
return filepath.Join(config.getCertsDir(), certsCADir)
}
// Create - creates configuration directory tree.
func (config *ConfigDir) Create() error {
return mkdirAll(config.GetCADir(), 0700)
}
// GetMinioConfigFile - returns absolute path of config.json file.
func (config *ConfigDir) GetMinioConfigFile() string {
return filepath.Join(config.Get(), minioConfigFile)
}
// GetPublicCertFile - returns absolute path of public.crt file.
func (config *ConfigDir) GetPublicCertFile() string {
return filepath.Join(config.getCertsDir(), publicCertFile)
}
// GetPrivateKeyFile - returns absolute path of private.key file.
func (config *ConfigDir) GetPrivateKeyFile() string {
return filepath.Join(config.getCertsDir(), privateKeyFile)
}
func mustGetDefaultConfigDir() string {
homeDir, err := homedir.Dir()
if err != nil {
console.Fatalln("Unable to get home directory.", err)
}
return filepath.Join(homeDir, defaultMinioConfigDir)
}
var configDir = &ConfigDir{dir: mustGetDefaultConfigDir()}
func setConfigDir(dir string) {
configDir.Set(dir)
}
func getConfigDir() string {
return configDir.Get()
}
func getCADir() string {
return configDir.GetCADir()
}
func createConfigDir() error {
return configDir.Create()
}
func getConfigFile() string {
return configDir.GetMinioConfigFile()
}
func getPublicCertFile() string {
return configDir.GetPublicCertFile()
}
func getPrivateKeyFile() string {
return configDir.GetPrivateKeyFile()
}
func isConfigFileExists() bool {
return isFile(getConfigFile())
}

View File

@ -66,7 +66,7 @@ func TestServerConfigMigrateInexistentConfig(t *testing.T) {
defer removeAll(rootPath) defer removeAll(rootPath)
setConfigDir(rootPath) setConfigDir(rootPath)
configPath := rootPath + "/" + globalMinioConfigFile configPath := rootPath + "/" + minioConfigFile
// Remove config file // Remove config file
if err := os.Remove(configPath); err != nil { if err := os.Remove(configPath); err != nil {
@ -121,7 +121,7 @@ func TestServerConfigMigrateV2toV14(t *testing.T) {
defer removeAll(rootPath) defer removeAll(rootPath)
setConfigDir(rootPath) setConfigDir(rootPath)
configPath := rootPath + "/" + globalMinioConfigFile configPath := rootPath + "/" + minioConfigFile
// Create a corrupted config file // Create a corrupted config file
if err := ioutil.WriteFile(configPath, []byte("{ \"version\":\"2\","), 0644); err != nil { if err := ioutil.WriteFile(configPath, []byte("{ \"version\":\"2\","), 0644); err != nil {
@ -175,7 +175,7 @@ func TestServerConfigMigrateFaultyConfig(t *testing.T) {
defer removeAll(rootPath) defer removeAll(rootPath)
setConfigDir(rootPath) setConfigDir(rootPath)
configPath := rootPath + "/" + globalMinioConfigFile configPath := rootPath + "/" + minioConfigFile
// Create a corrupted config file // Create a corrupted config file
if err := ioutil.WriteFile(configPath, []byte("{ \"version\":\""), 0644); err != nil { if err := ioutil.WriteFile(configPath, []byte("{ \"version\":\""), 0644); err != nil {

View File

@ -1,78 +0,0 @@
/*
* Minio Cloud Storage, (C) 2015, 2016, 2017 Minio, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package cmd
import (
"path/filepath"
"sync"
homedir "github.com/minio/go-homedir"
"github.com/minio/mc/pkg/console"
)
// ConfigDir - configuration directory with locking.
type ConfigDir struct {
sync.Mutex
dir string
}
// Set - saves given directory as configuration directory.
func (config *ConfigDir) Set(dir string) {
config.Lock()
defer config.Unlock()
config.dir = dir
}
// Get - returns current configuration directory.
func (config *ConfigDir) Get() string {
config.Lock()
defer config.Unlock()
return config.dir
}
func mustGetDefaultConfigDir() string {
homeDir, err := homedir.Dir()
if err != nil {
console.Fatalln("Unable to get home directory.", err)
}
return filepath.Join(homeDir, globalMinioConfigDir)
}
var configDir = &ConfigDir{dir: mustGetDefaultConfigDir()}
func setConfigDir(dir string) {
configDir.Set(dir)
}
func getConfigDir() string {
return configDir.Get()
}
func createConfigDir() error {
return mkdirAll(getConfigDir(), 0700)
}
func getConfigFile() string {
return filepath.Join(getConfigDir(), globalMinioConfigFile)
}
func isConfigFileExists() bool {
return isFile(getConfigFile())
}

View File

@ -28,12 +28,6 @@ import (
// minio configuration related constants. // minio configuration related constants.
const ( const (
globalMinioConfigDir = ".minio"
globalMinioCertsDir = "certs"
globalMinioCertsCADir = "CAs"
globalMinioCertFile = "public.crt"
globalMinioKeyFile = "private.key"
globalMinioConfigFile = "config.json"
globalMinioCertExpireWarnDays = time.Hour * 24 * 30 // 30 days. globalMinioCertExpireWarnDays = time.Hour * 24 * 30 // 30 days.
globalMinioDefaultRegion = "us-east-1" globalMinioDefaultRegion = "us-east-1"

View File

@ -144,6 +144,9 @@ func initConfig() {
// Generic Minio initialization to create/load config, prepare loggers, etc.. // Generic Minio initialization to create/load config, prepare loggers, etc..
func minioInit(ctx *cli.Context) { func minioInit(ctx *cli.Context) {
// Create certs path.
fatalIf(createConfigDir(), "Unable to create \"certs\" directory.")
// Is TLS configured?. // Is TLS configured?.
globalIsSSL = isSSL() globalIsSSL = isSSL()
@ -155,7 +158,6 @@ func minioInit(ctx *cli.Context) {
// Init the error tracing module. // Init the error tracing module.
initError() initError()
} }
type serverCmdConfig struct { type serverCmdConfig struct {
@ -208,11 +210,8 @@ func initServerConfig(c *cli.Context) {
// Initialization such as config generating/loading config, enable logging, .. // Initialization such as config generating/loading config, enable logging, ..
minioInit(c) minioInit(c)
// Create certs path.
fatalIf(createCertsPath(), "Unable to create \"certs\" directory.")
// Load user supplied root CAs // Load user supplied root CAs
loadRootCAs() fatalIf(loadRootCAs(), "Unable to load a CA files")
// Set system resources to maximum. // Set system resources to maximum.
errorIf(setMaxResources(), "Unable to change resource limit") errorIf(setMaxResources(), "Unable to change resource limit")
@ -540,7 +539,7 @@ func serverMain(c *cli.Context) {
go func() { go func() {
cert, key := "", "" cert, key := "", ""
if globalIsSSL { if globalIsSSL {
cert, key = getCertFile(), getKeyFile() cert, key = getPublicCertFile(), getPrivateKeyFile()
} }
fatalIf(apiServer.ListenAndServe(cert, key), "Failed to start minio server.") fatalIf(apiServer.ListenAndServe(cert, key), "Failed to start minio server.")
}() }()

View File

@ -354,12 +354,12 @@ func TestServerListenAndServeTLS(t *testing.T) {
})) }))
// Create a cert // Create a cert
err := createCertsPath() err := createConfigDir()
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
certFile := getCertFile() certFile := getPublicCertFile()
keyFile := getKeyFile() keyFile := getPrivateKeyFile()
defer os.RemoveAll(certFile) defer os.RemoveAll(certFile)
defer os.RemoveAll(keyFile) defer os.RemoveAll(keyFile)
@ -420,8 +420,8 @@ func TestServerListenAndServeTLS(t *testing.T) {
// generateTestCert creates a cert and a key used for testing only // generateTestCert creates a cert and a key used for testing only
func generateTestCert(host string) error { func generateTestCert(host string) error {
certPath := getCertFile() certPath := getPublicCertFile()
keyPath := getKeyFile() keyPath := getPrivateKeyFile()
priv, err := rsa.GenerateKey(rand.Reader, 2048) priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil { if err != nil {
return err return err