mirror of
https://github.com/drakkan/sftpgo.git
synced 2025-12-07 14:50:55 +03:00
web hooks: add mutual TLS support
This commit is contained in:
@@ -3,6 +3,7 @@ package httpclient
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
@@ -14,6 +15,12 @@ import (
|
||||
"github.com/drakkan/sftpgo/utils"
|
||||
)
|
||||
|
||||
// TLSKeyPair defines the paths for a TLS key pair
|
||||
type TLSKeyPair struct {
|
||||
Cert string `json:"cert" mapstructure:"cert"`
|
||||
Key string `json:"key" mapstructure:"key"`
|
||||
}
|
||||
|
||||
// Config defines the configuration for HTTP clients.
|
||||
// HTTP clients are used for executing hooks such as the ones used for
|
||||
// custom actions, external authentication and pre-login user modifications
|
||||
@@ -31,6 +38,8 @@ type Config struct {
|
||||
// Adding trusted CA certificates is a convenient way to use self-signed
|
||||
// certificates without defeating the purpose of using TLS
|
||||
CACertificates []string `json:"ca_certificates" mapstructure:"ca_certificates"`
|
||||
// Certificates defines the certificates to use for mutual TLS
|
||||
Certificates []TLSKeyPair `json:"certificates" mapstructure:"certificates"`
|
||||
// if enabled the HTTP client accepts any TLS certificate presented by
|
||||
// the server and any host name in that certificate.
|
||||
// In this mode, TLS is susceptible to man-in-the-middle attacks.
|
||||
@@ -45,25 +54,35 @@ const logSender = "httpclient"
|
||||
var httpConfig Config
|
||||
|
||||
// Initialize configures HTTP clients
|
||||
func (c Config) Initialize(configDir string) {
|
||||
httpConfig = c
|
||||
rootCAs := c.loadCACerts(configDir)
|
||||
func (c *Config) Initialize(configDir string) error {
|
||||
rootCAs, err := c.loadCACerts(configDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
customTransport := http.DefaultTransport.(*http.Transport).Clone()
|
||||
if customTransport.TLSClientConfig != nil {
|
||||
customTransport.TLSClientConfig.RootCAs = rootCAs
|
||||
} else {
|
||||
customTransport.TLSClientConfig = &tls.Config{
|
||||
RootCAs: rootCAs,
|
||||
RootCAs: rootCAs,
|
||||
NextProtos: []string{"h2", "http/1.1"},
|
||||
}
|
||||
}
|
||||
customTransport.TLSClientConfig.InsecureSkipVerify = c.SkipTLSVerify
|
||||
httpConfig.customTransport = customTransport
|
||||
httpConfig.tlsConfig = customTransport.TLSClientConfig
|
||||
c.customTransport = customTransport
|
||||
c.tlsConfig = customTransport.TLSClientConfig
|
||||
|
||||
err = c.loadCertificates(configDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
httpConfig = *c
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadCACerts returns system cert pools and try to add the configured
|
||||
// CA certificates to it
|
||||
func (c Config) loadCACerts(configDir string) *x509.CertPool {
|
||||
func (c *Config) loadCACerts(configDir string) (*x509.CertPool, error) {
|
||||
rootCAs, err := x509.SystemCertPool()
|
||||
if err != nil {
|
||||
rootCAs = x509.NewCertPool()
|
||||
@@ -71,26 +90,52 @@ func (c Config) loadCACerts(configDir string) *x509.CertPool {
|
||||
|
||||
for _, ca := range c.CACertificates {
|
||||
if !utils.IsFileInputValid(ca) {
|
||||
logger.Warn(logSender, "", "unable to load invalid CA certificate: %#v", ca)
|
||||
logger.WarnToConsole("unable to load invalid CA certificate: %#v", ca)
|
||||
continue
|
||||
return nil, fmt.Errorf("unable to load invalid CA certificate: %#v", ca)
|
||||
}
|
||||
if !filepath.IsAbs(ca) {
|
||||
ca = filepath.Join(configDir, ca)
|
||||
}
|
||||
certs, err := ioutil.ReadFile(ca)
|
||||
if err != nil {
|
||||
logger.Warn(logSender, "", "unable to load CA certificate: %v", err)
|
||||
logger.WarnToConsole("unable to load CA certificate: %#v", err)
|
||||
return nil, fmt.Errorf("unable to load CA certificate: %v", err)
|
||||
}
|
||||
if rootCAs.AppendCertsFromPEM(certs) {
|
||||
logger.Debug(logSender, "", "CA certificate %#v added to the trusted certificates", ca)
|
||||
} else {
|
||||
logger.Warn(logSender, "", "unable to add CA certificate %#v to the trusted cetificates", ca)
|
||||
logger.WarnToConsole("unable to add CA certificate %#v to the trusted cetificates", ca)
|
||||
return nil, fmt.Errorf("unable to add CA certificate %#v to the trusted cetificates", ca)
|
||||
}
|
||||
}
|
||||
return rootCAs
|
||||
return rootCAs, nil
|
||||
}
|
||||
|
||||
func (c *Config) loadCertificates(configDir string) error {
|
||||
if len(c.Certificates) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
for _, keyPair := range c.Certificates {
|
||||
cert := keyPair.Cert
|
||||
key := keyPair.Key
|
||||
if !utils.IsFileInputValid(cert) {
|
||||
return fmt.Errorf("unable to load invalid certificate: %#v", cert)
|
||||
}
|
||||
if !utils.IsFileInputValid(key) {
|
||||
return fmt.Errorf("unable to load invalid key: %#v", key)
|
||||
}
|
||||
if !filepath.IsAbs(cert) {
|
||||
cert = filepath.Join(configDir, cert)
|
||||
}
|
||||
if !filepath.IsAbs(key) {
|
||||
key = filepath.Join(configDir, key)
|
||||
}
|
||||
tlsCert, err := tls.LoadX509KeyPair(cert, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to load key pair %#v, %#v: %v", cert, key, err)
|
||||
}
|
||||
logger.Debug(logSender, "", "client certificate %#v and key %#v successfully loaded", cert, key)
|
||||
c.tlsConfig.Certificates = append(c.tlsConfig.Certificates, tlsCert)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetHTTPClient returns an HTTP client with the configured parameters
|
||||
|
||||
Reference in New Issue
Block a user