feat(ssl): enable mTLS certificates [EE-2617] (#6612)

pull/6648/head^2
Marcelo Rydel 2022-04-07 11:32:00 -03:00 committed by GitHub
parent f9f937f844
commit dff74f0823
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 45 additions and 64 deletions

View File

@ -18,8 +18,6 @@ const (
defaultHTTPDisabled = "false"
defaultHTTPEnabled = "false"
defaultSSL = "false"
defaultSSLCertPath = "/certs/portainer.crt"
defaultSSLKeyPath = "/certs/portainer.key"
defaultBaseURL = "/"
defaultSecretKeyName = "portainer"
)

View File

@ -15,8 +15,6 @@ const (
defaultHTTPDisabled = "false"
defaultHTTPEnabled = "false"
defaultSSL = "false"
defaultSSLCertPath = "C:\\certs\\portainer.crt"
defaultSSLKeyPath = "C:\\certs\\portainer.key"
defaultSnapshotInterval = "5m"
defaultBaseURL = "/"
defaultSecretKeyName = "portainer"

View File

@ -1,41 +1,19 @@
package main
import (
"fmt"
"log"
"strings"
"github.com/sirupsen/logrus"
)
type portainerFormatter struct {
logrus.TextFormatter
}
func (f *portainerFormatter) Format(entry *logrus.Entry) ([]byte, error) {
var levelColor int
switch entry.Level {
case logrus.DebugLevel, logrus.TraceLevel:
levelColor = 31 // gray
case logrus.WarnLevel:
levelColor = 33 // yellow
case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
levelColor = 31 // red
default:
levelColor = 36 // blue
}
return []byte(fmt.Sprintf("\x1b[%dm%s\x1b[0m %s %s\n", levelColor, strings.ToUpper(entry.Level.String()), entry.Time.Format(f.TimestampFormat), entry.Message)), nil
}
func configureLogger() {
logger := logrus.New() // logger is to implicitly substitute stdlib's log
log.SetOutput(logger.Writer())
formatter := &logrus.TextFormatter{DisableTimestamp: true, DisableLevelTruncation: true}
formatterLogrus := &portainerFormatter{logrus.TextFormatter{DisableTimestamp: false, DisableLevelTruncation: true, TimestampFormat: "2006/01/02 15:04:05", FullTimestamp: true}}
formatter := &logrus.TextFormatter{DisableTimestamp: false, DisableLevelTruncation: true}
logger.SetFormatter(formatter)
logrus.SetFormatter(formatterLogrus)
logrus.SetFormatter(formatter)
logger.SetLevel(logrus.DebugLevel)
logrus.SetLevel(logrus.DebugLevel)

View File

@ -208,7 +208,7 @@ func initGitService() portainer.GitService {
return git.NewService()
}
func initSSLService(addr, dataPath, certPath, keyPath string, fileService portainer.FileService, dataStore dataservices.DataStore, shutdownTrigger context.CancelFunc) (*ssl.Service, error) {
func initSSLService(addr, certPath, keyPath string, fileService portainer.FileService, dataStore dataservices.DataStore, shutdownTrigger context.CancelFunc) (*ssl.Service, error) {
slices := strings.Split(addr, ":")
host := slices[0]
if host == "" {
@ -568,7 +568,7 @@ func buildServer(flags *portainer.CLIFlags) portainer.Server {
cryptoService := initCryptoService()
digitalSignatureService := initDigitalSignatureService()
sslService, err := initSSLService(*flags.AddrHTTPS, *flags.Data, *flags.SSLCert, *flags.SSLKey, fileService, dataStore, shutdownTrigger)
sslService, err := initSSLService(*flags.AddrHTTPS, *flags.SSLCert, *flags.SSLKey, fileService, dataStore, shutdownTrigger)
if err != nil {
logrus.Fatal(err)
}

View File

@ -9,18 +9,7 @@ import (
// CreateServerTLSConfiguration creates a basic tls.Config to be used by servers with recommended TLS settings
func CreateServerTLSConfiguration() *tls.Config {
return &tls.Config{
MinVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_AES_128_GCM_SHA256,
tls.TLS_AES_256_GCM_SHA384,
tls.TLS_CHACHA20_POLY1305_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
},
MinVersion: tls.VersionTLS13,
}
}

View File

@ -56,10 +56,12 @@ const (
TempPath = "tmp"
// SSLCertPath represents the default ssl certificates path
SSLCertPath = "certs"
// DefaultSSLCertFilename represents the default ssl certificate file name
DefaultSSLCertFilename = "cert.pem"
// DefaultSSLKeyFilename represents the default ssl key file name
DefaultSSLKeyFilename = "key.pem"
// SSLCertFilename represents the ssl certificate file name
SSLCertFilename = "cert.pem"
// SSLKeyFilename represents the ssl key file name
SSLKeyFilename = "key.pem"
// SSLCACertFilename represents the CA ssl certificate file name for mTLS
SSLCACertFilename = "ca-cert.pem"
)
// ErrUndefinedTLSFileType represents an error returned on undefined TLS file type
@ -161,7 +163,7 @@ func (service *Service) Copy(fromFilePath string, toFilePath string, deleteIfExi
}
if !exists {
return errors.New("File doesn't exist")
return errors.New(fmt.Sprintf("File (%s) doesn't exist", fromFilePath))
}
finput, err := os.Open(fromFilePath)
@ -580,8 +582,8 @@ func (service *Service) wrapFileStore(filepath string) string {
}
func defaultCertPathUnderFileStore() (string, string) {
certPath := JoinPaths(SSLCertPath, DefaultSSLCertFilename)
keyPath := JoinPaths(SSLCertPath, DefaultSSLKeyFilename)
certPath := JoinPaths(SSLCertPath, SSLCertFilename)
keyPath := JoinPaths(SSLCertPath, SSLKeyFilename)
return certPath, keyPath
}
@ -627,6 +629,18 @@ func (service *Service) CopySSLCertPair(certPath, keyPath string) (string, strin
return defCertPath, defKeyPath, nil
}
// CopySSLCACert copies the specified caCert pem file
func (service *Service) CopySSLCACert(caCertPath string) (string, error) {
toFilePath := service.wrapFileStore(JoinPaths(SSLCertPath, SSLCACertFilename))
err := service.Copy(caCertPath, toFilePath, true)
if err != nil {
return "", err
}
return toFilePath, nil
}
// FileExists checks for the existence of the specified file.
func FileExists(filePath string) (bool, error) {
if _, err := os.Stat(filePath); err != nil {

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/pkg/errors"
"github.com/portainer/libcrypto"
portainer "github.com/portainer/portainer/api"
"github.com/portainer/portainer/api/dataservices"
@ -32,8 +33,8 @@ func NewService(fileService portainer.FileService, dataStore dataservices.DataSt
// Init initializes the service
func (service *Service) Init(host, certPath, keyPath string) error {
pathSupplied := certPath != "" && keyPath != ""
if pathSupplied {
certSupplied := certPath != "" && keyPath != ""
if certSupplied {
newCertPath, newKeyPath, err := service.fileService.CopySSLCertPair(certPath, keyPath)
if err != nil {
return errors.Wrap(err, "failed copying supplied certs")
@ -60,16 +61,24 @@ func (service *Service) Init(host, certPath, keyPath string) error {
}
}
// path not supplied and certificates doesn't exist - generate self signed
// path not supplied and certificates doesn't exist - generate self-signed
certPath, keyPath = service.fileService.GetDefaultSSLCertsPath()
err = service.generateSelfSignedCertificates(host, certPath, keyPath)
err = generateSelfSignedCertificates(host, certPath, keyPath)
if err != nil {
return errors.Wrap(err, "failed generating self signed certs")
}
return service.cacheInfo(certPath, keyPath, true)
}
func generateSelfSignedCertificates(ip, certPath, keyPath string) error {
if ip == "" {
return errors.New("host can't be empty")
}
log.Printf("[INFO] [internal,ssl] [message: no cert files found, generating self signed ssl certificates]")
return libcrypto.GenerateCertsForHost("localhost", ip, certPath, keyPath, time.Now().AddDate(5, 0, 0))
}
// GetRawCertificate gets the raw certificate
@ -98,7 +107,10 @@ func (service *Service) SetCertificates(certData, keyData []byte) error {
return err
}
service.cacheInfo(certPath, keyPath, false)
err = service.cacheInfo(certPath, keyPath, false)
if err != nil {
return err
}
service.shutdownTrigger()
@ -138,7 +150,7 @@ func (service *Service) cacheCertificate(certPath, keyPath string) error {
return nil
}
func (service *Service) cacheInfo(certPath, keyPath string, selfSigned bool) error {
func (service *Service) cacheInfo(certPath string, keyPath string, selfSigned bool) error {
err := service.cacheCertificate(certPath, keyPath)
if err != nil {
return err
@ -160,12 +172,3 @@ func (service *Service) cacheInfo(certPath, keyPath string, selfSigned bool) err
return nil
}
func (service *Service) generateSelfSignedCertificates(ip, certPath, keyPath string) error {
if ip == "" {
return errors.New("host can't be empty")
}
log.Printf("[INFO] [internal,ssl] [message: no cert files found, generating self signed ssl certificates]")
return libcrypto.GenerateCertsForHost("localhost", ip, certPath, keyPath, time.Now().AddDate(5, 0, 0))
}

View File

@ -1238,6 +1238,7 @@ type (
GetDefaultSSLCertsPath() (string, string)
StoreSSLCertPair(cert, key []byte) (string, string, error)
CopySSLCertPair(certPath, keyPath string) (string, string, error)
CopySSLCACert(caCertPath string) (string, error)
StoreFDOProfileFileFromBytes(fdoProfileIdentifier string, data []byte) (string, error)
}