mirror of https://github.com/minio/minio.git
print proper certinfo on console when starting up (#9479)
also potentially fix a race in certs.go implementation while accessing tls.Certificate concurrently.
This commit is contained in:
parent
9a547dcbfb
commit
5205c9591f
|
@ -0,0 +1,92 @@
|
||||||
|
/*
|
||||||
|
* MinIO Cloud Storage, (C) 2020 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 config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/x509"
|
||||||
|
"crypto/x509/pkix"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
color "github.com/minio/minio/pkg/color"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Extra ASN1 OIDs that we may need to handle
|
||||||
|
var (
|
||||||
|
oidEmailAddress = []int{1, 2, 840, 113549, 1, 9, 1}
|
||||||
|
)
|
||||||
|
|
||||||
|
// printName prints the fields of a distinguished name, which include such
|
||||||
|
// things as its common name and locality.
|
||||||
|
func printName(names []pkix.AttributeTypeAndValue, buf *strings.Builder) []string {
|
||||||
|
values := []string{}
|
||||||
|
for _, name := range names {
|
||||||
|
oid := name.Type
|
||||||
|
if len(oid) == 4 && oid[0] == 2 && oid[1] == 5 && oid[2] == 4 {
|
||||||
|
switch oid[3] {
|
||||||
|
case 3:
|
||||||
|
values = append(values, fmt.Sprintf("CN=%s", name.Value))
|
||||||
|
case 6:
|
||||||
|
values = append(values, fmt.Sprintf("C=%s", name.Value))
|
||||||
|
case 8:
|
||||||
|
values = append(values, fmt.Sprintf("ST=%s", name.Value))
|
||||||
|
case 10:
|
||||||
|
values = append(values, fmt.Sprintf("O=%s", name.Value))
|
||||||
|
case 11:
|
||||||
|
values = append(values, fmt.Sprintf("OU=%s", name.Value))
|
||||||
|
default:
|
||||||
|
values = append(values, fmt.Sprintf("UnknownOID=%s", name.Type.String()))
|
||||||
|
}
|
||||||
|
} else if oid.Equal(oidEmailAddress) {
|
||||||
|
values = append(values, fmt.Sprintf("emailAddress=%s", name.Value))
|
||||||
|
} else {
|
||||||
|
values = append(values, fmt.Sprintf("UnknownOID=%s", name.Type.String()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(values) > 0 {
|
||||||
|
buf.WriteString(values[0])
|
||||||
|
for i := 1; i < len(values); i++ {
|
||||||
|
buf.WriteString(", " + values[i])
|
||||||
|
}
|
||||||
|
buf.WriteString("\n")
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
}
|
||||||
|
|
||||||
|
// CertificateText returns a human-readable string representation
|
||||||
|
// of the certificate cert. The format is similar to the OpenSSL
|
||||||
|
// way of printing certificates (not identical).
|
||||||
|
func CertificateText(cert *x509.Certificate) string {
|
||||||
|
var buf strings.Builder
|
||||||
|
|
||||||
|
buf.WriteString(color.Blue("\nCertificate:\n"))
|
||||||
|
if cert.SignatureAlgorithm != x509.UnknownSignatureAlgorithm {
|
||||||
|
buf.WriteString(color.Blue("%4sSignature Algorithm: ", "") + color.Bold(fmt.Sprintf("%s\n", cert.SignatureAlgorithm)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Issuer information
|
||||||
|
buf.WriteString(color.Blue("%4sIssuer: ", ""))
|
||||||
|
printName(cert.Issuer.Names, &buf)
|
||||||
|
|
||||||
|
// Validity information
|
||||||
|
buf.WriteString(color.Blue("%4sValidity\n", ""))
|
||||||
|
buf.WriteString(color.Bold(fmt.Sprintf("%8sNot Before: %s\n", "", cert.NotBefore.Format(http.TimeFormat))))
|
||||||
|
buf.WriteString(color.Bold(fmt.Sprintf("%8sNot After : %s\n", "", cert.NotAfter.Format(http.TimeFormat))))
|
||||||
|
|
||||||
|
return buf.String()
|
||||||
|
}
|
|
@ -206,7 +206,7 @@ func (d *dataUpdateTracker) load(ctx context.Context, drives ...string) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
err = d.deserialize(f, d.Saved)
|
err = d.deserialize(f, d.Saved)
|
||||||
if err != nil {
|
if err != nil && err != io.EOF {
|
||||||
logger.LogIf(ctx, err)
|
logger.LogIf(ctx, err)
|
||||||
}
|
}
|
||||||
f.Close()
|
f.Close()
|
||||||
|
|
|
@ -43,10 +43,12 @@ func printGatewayStartupMessage(apiEndPoints []string, backendType string) {
|
||||||
|
|
||||||
// SSL is configured reads certification chain, prints
|
// SSL is configured reads certification chain, prints
|
||||||
// authority and expiry.
|
// authority and expiry.
|
||||||
|
if color.IsTerminal() && !globalCLIContext.Anonymous {
|
||||||
if globalIsSSL {
|
if globalIsSSL {
|
||||||
printCertificateMsg(globalPublicCerts)
|
printCertificateMsg(globalPublicCerts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Prints common server startup message. Prints credential, region and browser access.
|
// Prints common server startup message. Prints credential, region and browser access.
|
||||||
func printGatewayCommonMsg(apiEndpoints []string) {
|
func printGatewayCommonMsg(apiEndpoints []string) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
humanize "github.com/dustin/go-humanize"
|
humanize "github.com/dustin/go-humanize"
|
||||||
|
"github.com/minio/minio/cmd/config"
|
||||||
"github.com/minio/minio/cmd/logger"
|
"github.com/minio/minio/cmd/logger"
|
||||||
color "github.com/minio/minio/pkg/color"
|
color "github.com/minio/minio/pkg/color"
|
||||||
xnet "github.com/minio/minio/pkg/net"
|
xnet "github.com/minio/minio/pkg/net"
|
||||||
|
@ -131,10 +132,12 @@ func printStartupMessage(apiEndpoints []string) {
|
||||||
|
|
||||||
// SSL is configured reads certification chain, prints
|
// SSL is configured reads certification chain, prints
|
||||||
// authority and expiry.
|
// authority and expiry.
|
||||||
|
if color.IsTerminal() && !globalCLIContext.Anonymous {
|
||||||
if globalIsSSL {
|
if globalIsSSL {
|
||||||
printCertificateMsg(globalPublicCerts)
|
printCertificateMsg(globalPublicCerts)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Returns true if input is not IPv4, false if it is.
|
// Returns true if input is not IPv4, false if it is.
|
||||||
func isNotIPv4(host string) bool {
|
func isNotIPv4(host string) bool {
|
||||||
|
@ -298,25 +301,9 @@ func printCacheStorageInfo(storageInfo CacheStorageInfo) {
|
||||||
logStartupMessage(msg)
|
logStartupMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prints certificate expiry date warning
|
|
||||||
func getCertificateChainMsg(certs []*x509.Certificate) string {
|
|
||||||
msg := color.Blue("\nCertificate expiry info:\n")
|
|
||||||
totalCerts := len(certs)
|
|
||||||
var expiringCerts int
|
|
||||||
for i := totalCerts - 1; i >= 0; i-- {
|
|
||||||
cert := certs[i]
|
|
||||||
if cert.NotAfter.Before(UTCNow().Add(globalMinioCertExpireWarnDays)) {
|
|
||||||
expiringCerts++
|
|
||||||
msg += fmt.Sprintf(color.Bold("#%d %s will expire on %s\n"), expiringCerts, cert.Subject.CommonName, cert.NotAfter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if expiringCerts > 0 {
|
|
||||||
return msg
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prints the certificate expiry message.
|
// Prints the certificate expiry message.
|
||||||
func printCertificateMsg(certs []*x509.Certificate) {
|
func printCertificateMsg(certs []*x509.Certificate) {
|
||||||
logStartupMessage(getCertificateChainMsg(certs))
|
for _, cert := range certs {
|
||||||
|
logStartupMessage(config.CertificateText(cert))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,16 +17,11 @@
|
||||||
package cmd
|
package cmd
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
|
||||||
"crypto/x509/pkix"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/minio/minio/pkg/color"
|
|
||||||
"github.com/minio/minio/pkg/madmin"
|
"github.com/minio/minio/pkg/madmin"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -42,55 +37,6 @@ func TestStorageInfoMsg(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tests if certificate expiry warning will be printed
|
|
||||||
func TestCertificateExpiryInfo(t *testing.T) {
|
|
||||||
// given
|
|
||||||
var expiredDate = time.Now().Add(time.Hour * 24 * (30 - 1)) // 29 days.
|
|
||||||
|
|
||||||
var fakeCerts = []*x509.Certificate{
|
|
||||||
{
|
|
||||||
NotAfter: expiredDate,
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: "Test cert",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
expectedMsg := color.Blue("\nCertificate expiry info:\n") +
|
|
||||||
color.Bold(fmt.Sprintf("#1 Test cert will expire on %s\n", expiredDate))
|
|
||||||
|
|
||||||
// When
|
|
||||||
msg := getCertificateChainMsg(fakeCerts)
|
|
||||||
|
|
||||||
// Then
|
|
||||||
if msg != expectedMsg {
|
|
||||||
t.Fatalf("Expected message was: %s, got: %s", expectedMsg, msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests if certificate expiry warning will not be printed if certificate not expired
|
|
||||||
func TestCertificateNotExpired(t *testing.T) {
|
|
||||||
// given
|
|
||||||
var expiredDate = time.Now().Add(time.Hour * 24 * (30 + 1)) // 31 days.
|
|
||||||
|
|
||||||
var fakeCerts = []*x509.Certificate{
|
|
||||||
{
|
|
||||||
NotAfter: expiredDate,
|
|
||||||
Subject: pkix.Name{
|
|
||||||
CommonName: "Test cert",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
// when
|
|
||||||
msg := getCertificateChainMsg(fakeCerts)
|
|
||||||
|
|
||||||
// then
|
|
||||||
if msg != "" {
|
|
||||||
t.Fatalf("Expected empty message was: %s", msg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tests stripping standard ports from apiEndpoints.
|
// Tests stripping standard ports from apiEndpoints.
|
||||||
func TestStripStandardPorts(t *testing.T) {
|
func TestStripStandardPorts(t *testing.T) {
|
||||||
apiEndpoints := []string{"http://127.0.0.1:9000", "http://127.0.0.2:80", "https://127.0.0.3:443"}
|
apiEndpoints := []string{"http://127.0.0.1:9000", "http://127.0.0.2:80", "https://127.0.0.3:443"}
|
||||||
|
|
|
@ -36,7 +36,7 @@ type Certs struct {
|
||||||
loadCert LoadX509KeyPairFunc
|
loadCert LoadX509KeyPairFunc
|
||||||
|
|
||||||
// points to the latest certificate.
|
// points to the latest certificate.
|
||||||
cert tls.Certificate
|
cert *tls.Certificate
|
||||||
|
|
||||||
// internal param to track for events, also
|
// internal param to track for events, also
|
||||||
// used to close the watcher.
|
// used to close the watcher.
|
||||||
|
@ -89,12 +89,13 @@ func checkSymlink(file string) (bool, error) {
|
||||||
// watchSymlinks reloads symlinked files since fsnotify cannot watch
|
// watchSymlinks reloads symlinked files since fsnotify cannot watch
|
||||||
// on symbolic links.
|
// on symbolic links.
|
||||||
func (c *Certs) watchSymlinks() (err error) {
|
func (c *Certs) watchSymlinks() (err error) {
|
||||||
c.Lock()
|
cert, err := c.loadCert(c.certFile, c.keyFile)
|
||||||
c.cert, err = c.loadCert(c.certFile, c.keyFile)
|
|
||||||
c.Unlock()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
c.Lock()
|
||||||
|
c.cert = &cert
|
||||||
|
c.Unlock()
|
||||||
go func() {
|
go func() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
|
@ -107,7 +108,7 @@ func (c *Certs) watchSymlinks() (err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Lock()
|
c.Lock()
|
||||||
c.cert = cert
|
c.cert = &cert
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -138,8 +139,12 @@ func (c *Certs) watch() (err error) {
|
||||||
if err = notify.Watch(filepath.Dir(c.keyFile), c.e, eventWrite...); err != nil {
|
if err = notify.Watch(filepath.Dir(c.keyFile), c.e, eventWrite...); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
cert, err := c.loadCert(c.certFile, c.keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
c.Lock()
|
c.Lock()
|
||||||
c.cert, err = c.loadCert(c.certFile, c.keyFile)
|
c.cert = &cert
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -162,7 +167,7 @@ func (c *Certs) run() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
c.Lock()
|
c.Lock()
|
||||||
c.cert = cert
|
c.cert = &cert
|
||||||
c.Unlock()
|
c.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -177,7 +182,7 @@ type GetCertificateFunc func(hello *tls.ClientHelloInfo) (*tls.Certificate, erro
|
||||||
func (c *Certs) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
func (c *Certs) GetCertificate(hello *tls.ClientHelloInfo) (*tls.Certificate, error) {
|
||||||
c.RLock()
|
c.RLock()
|
||||||
defer c.RUnlock()
|
defer c.RUnlock()
|
||||||
return &c.cert, nil
|
return c.cert, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop tells loader to stop watching for changes to the
|
// Stop tells loader to stop watching for changes to the
|
||||||
|
|
Loading…
Reference in New Issue