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
|
||||
}
|
||||
err = d.deserialize(f, d.Saved)
|
||||
if err != nil {
|
||||
if err != nil && err != io.EOF {
|
||||
logger.LogIf(ctx, err)
|
||||
}
|
||||
f.Close()
|
||||
|
|
|
@ -43,8 +43,10 @@ func printGatewayStartupMessage(apiEndPoints []string, backendType string) {
|
|||
|
||||
// SSL is configured reads certification chain, prints
|
||||
// authority and expiry.
|
||||
if globalIsSSL {
|
||||
printCertificateMsg(globalPublicCerts)
|
||||
if color.IsTerminal() && !globalCLIContext.Anonymous {
|
||||
if globalIsSSL {
|
||||
printCertificateMsg(globalPublicCerts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ import (
|
|||
"strings"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/minio/minio/cmd/config"
|
||||
"github.com/minio/minio/cmd/logger"
|
||||
color "github.com/minio/minio/pkg/color"
|
||||
xnet "github.com/minio/minio/pkg/net"
|
||||
|
@ -131,8 +132,10 @@ func printStartupMessage(apiEndpoints []string) {
|
|||
|
||||
// SSL is configured reads certification chain, prints
|
||||
// authority and expiry.
|
||||
if globalIsSSL {
|
||||
printCertificateMsg(globalPublicCerts)
|
||||
if color.IsTerminal() && !globalCLIContext.Anonymous {
|
||||
if globalIsSSL {
|
||||
printCertificateMsg(globalPublicCerts)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -298,25 +301,9 @@ func printCacheStorageInfo(storageInfo CacheStorageInfo) {
|
|||
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.
|
||||
func printCertificateMsg(certs []*x509.Certificate) {
|
||||
logStartupMessage(getCertificateChainMsg(certs))
|
||||
for _, cert := range certs {
|
||||
logStartupMessage(config.CertificateText(cert))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,16 +17,11 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/minio/minio/pkg/color"
|
||||
"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.
|
||||
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"}
|
||||
|
|
|
@ -36,7 +36,7 @@ type Certs struct {
|
|||
loadCert LoadX509KeyPairFunc
|
||||
|
||||
// points to the latest certificate.
|
||||
cert tls.Certificate
|
||||
cert *tls.Certificate
|
||||
|
||||
// internal param to track for events, also
|
||||
// used to close the watcher.
|
||||
|
@ -89,12 +89,13 @@ func checkSymlink(file string) (bool, error) {
|
|||
// watchSymlinks reloads symlinked files since fsnotify cannot watch
|
||||
// on symbolic links.
|
||||
func (c *Certs) watchSymlinks() (err error) {
|
||||
c.Lock()
|
||||
c.cert, err = c.loadCert(c.certFile, c.keyFile)
|
||||
c.Unlock()
|
||||
cert, err := c.loadCert(c.certFile, c.keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
c.cert = &cert
|
||||
c.Unlock()
|
||||
go func() {
|
||||
for {
|
||||
select {
|
||||
|
@ -107,7 +108,7 @@ func (c *Certs) watchSymlinks() (err error) {
|
|||
continue
|
||||
}
|
||||
c.Lock()
|
||||
c.cert = cert
|
||||
c.cert = &cert
|
||||
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 {
|
||||
return err
|
||||
}
|
||||
cert, err := c.loadCert(c.certFile, c.keyFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
c.Lock()
|
||||
c.cert, err = c.loadCert(c.certFile, c.keyFile)
|
||||
c.cert = &cert
|
||||
c.Unlock()
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -162,7 +167,7 @@ func (c *Certs) run() {
|
|||
continue
|
||||
}
|
||||
c.Lock()
|
||||
c.cert = cert
|
||||
c.cert = &cert
|
||||
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) {
|
||||
c.RLock()
|
||||
defer c.RUnlock()
|
||||
return &c.cert, nil
|
||||
return c.cert, nil
|
||||
}
|
||||
|
||||
// Stop tells loader to stop watching for changes to the
|
||||
|
|
Loading…
Reference in New Issue