Make unit testable cert parsing functions. (#3863)

master
Bala FA 8 years ago committed by Harshavardhana
parent 47ac410ab0
commit 8a9852220d
  1. 2
      cmd/admin-handlers.go
  2. 140
      cmd/certs.go
  3. 247
      cmd/certs_test.go
  4. 139
      cmd/config-dir.go
  5. 6
      cmd/config-migrate_test.go
  6. 78
      cmd/config.go
  7. 6
      cmd/globals.go
  8. 11
      cmd/server-main.go
  9. 10
      cmd/server-mux_test.go

@ -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()

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

@ -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,55 +17,70 @@
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) { if _, err = tmpfile.Write([]byte(content)); err != nil {
t.Errorf("expected path to contain file separator") return tempFile, err
} }
// It should also be an absolute path.
if !filepath.IsAbs(path) { if err = tmpfile.Close(); err != nil {
t.Errorf("expected path to be an absolute path") return tempFile, err
} }
// This will error if something goes wrong, so just call it. tempFile = tmpfile.Name()
getCertsPath() 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
cert := `-----BEGIN CERTIFICATE-----
MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
AQkBFg50ZXN0c0BtaW5pby5pbzAeFw0xNjEwMTQxMTM0MjJaFw0xNzEwMTQxMTM0 AQkBFg50ZXN0c0BtaW5pby5pbzAeFw0xNjEwMTQxMTM0MjJaFw0xNzEwMTQxMTM0
MjJaMH8xCzAJBgNVBAYTAlpZMQ4wDAYDVQQIEwVNaW5pbzERMA8GA1UEBxMISW50 MjJaMH8xCzAJBgNVBAYTAlpZMQ4wDAYDVQQIEwVNaW5pbzERMA8GA1UEBxMISW50
ZXJuZXQxDjAMBgNVBAoTBU1pbmlvMQ4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF ZXJuZXQxDjAMBgNVBAabababababaQ4wDAYDVQQLEwVNaW5pbzEOMAwGA1UEAxMF
TWluaW8xHTAbBgkqhkiG9w0BCQEWDnRlc3RzQG1pbmlvLmlvMIGfMA0GCSqGSIb3 TWluaW8xHTAbBgkqhkiG9w0BCQEWDnRlc3RzQG1pbmlvLmlvMIGfMA0GCSqGSIb3
DQEBAQUAA4GNADCBiQKBgQDwNUYB/Sj79WsUE8qnXzzh2glSzWxUE79sCOpQYK83 DQEBAQUAA4GNADCBiQKBgQDwNUYB/Sj79WsUE8qnXzzh2glSzWxUE79sCOpQYK83
HWkrl5WxlG8ZxDR1IQV9Ex/lzigJu8G+KXahon6a+3n5GhNrYRe5kIXHQHz0qvv4 HWkrl5WxlG8ZxDR1IQV9Ex/lzigJu8G+KXahon6a+3n5GhNrYRe5kIXHQHz0qvv4
@ -74,35 +89,171 @@ JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
M9ofSEt/bdRD M9ofSEt/bdRD
-----END CERTIFICATE-----` -----END CERTIFICATE-----`)
if err != nil {
t.Fatalf("Unable to create temporary file. %v", err)
}
defer os.Remove(tempFile3)
// when tempFile4, err := createTempFile("public-cert-file",
certs, err := parseCertificateChain([]byte(cert)) `-----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(tempFile4)
// then 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 { if err != nil {
t.Fatalf("Could not parse certificate: %s", err) 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)
dir1, err := ioutil.TempDir("", "test-get-root-cas")
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)
}
dir2, err := ioutil.TempDir("", "test-get-root-cas")
if err != nil {
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},
}
// when for _, testCase := range testCases {
_, err := parseCertificateChain([]byte(cert)) _, err := getRootCAs(testCase.certCAsDir)
// then if testCase.expectedErr == nil {
if err == nil { if err != nil {
t.Fatalf("Expected error but none occurred") 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)
}
} }
} }

@ -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())
}

@ -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 {

@ -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())
}

@ -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"

@ -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.")
}() }()

@ -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

Loading…
Cancel
Save