mirror of
https://github.com/minio/minio.git
synced 2025-11-07 12:52:58 -05:00
Display SSL expiry warnings (#2925)
This commit is contained in:
committed by
Harshavardhana
parent
0320a77dc0
commit
c03ce0f74a
49
cmd/certs.go
49
cmd/certs.go
@@ -17,8 +17,13 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// createCertsPath create certs path.
|
||||
@@ -87,3 +92,47 @@ func isSSL() bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func readCertificateChain() ([]*x509.Certificate, error) {
|
||||
certPath := filepath.Join(mustGetCertsPath(), globalMinioCertFile)
|
||||
file, err := os.Open(certPath)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not open certificate for reading")
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
bytes, err2 := ioutil.ReadAll(file)
|
||||
|
||||
if err2 != nil {
|
||||
return nil, errors.Wrapf(err2, "Could not read certificate contents")
|
||||
}
|
||||
|
||||
return parseCertificateChain(bytes)
|
||||
}
|
||||
|
||||
// Parses certificate chain
|
||||
func parseCertificateChain(bytes []byte) ([]*x509.Certificate, error) {
|
||||
var certs []*x509.Certificate
|
||||
var block *pem.Block
|
||||
current := bytes
|
||||
|
||||
for len(current) > 0 {
|
||||
block, current = pem.Decode(current)
|
||||
|
||||
if block == nil {
|
||||
return nil, errors.New("Could not PEM block")
|
||||
}
|
||||
|
||||
cert, err := x509.ParseCertificate(block.Bytes)
|
||||
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "Could not parse certficiate")
|
||||
}
|
||||
|
||||
certs = append(certs, cert)
|
||||
|
||||
}
|
||||
|
||||
return certs, nil
|
||||
}
|
||||
|
||||
@@ -58,3 +58,54 @@ func TestGetFiles(t *testing.T) {
|
||||
t.Errorf("KeyFile does not contain %s", globalMinioKeyFile)
|
||||
}
|
||||
}
|
||||
|
||||
// Parses .crt file contents
|
||||
func TestParseCertificateChain(t *testing.T) {
|
||||
// given
|
||||
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-----`
|
||||
|
||||
// when
|
||||
certs, err := parseCertificateChain([]byte(cert))
|
||||
|
||||
// then
|
||||
if err != nil {
|
||||
t.Fatalf("Could not parse certificate: %s", err)
|
||||
}
|
||||
|
||||
if len(certs) != 1 {
|
||||
t.Fatalf("Expected number of certificates in chain was 1, actual: %d", len(certs))
|
||||
}
|
||||
|
||||
if certs[0].Subject.CommonName != "Minio" {
|
||||
t.Fatalf("Expected Subject.CommonName was Minio, actual: %s", certs[0].Subject.CommonName)
|
||||
}
|
||||
}
|
||||
|
||||
// Parses invalid .crt file contents and returns error
|
||||
func TestParseInvalidCertificateChain(t *testing.T) {
|
||||
// given
|
||||
cert := `This is now valid certificate`
|
||||
|
||||
// when
|
||||
_, err := parseCertificateChain([]byte(cert))
|
||||
|
||||
// then
|
||||
if err == nil {
|
||||
t.Fatalf("Expected error but none occured")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,12 +30,13 @@ const (
|
||||
|
||||
// minio configuration related constants.
|
||||
const (
|
||||
globalMinioConfigVersion = "9"
|
||||
globalMinioConfigDir = ".minio"
|
||||
globalMinioCertsDir = "certs"
|
||||
globalMinioCertFile = "public.crt"
|
||||
globalMinioKeyFile = "private.key"
|
||||
globalMinioConfigFile = "config.json"
|
||||
globalMinioConfigVersion = "9"
|
||||
globalMinioConfigDir = ".minio"
|
||||
globalMinioCertsDir = "certs"
|
||||
globalMinioCertFile = "public.crt"
|
||||
globalMinioKeyFile = "private.key"
|
||||
globalMinioConfigFile = "config.json"
|
||||
globalMinioCertExpireWarnDays = 30
|
||||
// Add new global values here.
|
||||
)
|
||||
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
humanize "github.com/dustin/go-humanize"
|
||||
"github.com/minio/mc/pkg/console"
|
||||
@@ -46,10 +48,15 @@ func printStartupMessage(endPoints []string) {
|
||||
printServerCommonMsg(endPoints)
|
||||
printCLIAccessMsg(endPoints[0])
|
||||
printObjectAPIMsg()
|
||||
|
||||
objAPI := newObjectLayerFn()
|
||||
if objAPI != nil {
|
||||
printStorageInfo(objAPI.StorageInfo())
|
||||
}
|
||||
|
||||
if certs, err := readCertificateChain(); err == nil {
|
||||
printCertificateMsg(certs)
|
||||
}
|
||||
}
|
||||
|
||||
// Prints common server startup message. Prints credential, region and browser access.
|
||||
@@ -147,3 +154,28 @@ func printStorageInfo(storageInfo StorageInfo) {
|
||||
console.Println()
|
||||
console.Println(getStorageInfoMsg(storageInfo))
|
||||
}
|
||||
|
||||
// Prints certificate expiry date warning
|
||||
func getCertificateChainMsg(certs []*x509.Certificate) string {
|
||||
msg := colorBlue("\nCertificate expiry info:\n")
|
||||
totalCerts := len(certs)
|
||||
var expiringCerts int
|
||||
|
||||
for i := totalCerts - 1; i >= 0; i-- {
|
||||
cert := certs[i]
|
||||
|
||||
if cert.NotAfter.Before(time.Now().Add(time.Hour * 24 * globalMinioCertExpireWarnDays)) {
|
||||
expiringCerts++
|
||||
msg += fmt.Sprintf(colorBold("#%d %s will expire on %s\n"), expiringCerts, cert.Subject.CommonName, cert.NotAfter)
|
||||
}
|
||||
}
|
||||
|
||||
if expiringCerts > 0 {
|
||||
return msg
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func printCertificateMsg(certs []*x509.Certificate) {
|
||||
console.Println(getCertificateChainMsg(certs))
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/x509"
|
||||
"crypto/x509/pkix"
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Tests if we generate storage info.
|
||||
@@ -39,3 +43,52 @@ func TestStorageInfoMsg(t *testing.T) {
|
||||
t.Fatal("Empty message string is not implemented", msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests if certificate expiry warning will be printed
|
||||
func TestCertificateExpiryInfo(t *testing.T) {
|
||||
// given
|
||||
var expiredDate = time.Now().Add(time.Hour * 24 * (globalMinioCertExpireWarnDays - 1))
|
||||
|
||||
var fakeCerts = []*x509.Certificate{
|
||||
&x509.Certificate{
|
||||
NotAfter: expiredDate,
|
||||
Subject: pkix.Name{
|
||||
CommonName: "Test cert",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
expectedMsg := colorBlue("\nCertificate expiry info:\n") +
|
||||
colorBold(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 * (globalMinioCertExpireWarnDays + 1))
|
||||
|
||||
var fakeCerts = []*x509.Certificate{
|
||||
&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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user