mirror of
https://github.com/minio/minio.git
synced 2024-12-24 06:05:55 -05:00
Make unit testable cert parsing functions. (#3863)
This commit is contained in:
parent
47ac410ab0
commit
8a9852220d
@ -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
|
||||
// bucket name and wouldn't conflict with normal object
|
||||
// operations.
|
||||
configLock := globalNSMutex.NewNSLock(minioReservedBucket, globalMinioConfigFile)
|
||||
configLock := globalNSMutex.NewNSLock(minioReservedBucket, minioConfigFile)
|
||||
configLock.Lock()
|
||||
defer configLock.Unlock()
|
||||
|
||||
|
160
cmd/certs.go
160
cmd/certs.go
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -19,120 +19,90 @@ package cmd
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"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.
|
||||
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) {
|
||||
var bytes []byte
|
||||
|
||||
if bytes, err = ioutil.ReadFile(certFile); err != nil {
|
||||
return certs, err
|
||||
}
|
||||
|
||||
// Parse all certs in the chain.
|
||||
var block *pem.Block
|
||||
var cert *x509.Certificate
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
bytes, err := ioutil.ReadFile(getCertFile())
|
||||
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 {
|
||||
return nil, 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.
|
||||
for len(current) > 0 {
|
||||
block, current = pem.Decode(current)
|
||||
if block == nil {
|
||||
return nil, errors.New("Could not PEM block")
|
||||
}
|
||||
// Parse the decoded certificate.
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
if err != nil {
|
||||
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 {
|
||||
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
|
||||
for _, caFile := range caFiles {
|
||||
caCert, err := ioutil.ReadFile(caFile)
|
||||
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
|
||||
}
|
||||
|
@ -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");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@ -17,49 +17,86 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"runtime"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Make sure we have a valid certs path.
|
||||
func TestGetCertsPath(t *testing.T) {
|
||||
path := getCertsPath()
|
||||
if path == "" {
|
||||
t.Errorf("expected path to not be an empty string, got: '%s'", path)
|
||||
}
|
||||
// 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")
|
||||
func createTempFile(prefix, content string) (tempFile string, err error) {
|
||||
var tmpfile *os.File
|
||||
|
||||
if tmpfile, err = ioutil.TempFile("", prefix); err != nil {
|
||||
return tempFile, err
|
||||
}
|
||||
|
||||
// This will error if something goes wrong, so just call it.
|
||||
getCertsPath()
|
||||
if _, err = tmpfile.Write([]byte(content)); err != nil {
|
||||
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
|
||||
// file name and endings.
|
||||
func TestGetFiles(t *testing.T) {
|
||||
file := getCertFile()
|
||||
if !strings.Contains(file, globalMinioCertFile) {
|
||||
t.Errorf("CertFile does not contain %s", globalMinioCertFile)
|
||||
func TestParsePublicCertFile(t *testing.T) {
|
||||
tempFile1, err := createTempFile("public-cert-file", "")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to create temporary file. %v", err)
|
||||
}
|
||||
defer os.Remove(tempFile1)
|
||||
|
||||
file = getKeyFile()
|
||||
if !strings.Contains(file, globalMinioKeyFile) {
|
||||
t.Errorf("KeyFile does not contain %s", globalMinioKeyFile)
|
||||
tempFile2, err := createTempFile("public-cert-file",
|
||||
`-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
func TestParseCertificateChain(t *testing.T) {
|
||||
// given
|
||||
cert := `-----BEGIN CERTIFICATE-----
|
||||
tempFile3, err := createTempFile("public-cert-file",
|
||||
`-----BEGIN CERTIFICATE-----
|
||||
MIICdTCCAd4CCQCO5G/W1xcE9TANBgkqhkiG9w0BAQUFADB/MQswCQYDVQQGEwJa
|
||||
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
|
||||
WTEOMAwGA1UECBMFTWluaW8xETAPBgNVBAcTCEludGVybmV0MQ4wDAYDVQQKEwVN
|
||||
aW5pbzEOMAwGA1UECxMFTWluaW8xDjAMBgNVBAMTBU1pbmlvMR0wGwYJKoZIhvcN
|
||||
@ -74,35 +111,149 @@ JwIDAQABMA0GCSqGSIb3DQEBBQUAA4GBALqjOA6bD8BEl7hkQ8XwX/owSAL0URDe
|
||||
nUfCOsXgIIAqgw4uTCLOfCJVZNKmRT+KguvPAQ6Z80vau2UxPX5Q2Q+OHXDRrEnK
|
||||
FjqSBgLP06Qw7a++bshlWGTt5bHWOneW3EQikedckVuIKPkOCib9yGi4VmBBjdFE
|
||||
M9ofSEt/bdRD
|
||||
-----END CERTIFICATE-----`
|
||||
|
||||
// when
|
||||
certs, err := parseCertificateChain([]byte(cert))
|
||||
|
||||
// then
|
||||
-----END CERTIFICATE-----`)
|
||||
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 {
|
||||
t.Fatalf("Expected number of certificates in chain was 1, actual: %d", len(certs))
|
||||
testCases := []struct {
|
||||
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" {
|
||||
t.Fatalf("Expected Subject.CommonName was Minio, actual: %s", certs[0].Subject.CommonName)
|
||||
for _, testCase := range testCases {
|
||||
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 TestParseInvalidCertificateChain(t *testing.T) {
|
||||
// given
|
||||
cert := `This is now valid certificate`
|
||||
func TestGetRootCAs(t *testing.T) {
|
||||
emptydir, err := ioutil.TempDir("", "test-get-root-cas")
|
||||
if err != nil {
|
||||
t.Fatalf("Unable create temp directory. %v", emptydir)
|
||||
}
|
||||
defer os.RemoveAll(emptydir)
|
||||
|
||||
// when
|
||||
_, err := parseCertificateChain([]byte(cert))
|
||||
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)
|
||||
}
|
||||
|
||||
// then
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but none occurred")
|
||||
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},
|
||||
}
|
||||
|
||||
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
139
cmd/config-dir.go
Normal 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())
|
||||
}
|
@ -66,7 +66,7 @@ func TestServerConfigMigrateInexistentConfig(t *testing.T) {
|
||||
defer removeAll(rootPath)
|
||||
|
||||
setConfigDir(rootPath)
|
||||
configPath := rootPath + "/" + globalMinioConfigFile
|
||||
configPath := rootPath + "/" + minioConfigFile
|
||||
|
||||
// Remove config file
|
||||
if err := os.Remove(configPath); err != nil {
|
||||
@ -121,7 +121,7 @@ func TestServerConfigMigrateV2toV14(t *testing.T) {
|
||||
defer removeAll(rootPath)
|
||||
|
||||
setConfigDir(rootPath)
|
||||
configPath := rootPath + "/" + globalMinioConfigFile
|
||||
configPath := rootPath + "/" + minioConfigFile
|
||||
|
||||
// Create a corrupted config file
|
||||
if err := ioutil.WriteFile(configPath, []byte("{ \"version\":\"2\","), 0644); err != nil {
|
||||
@ -175,7 +175,7 @@ func TestServerConfigMigrateFaultyConfig(t *testing.T) {
|
||||
defer removeAll(rootPath)
|
||||
|
||||
setConfigDir(rootPath)
|
||||
configPath := rootPath + "/" + globalMinioConfigFile
|
||||
configPath := rootPath + "/" + minioConfigFile
|
||||
|
||||
// Create a corrupted config file
|
||||
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.
|
||||
const (
|
||||
globalMinioConfigDir = ".minio"
|
||||
globalMinioCertsDir = "certs"
|
||||
globalMinioCertsCADir = "CAs"
|
||||
globalMinioCertFile = "public.crt"
|
||||
globalMinioKeyFile = "private.key"
|
||||
globalMinioConfigFile = "config.json"
|
||||
globalMinioCertExpireWarnDays = time.Hour * 24 * 30 // 30 days.
|
||||
|
||||
globalMinioDefaultRegion = "us-east-1"
|
||||
|
@ -144,6 +144,9 @@ func initConfig() {
|
||||
|
||||
// Generic Minio initialization to create/load config, prepare loggers, etc..
|
||||
func minioInit(ctx *cli.Context) {
|
||||
// Create certs path.
|
||||
fatalIf(createConfigDir(), "Unable to create \"certs\" directory.")
|
||||
|
||||
// Is TLS configured?.
|
||||
globalIsSSL = isSSL()
|
||||
|
||||
@ -155,7 +158,6 @@ func minioInit(ctx *cli.Context) {
|
||||
|
||||
// Init the error tracing module.
|
||||
initError()
|
||||
|
||||
}
|
||||
|
||||
type serverCmdConfig struct {
|
||||
@ -208,11 +210,8 @@ func initServerConfig(c *cli.Context) {
|
||||
// Initialization such as config generating/loading config, enable logging, ..
|
||||
minioInit(c)
|
||||
|
||||
// Create certs path.
|
||||
fatalIf(createCertsPath(), "Unable to create \"certs\" directory.")
|
||||
|
||||
// Load user supplied root CAs
|
||||
loadRootCAs()
|
||||
fatalIf(loadRootCAs(), "Unable to load a CA files")
|
||||
|
||||
// Set system resources to maximum.
|
||||
errorIf(setMaxResources(), "Unable to change resource limit")
|
||||
@ -540,7 +539,7 @@ func serverMain(c *cli.Context) {
|
||||
go func() {
|
||||
cert, key := "", ""
|
||||
if globalIsSSL {
|
||||
cert, key = getCertFile(), getKeyFile()
|
||||
cert, key = getPublicCertFile(), getPrivateKeyFile()
|
||||
}
|
||||
fatalIf(apiServer.ListenAndServe(cert, key), "Failed to start minio server.")
|
||||
}()
|
||||
|
@ -354,12 +354,12 @@ func TestServerListenAndServeTLS(t *testing.T) {
|
||||
}))
|
||||
|
||||
// Create a cert
|
||||
err := createCertsPath()
|
||||
err := createConfigDir()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
certFile := getCertFile()
|
||||
keyFile := getKeyFile()
|
||||
certFile := getPublicCertFile()
|
||||
keyFile := getPrivateKeyFile()
|
||||
defer os.RemoveAll(certFile)
|
||||
defer os.RemoveAll(keyFile)
|
||||
|
||||
@ -420,8 +420,8 @@ func TestServerListenAndServeTLS(t *testing.T) {
|
||||
|
||||
// generateTestCert creates a cert and a key used for testing only
|
||||
func generateTestCert(host string) error {
|
||||
certPath := getCertFile()
|
||||
keyPath := getKeyFile()
|
||||
certPath := getPublicCertFile()
|
||||
keyPath := getPrivateKeyFile()
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
return err
|
||||
|
Loading…
Reference in New Issue
Block a user