mirror of https://github.com/portainer/portainer
feat(ssl): enable mTLS certificates [EE-2617] (#6612)
parent
f9f937f844
commit
dff74f0823
|
@ -18,8 +18,6 @@ const (
|
|||
defaultHTTPDisabled = "false"
|
||||
defaultHTTPEnabled = "false"
|
||||
defaultSSL = "false"
|
||||
defaultSSLCertPath = "/certs/portainer.crt"
|
||||
defaultSSLKeyPath = "/certs/portainer.key"
|
||||
defaultBaseURL = "/"
|
||||
defaultSecretKeyName = "portainer"
|
||||
)
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue