diff --git a/cmd/certs.go b/cmd/certs.go index a3fe83a56..4683ea494 100644 --- a/cmd/certs.go +++ b/cmd/certs.go @@ -31,7 +31,11 @@ func createCertsPath() error { if err != nil { return err } - return os.MkdirAll(certsPath, 0700) + if err := os.MkdirAll(certsPath, 0700); err != nil { + return err + } + rootCAsPath := filepath.Join(certsPath, globalMinioCertsCADir) + return os.MkdirAll(rootCAsPath, 0700) } // getCertsPath get certs path. @@ -62,6 +66,25 @@ func mustGetKeyFile() string { return filepath.Join(mustGetCertsPath(), globalMinioKeyFile) } +// mustGetCAFiles must get the list of the CA certificates stored in minio config dir +func mustGetCAFiles() (caCerts []string) { + CAsDir := filepath.Join(mustGetCertsPath(), globalMinioCertsCADir) + caFiles, _ := ioutil.ReadDir(CAsDir) + for _, cert := range caFiles { + caCerts = append(caCerts, filepath.Join(CAsDir, cert.Name())) + } + return +} + +// mustGetSystemCertPool returns empty cert pool in case of error (windows) +func mustGetSystemCertPool() *x509.CertPool { + pool, err := x509.SystemCertPool() + if err != nil { + return x509.NewCertPool() + } + return pool +} + // isCertFileExists verifies if cert file exists, returns true if // found, false otherwise. func isCertFileExists() bool { diff --git a/cmd/globals.go b/cmd/globals.go index 36702d1c1..649891901 100644 --- a/cmd/globals.go +++ b/cmd/globals.go @@ -17,6 +17,7 @@ package cmd import ( + "crypto/x509" "time" "github.com/fatih/color" @@ -33,6 +34,7 @@ const ( globalMinioConfigVersion = "9" globalMinioConfigDir = ".minio" globalMinioCertsDir = "certs" + globalMinioCertsCADir = "CAs" globalMinioCertFile = "public.crt" globalMinioKeyFile = "private.key" globalMinioConfigFile = "config.json" @@ -59,6 +61,9 @@ var ( // Peer communication struct globalS3Peers = s3Peers{} + // CA root certificates, a nil value means system certs pool will be used + globalRootCAs *x509.CertPool + // Add new variable global values here. ) diff --git a/cmd/net-rpc-client.go b/cmd/net-rpc-client.go index 5726316e4..246f44511 100644 --- a/cmd/net-rpc-client.go +++ b/cmd/net-rpc-client.go @@ -81,9 +81,10 @@ func (rpcClient *RPCClient) dialRPCClient() (*rpc.Client, error) { hostname, _, splitErr := net.SplitHostPort(rpcClient.node) if splitErr != nil { return nil, errors.New("Unable to parse RPC address <" + rpcClient.node + "> : " + splitErr.Error()) + } // ServerName in tls.Config needs to be specified to support SNI certificates - conn, err = tls.Dial("tcp", rpcClient.node, &tls.Config{ServerName: hostname}) + conn, err = tls.Dial("tcp", rpcClient.node, &tls.Config{ServerName: hostname, RootCAs: globalRootCAs}) } else { // Have a dial timeout with 3 secs. conn, err = net.DialTimeout("tcp", rpcClient.node, 3*time.Second) diff --git a/cmd/server-main.go b/cmd/server-main.go index 593708044..3c957b241 100644 --- a/cmd/server-main.go +++ b/cmd/server-main.go @@ -18,6 +18,7 @@ package cmd import ( "fmt" + "io/ioutil" "net" "net/http" "net/url" @@ -170,12 +171,34 @@ func finalizeEndpoints(tls bool, apiServer *http.Server) (endPoints []string) { return endPoints } +// 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 := mustGetCAFiles() + if len(caFiles) == 0 { + return + } + // Get system cert pool, and empty cert pool under Windows because it is not supported + globalRootCAs = mustGetSystemCertPool() + // Load custom root CAs for client requests + for _, caFile := range mustGetCAFiles() { + caCert, err := ioutil.ReadFile(caFile) + if err != nil { + fatalIf(err, "Unable to load a CA file") + } + globalRootCAs.AppendCertsFromPEM(caCert) + } +} + // initServerConfig initialize server config. func initServerConfig(c *cli.Context) { // Create certs path. err := createCertsPath() fatalIf(err, "Unable to create \"certs\" directory.") + // Load user supplied root CAs + loadRootCAs() + // When credentials inherited from the env, server cmd has to save them in the disk if os.Getenv("MINIO_ACCESS_KEY") != "" && os.Getenv("MINIO_SECRET_KEY") != "" { // Env credentials are already loaded in serverConfig, just save in the disk diff --git a/cmd/server_test.go b/cmd/server_test.go index 2139ed0d4..461d76f0a 100644 --- a/cmd/server_test.go +++ b/cmd/server_test.go @@ -20,6 +20,7 @@ import ( "bytes" "crypto/md5" "crypto/tls" + "crypto/x509" "encoding/base64" "encoding/hex" "encoding/xml" @@ -64,11 +65,20 @@ var _ = Suite(&TestSuiteCommon{serverType: "XL", signer: signerV4}) // Starting the Test server with temporary FS backend. func (s *TestSuiteCommon) SetUpSuite(c *C) { if s.secure { - s.testServer = StartTestTLSServer(c, s.serverType) + cert, key, err := generateTLSCertKey("127.0.0.1") + c.Assert(err, IsNil) + + s.testServer = StartTestTLSServer(c, s.serverType, cert, key) + + rootCAs := x509.NewCertPool() + rootCAs.AppendCertsFromPEM(cert) + tlsConfig := &tls.Config{ + RootCAs: rootCAs, + } + tlsConfig.BuildNameToCertificate() + s.transport = &http.Transport{ - TLSClientConfig: &tls.Config{ - InsecureSkipVerify: true, - }, + TLSClientConfig: tlsConfig, } } else { s.testServer = StartTestServer(c, s.serverType) diff --git a/cmd/test-utils_test.go b/cmd/test-utils_test.go index 79da5f100..77dea3e4c 100644 --- a/cmd/test-utils_test.go +++ b/cmd/test-utils_test.go @@ -19,16 +19,23 @@ package cmd import ( "bufio" "bytes" + "crypto/ecdsa" "crypto/hmac" + crand "crypto/rand" + "crypto/rsa" "crypto/sha1" "crypto/tls" + "crypto/x509" + "crypto/x509/pkix" "encoding/base64" "encoding/hex" "encoding/json" + "encoding/pem" "errors" "fmt" "io" "io/ioutil" + "math/big" "math/rand" "net" "net/http" @@ -233,66 +240,68 @@ func UnstartedTestServer(t TestErrHandler, instanceType string) TestServer { } -// Starts the test server and returns the TestServer with TLS configured instance. -func StartTestTLSServer(t TestErrHandler, instanceType string) TestServer { - serverKey := []byte(`-----BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAwD0kEmvtaHx+M0qJAY8zFEn6UpCIbZshNIoXOOr2S3XBEar9 -gtvTGpL73rPJroVcaTJxavsQJx6iD8E38t85rTsrlxEomAk5eKVK3WyplcUuqBgm -+KMYyyWxMXgYA3+AumEHiDg1SMIgrWFka2x+dSsqRb64tzWtD3LLy/Amq4cdiO1v -/v1rNEdqj+9G7G8leZSd8TNWZqebOwBPA4JiVtDDubemk4Qr4qYt3ChwNQiwq7Bt -RFR7EokO2an9XfT1NS71evikmGduhBLz3T+3QinxZDwb6SmNouYJkdqy6oPcWt0z -OXDgSPmY1NVlrujJ5JhtQTQxOs6mFVZ/82mn7wIDAQABAoIBAQCWiIoRntAGLM5J -7cjBHthZv+Az/RfH9F0ZHjU3Dc6VonzwD9x6NxbkzUpLxq9caPPHMIfdxQGOEI/J -FH1yQtiQTTBCGF6YR0jor06jey6EqCZz3I3Pzy9gDIDnguoS+ynbSJW0VodrFRCv -k/8lm4yexZFRkhpk5LRCz5rEdKZjU4kBgTBzeD6P1JbYKfAs49A99x9L42hExwfv -ppX/7ECbdMTQRVgDytOJpQR+mrrEHq30lxNZg0XngGm/4Rby8Ga6cfxmQbUrj5of -uA9TsQ6CAmTy6OqagLK4Rr9tSd4cjbBm2MCs2bDMYhzkhsveoFidsF1A9S3zSo/z -VJlqFtXpAoGBAP2ewImNRpaa0D1TWk/559XZJ64MMd4J0VK4cGzXPeBZ8WGVJxqF -PLl74AXG7/Iu18EWMHqTuMxlrkTKpF6KF9G5RCmAFi+UzVVspj9uvAk8SrFUA5P7 -c7Ahnmz44isD7OJ6sHUOP1d88dehODQdRAp5hX0h+rsTH3L6g3QRnEEdAoGBAMIK -8DJMsl2dmuuV4WPrgoqdnDnSmuC5YqxibJPJnZpgp19IxlIYRYtuUZjHIYx7OM/r -1X/dIvNqpFbvTnT9XFHWSyYMqal1+OY1Sg9i9E6YKuPAW2wccf3svhzehc98vJ0j -d7S81UpfKKWY+uD/wvOJdV1Pw7SoSvs5pmbFuKt7AoGAUY7ClblDsNy6CG6MhVl0 -7zT06KhtRNzdXn+HT8jr0gC6ecnwGDwuaetnABSYRsY/hY0wK8rjS3+LSf3sW6aG -wF+Whs301HpCiaz1zUI737BuyJWezPC4pDQ7cQmcGX8apz4TDqF1Rxob316t5zxe -DAxGHBZYPd6JZ30d1q5vFBUCgYEAvnaOHlE6Irm4ftW3TqS0lerulbMrYrmVKS/S -851KnWWR4+1C/QHmAV5fqV6Mh5/LvAr4nXEqBVP/y3VJxXuLSqjVSpvTTQsHLK/R -6hhvRVYHg1YkZpHlMiFW2m9xWKBPYs6ViUpw8XdGJoVqe7+QVAvwr47DwmgOcVm9 -A9O/2FECgYAgttnwo3gBxY0DJdfXBuqZCAa1MMErIxCaKw2Gm9JccnQW0fcuUcb3 -WSHJPyJ74ktk/QZGEmtKzAxVZ73t14dwHNNDid5CN2FyTIMCeWG5b2vM5NJe8KuQ -6cJePZj7ZkSvm2tkREdR37Oh2eZqGtaIbj6VTplvKUByWa/TEozMpQ== +// testServerCertPEM and testServerKeyPEM are generated by +// https://golang.org/src/crypto/tls/generate_cert.go +// $ go run generate_cert.go -ca --host 127.0.0.1 +// The generated certificate contains IP SAN, that way we don't need +// to enable InsecureSkipVerify in TLS config + +var testServerCertPEM = []byte(`-----BEGIN CERTIFICATE----- +MIIC9zCCAd+gAwIBAgIQV9ukx5ZahXeFygLXnR1WJTANBgkqhkiG9w0BAQsFADAS +MRAwDgYDVQQKEwdBY21lIENvMB4XDTE2MTExNTE1MDQxNFoXDTE3MTExNTE1MDQx +NFowEjEQMA4GA1UEChMHQWNtZSBDbzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC +AQoCggEBALLDXunOVIipgtvPVpQxIBTzUpceUtLYrNKTCtYfLtvFCNSPAa2W2EAi +mW2WgtU+Wd+jFN2leG+lvyEp2n1YzBN12oOzAZMf39K2j05aO6vN68Pf/3w/h2qz +PDYFWbWBMS1vC6RosfaQc4VFZCkz89M1aonwj0K8FjOHG4pu7rKnVkluC0c4+Xpu +8rB652chx/h6wFZwscVqFZIarTte8Z1tcbRhbvpdkOV749Wn5i2umlrKpBgsBv22 +8jn115BK7E2mN0rlCYPuN312bFFSSE85NaSdOp06TjD+2Rv9jPKizvnFN+2ADEje +nlCaYe3VRybKPZLrxPcqFQoCQsO+8ZsCAwEAAaNJMEcwDgYDVR0PAQH/BAQDAgKk +MBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1UdEwEB/wQFMAMBAf8wDwYDVR0RBAgw +BocEfwAAATANBgkqhkiG9w0BAQsFAAOCAQEAsmNCixmx+srB93+Jz5t90zzCJN4O +5RDWh7X7D54xtRRZ/t9HLLLFKW9EqhAM17xee3C4eNCicOqHP/htEvLwt3BWFmya +djvIUQYfymx4GtBTfMH4eC5vYGdxSuTVNe7JGHMpJjArNe4vIlUHyj2n12aGDHUf +NKEiTR2m+6hiKEyym74vhxGnl208OFa4tAMv3J7BjEObE37oy/vH/getE0HwG/EL +feE4D2Pp9XqeMCg/sPZPoQgBuq3QsL2RdL8DQywb/HrApdLyfmN0avV5tmbrm0cL +/0NUqCWjJIIKF0XxZbqlkQsYK5zpDJ36MFXO65aF3QGOMP1rlBD3d0S6kw== +-----END CERTIFICATE-----`) + +var testServerKeyPEM = []byte(`-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAssNe6c5UiKmC289WlDEgFPNSlx5S0tis0pMK1h8u28UI1I8B +rZbYQCKZbZaC1T5Z36MU3aV4b6W/ISnafVjME3Xag7MBkx/f0raPTlo7q83rw9// +fD+HarM8NgVZtYExLW8LpGix9pBzhUVkKTPz0zVqifCPQrwWM4cbim7usqdWSW4L +Rzj5em7ysHrnZyHH+HrAVnCxxWoVkhqtO17xnW1xtGFu+l2Q5Xvj1afmLa6aWsqk +GCwG/bbyOfXXkErsTaY3SuUJg+43fXZsUVJITzk1pJ06nTpOMP7ZG/2M8qLO+cU3 +7YAMSN6eUJph7dVHJso9kuvE9yoVCgJCw77xmwIDAQABAoIBAEE6CmLTd4LaHzZn +RBcUibk7Q5KCbQQkLYM0Rgr1G9ry3RL6D0mwtb1JIqSa+6gldROl5NIvM2/Bkajf +JasBAI3FPfM6GMP/KGMxW77iK823eGRjUkyavaWQOtMXRrF0r2X9k8jsrqrh8FTb +if2CyF/zqKkmTo+yI4Ovs7viWFR1IFBUHRwfYTTKnXA2q4S39knExALe1wWUkc4L +oOidewQ5IVCU3OQLWXP/beKoV/jw6+dOs5CYjXFsww6tdOsh+WkA9d3/rKPPtLdP +tDQiZtmI6FCYy/PdYqmzY0xg6dipGTDRfENUEx5SJu6HeSoUQUwEpQqnRxIu0iZl +FJ2ZziECgYEAzpdbIrFltGlSh7DIJfnQG86QeOw/nGluFTED9AweRAIzOYnUQCV3 +XCKMhFqmzsNpibEC1Cok92ZJk7bfsmPlx+qzL7BFpynA/gezxgc2wNZlWs8btPHi +s9h8hwL5If1FgAMD4E2iJtNgI/Kn5j8SDo/A5hAP1CXv12JRTB+pzlECgYEA3YQ6 +e2MLQYLDIcD5RoCrXOc9qo/l46uzo5laIuCKtd/IoOlip95kdgzpQC0/eenDLV9y +KLqAOZxZe+TVKtSOzVGy58FyD6L1oBJgfwuBku1x5ADRsIblq2uIOumDygRU0hMg +0tM3orIFGLyJU5hv6vC0x1ZdIGit0wP4ULhgKisCgYARJs3BLps0BD5+13V2eawG +cvrZnzuUv8gM6FncrBjjKo+YKlI91R54vsGNx3zr05tyfAixFqKlC4/2PIuL4vFT +zK99uRO/Uh8cuAT73uNz1RjrFiDFwANDTSjhiKSoZr+bZiSvPaLFuGzV7zJzUi8s +mFC6iQDXayLjbd00BbjyUQKBgHJD2R74sj+ywhFRR8S0brDXn5mx7LYKRfnoCvTe +uu6iZw2KFhfdwhibBF7UeF/c048+ItcbjTUqj4Y3PjZ/usHymMSvprSmLOnLUPd3 +6fjufsdMHN5gV2ybZYRuHEtC/LX4o//ccGB+T964smXqxiB81ePViuhC1xd4fsi0 +svZNAoGBALJOOR8ebtgATqc6jpnFxdqNmlwzAf/dH/jMZ6FZrttqIWiwxKvWaWPK +eHJtMmEPMustw/sv1GhDzwWmvgNFPzwEitPKW31m4EdbUCZFxPZ69/BtHTjXD3q3 +dP9W+omFXKQ36bVCB6xKmZH/ZVH5iQW0pdkD2JRnUPsDMNBeqmd6 -----END RSA PRIVATE KEY-----`) - serverPem := []byte(`-----BEGIN CERTIFICATE----- -MIID2zCCAsOgAwIBAgIJALPniQGEq3KtMA0GCSqGSIb3DQEBCwUAMIGDMQswCQYD -VQQGEwJJTjESMBAGA1UECAwJS2FybmF0YWthMRIwEAYDVQQHDAlCZW5nYWx1cnUx -FzAVBgNVBAoMDk1pbmlvVW5pdFRlc3RzMREwDwYDVQQLDAhTU0xUZXN0czEgMB4G -CSqGSIb3DQEJARYRc3NsdGVzdHNAbWluaW8uaW8wHhcNMTYxMDI0MDk1ODQzWhcN -MjYxMDIyMDk1ODQzWjCBgzELMAkGA1UEBhMCSU4xEjAQBgNVBAgMCUthcm5hdGFr -YTESMBAGA1UEBwwJQmVuZ2FsdXJ1MRcwFQYDVQQKDA5NaW5pb1VuaXRUZXN0czER -MA8GA1UECwwIU1NMVGVzdHMxIDAeBgkqhkiG9w0BCQEWEXNzbHRlc3RzQG1pbmlv -LmlvMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwD0kEmvtaHx+M0qJ -AY8zFEn6UpCIbZshNIoXOOr2S3XBEar9gtvTGpL73rPJroVcaTJxavsQJx6iD8E3 -8t85rTsrlxEomAk5eKVK3WyplcUuqBgm+KMYyyWxMXgYA3+AumEHiDg1SMIgrWFk -a2x+dSsqRb64tzWtD3LLy/Amq4cdiO1v/v1rNEdqj+9G7G8leZSd8TNWZqebOwBP -A4JiVtDDubemk4Qr4qYt3ChwNQiwq7BtRFR7EokO2an9XfT1NS71evikmGduhBLz -3T+3QinxZDwb6SmNouYJkdqy6oPcWt0zOXDgSPmY1NVlrujJ5JhtQTQxOs6mFVZ/ -82mn7wIDAQABo1AwTjAdBgNVHQ4EFgQUv++gaIEUL0sboDER+4KPpiU27FMwHwYD -VR0jBBgwFoAUv++gaIEUL0sboDER+4KPpiU27FMwDAYDVR0TBAUwAwEB/zANBgkq -hkiG9w0BAQsFAAOCAQEAHumbrFEBhN0EWsjZZB/VkArE/owBg7djvNetYE/rEWSV -/dwysQgkTpGrCyfmzSwhsX++gr5a5qh+HAF0Ygufd5OIk/kn9X3pz66Kaq4TYdFO -hc/DUD7wwY3/Mfi9lhT6lKSfMu69D3FuiI+xtUJ7CU8Fhr2ua6UB7e/2inYzsJDN -WYMzrkLMasQNzNWiz3Tditxj1WuuRe9mgXbbBHT03udUyuLi+4ZiOuw6CiJL4Pfk -PAKMo7QWaxAectHZsxvcfH9uYOIuv1AwDUQBA+jhADvLh55epFq0DdJ057+QKItL -vtKIzIB9HcGDFfBvIq+WlxYlQPSIkeq2z1iZaTl11g== ------END CERTIFICATE-----`) +// Starts the test server and returns the TestServer with TLS configured instance. +func StartTestTLSServer(t TestErrHandler, instanceType string, cert, key []byte) TestServer { // Fetch TLS key and pem files from test-data/ directory. // dir, _ := os.Getwd() // testDataDir := filepath.Join(filepath.Dir(dir), "test-data") // // pemFile := filepath.Join(testDataDir, "server.pem") // keyFile := filepath.Join(testDataDir, "server.key") - cer, err := tls.X509KeyPair(serverPem, serverKey) + cer, err := tls.X509KeyPair(cert, key) if err != nil { t.Fatalf("Failed to load certificate: %v", err) } @@ -2115,3 +2124,97 @@ func StartTestS3PeerRPCServer(t TestErrHandler) (TestServer, []string) { testRPCServer.Server = httptest.NewServer(muxRouter) return testRPCServer, fsDirs } + +// generateTLSCertKey creates valid key/cert with registered DNS or IP address +// depending on the passed parameter. That way, we can use tls config without +// passing InsecureSkipVerify flag. This code is a simplified version of +// https://golang.org/src/crypto/tls/generate_cert.go +func generateTLSCertKey(host string) ([]byte, []byte, error) { + validFor := 365 * 24 * time.Hour + rsaBits := 2048 + + if len(host) == 0 { + return nil, nil, fmt.Errorf("Missing host parameter") + } + + publicKey := func(priv interface{}) interface{} { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &k.PublicKey + case *ecdsa.PrivateKey: + return &k.PublicKey + default: + return nil + } + } + + pemBlockForKey := func(priv interface{}) *pem.Block { + switch k := priv.(type) { + case *rsa.PrivateKey: + return &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(k)} + case *ecdsa.PrivateKey: + b, err := x509.MarshalECPrivateKey(k) + if err != nil { + fmt.Fprintf(os.Stderr, "Unable to marshal ECDSA private key: %v", err) + os.Exit(2) + } + return &pem.Block{Type: "EC PRIVATE KEY", Bytes: b} + default: + return nil + } + } + + var priv interface{} + var err error + priv, err = rsa.GenerateKey(crand.Reader, rsaBits) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate private key: %s", err) + } + + notBefore := time.Now() + notAfter := notBefore.Add(validFor) + + serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128) + serialNumber, err := crand.Int(crand.Reader, serialNumberLimit) + if err != nil { + return nil, nil, fmt.Errorf("failed to generate serial number: %s", err) + } + + template := x509.Certificate{ + SerialNumber: serialNumber, + Subject: pkix.Name{ + Organization: []string{"Acme Co"}, + }, + NotBefore: notBefore, + NotAfter: notAfter, + + KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature, + ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}, + BasicConstraintsValid: true, + } + + hosts := strings.Split(host, ",") + for _, h := range hosts { + if ip := net.ParseIP(h); ip != nil { + template.IPAddresses = append(template.IPAddresses, ip) + } else { + template.DNSNames = append(template.DNSNames, h) + } + } + + template.IsCA = true + template.KeyUsage |= x509.KeyUsageCertSign + + derBytes, err := x509.CreateCertificate(crand.Reader, &template, &template, publicKey(priv), priv) + if err != nil { + return nil, nil, fmt.Errorf("Failed to create certificate: %s", err) + } + + certOut := bytes.NewBuffer([]byte{}) + pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes}) + + keyOut := bytes.NewBuffer([]byte{}) + pem.Encode(keyOut, pemBlockForKey(priv)) + + return certOut.Bytes(), keyOut.Bytes(), nil +} diff --git a/docs/configure-minio-with-tls.md b/docs/configure-minio-with-tls.md new file mode 100644 index 000000000..b157310b6 --- /dev/null +++ b/docs/configure-minio-with-tls.md @@ -0,0 +1,50 @@ +# How to secure access to your Minio server with TLS [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/minio/minio?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +In this document, we will show how to configure your Minio servers with TLS certificates. + +## 1. Prerequisites + +* Download Minio server from [here](https://docs.minio.io/docs/minio) + +## 2. Generate TLS certificate + +Minio supports only key/certificate in PEM format. + +### With Letsencrypt + +Please explore [here](https://docs.minio.io/docs/generate-let-s-encypt-certificate-using-concert-for-minio) + +### With generate_cert.go (self-signed certificate) + +You need to download [generate_cert.go](https://golang.org/src/crypto/tls/generate_cert.go?m=text) which is a simple go tool for generating self-signed certificates but works for the most of cases. + +`generate_cert.go` already provides SAN certificates with DNS and IP entries: + +```sh + go run generate_cert.go -ca --host "10.10.0.3" +``` + +### With OpenSSL: + +Generate the private key: +```sh +openssl genrsa -out private.key 1024 +``` + +Generate the self-signed certificate: +```sh +openssl req -new -x509 -days 3650 -key private.key -out public.crt -subj "/C=country/ST=state/L=location/O=organization/CN=domain" +``` + +## 3. Configure Minio with the generated certificate + +To make Minio aware about your generated key and certificate, you will need to put them under `certs` directory in your Minio config path (usually ~/.minio) using the names of `private.key` and `public.crt` for key and certificate files respectively. + +## 4. Install third parties CAs + +Minio can be configured to connect to other servers, whether Minio nodes or servers like NATs, Redis... If these servers use certificates that are not registerd in one of the known certificates authorities, you can make your Minio trusts them by putting these certificates under `certs/CAs/` in your Minio config path. + + +# Explore Further +* [Minio Quickstart Guide](https://docs.minio.io/docs/minio-quickstart-guide) +* [Minio Client Complete Guide](https://docs.minio.io/docs/minio-client-complete-guide)