From b3828c9c4c622891426da8a65f1de471fdd3ecbe Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Thu, 10 Dec 2015 16:46:43 -0500 Subject: [PATCH 1/2] notifier: add ServerName configuration for TLS --- config.example.yaml | 3 ++- config/config.go | 11 +++++++---- notifier/notifier.go | 40 ++++++++++++++++++++++++++++++++++++++-- utils/http/http.go | 33 --------------------------------- 4 files changed, 47 insertions(+), 40 deletions(-) diff --git a/config.example.yaml b/config.example.yaml index ffb89bdc..cdefaef6 100644 --- a/config.example.yaml +++ b/config.example.yaml @@ -31,7 +31,8 @@ updater: notifier: # HTTP endpoint that will receive notifications with POST requests. endpoint: - # Path to certificates to call the endpoint securely with TLS and client certificate auth. + # Server name and path to certificates to call the endpoint securely with TLS and client certificate auth. + servername: cafile: keyfile: certfile: diff --git a/config/config.go b/config/config.go index f944d50b..5c085994 100644 --- a/config/config.go +++ b/config/config.go @@ -1,5 +1,3 @@ -package config - // Copyright 2015 clair authors // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -14,6 +12,8 @@ package config // See the License for the specific language governing permissions and // limitations under the License. +package config + import ( "io/ioutil" "os" @@ -44,8 +44,11 @@ type UpdaterConfig struct { // NotifierConfig is the configuration for the Notifier service. type NotifierConfig struct { - Endpoint string - CertFile, KeyFile, CAFile string + Endpoint string + ServerName string + CertFile string + KeyFile string + CAFile string } // APIConfig is the configuration for the API service. diff --git a/notifier/notifier.go b/notifier/notifier.go index fbf38c17..d432e301 100644 --- a/notifier/notifier.go +++ b/notifier/notifier.go @@ -18,7 +18,10 @@ package notifier import ( "bytes" + "crypto/tls" + "crypto/x509" "encoding/json" + "io/ioutil" "net/http" "net/url" "time" @@ -30,7 +33,6 @@ import ( "github.com/coreos/clair/database" "github.com/coreos/clair/health" "github.com/coreos/clair/utils" - httputils "github.com/coreos/clair/utils/http" ) var log = capnslog.NewPackageLogger("github.com/coreos/clair", "notifier") @@ -70,7 +72,7 @@ func New(config *config.NotifierConfig) *Notifier { } // Initialize TLS. - tlsConfig, err := httputils.LoadTLSClientConfig(config.CertFile, config.KeyFile, config.CAFile) + tlsConfig, err := loadTLSClientConfig(config) if err != nil { log.Fatalf("could not initialize client cert authentification: %s\n", err) } @@ -203,3 +205,37 @@ func (n *Notifier) Healthcheck() health.Status { queueSize, err := database.CountNotificationsToSend() return health.Status{IsEssential: false, IsHealthy: err == nil, Details: struct{ QueueSize int }{QueueSize: queueSize}} } + +// loadTLSClientConfig initializes a *tls.Config using the given notifier +// configuration. +// +// If no certificates are given, (nil, nil) is returned. +// The CA certificate is optional and falls back to the system default. +func loadTLSClientConfig(cfg *config.NotifierConfig) (*tls.Config, error) { + if cfg.CertFile == "" || cfg.KeyFile == "" { + return nil, nil + } + + cert, err := tls.LoadX509KeyPair(cfg.CertFile, cfg.KeyFile) + if err != nil { + return nil, err + } + + var caCertPool *x509.CertPool + if cfg.CAFile != "" { + caCert, err := ioutil.ReadFile(cfg.CAFile) + if err != nil { + return nil, err + } + caCertPool = x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + } + + tlsConfig := &tls.Config{ + ServerName: cfg.ServerName, + Certificates: []tls.Certificate{cert}, + RootCAs: caCertPool, + } + + return tlsConfig, nil +} diff --git a/utils/http/http.go b/utils/http/http.go index 1b1c0f8d..b06c94ac 100644 --- a/utils/http/http.go +++ b/utils/http/http.go @@ -31,39 +31,6 @@ import ( // MaxPostSize is the maximum number of bytes that ParseHTTPBody reads from an http.Request.Body. const MaxBodySize int64 = 1048576 -// LoadTLSClientConfig initializes a *tls.Config using the given certificates and private key, that -// can be used to communicate with a server using client certificate authentificate. -// -// If no certificates are given, a nil *tls.Config is returned. -// The CA certificate is optionnal, the system defaults are used if not provided. -func LoadTLSClientConfig(certFile, keyFile, caFile string) (*tls.Config, error) { - if len(certFile) == 0 || len(keyFile) == 0 { - return nil, nil - } - - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, err - } - - var caCertPool *x509.CertPool - if len(caFile) > 0 { - caCert, err := ioutil.ReadFile(caFile) - if err != nil { - return nil, err - } - caCertPool = x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - } - - tlsConfig := &tls.Config{ - Certificates: []tls.Certificate{cert}, - RootCAs: caCertPool, - } - - return tlsConfig, nil -} - // LoadTLSClientConfigForServer initializes a *tls.Config using the given CA, that can be used to // configure http server to do client certificate authentification. // From 34870a2a2bd1c343c0fa95fbb24551961808106c Mon Sep 17 00:00:00 2001 From: Jimmy Zelinskie Date: Tue, 15 Dec 2015 12:03:42 -0500 Subject: [PATCH 2/2] move LoadTLSClientConfigForServer into API package This isn't reused any where just yet, so we're best off leaving it local to the place that needs it. --- api/api.go | 33 +++++++++++++++++++++++++++++++-- utils/http/http.go | 28 ---------------------------- 2 files changed, 31 insertions(+), 30 deletions(-) diff --git a/api/api.go b/api/api.go index 7a99ea3a..dbd810e1 100644 --- a/api/api.go +++ b/api/api.go @@ -17,6 +17,9 @@ package api import ( + "crypto/tls" + "crypto/x509" + "io/ioutil" "net" "net/http" "strconv" @@ -27,7 +30,6 @@ import ( "github.com/coreos/clair/config" "github.com/coreos/clair/utils" - httputils "github.com/coreos/clair/utils/http" ) var log = capnslog.NewPackageLogger("github.com/coreos/clair", "api") @@ -44,7 +46,7 @@ func Run(config *config.APIConfig, st *utils.Stopper) { } log.Infof("starting main API on port %d.", config.Port) - tlsConfig, err := httputils.LoadTLSClientConfigForServer(config.CAFile) + tlsConfig, err := tlsClientConfig(config.CAFile) if err != nil { log.Fatalf("could not initialize client cert authentification: %s\n", err) } @@ -110,3 +112,30 @@ func listenAndServeWithStopper(srv *graceful.Server, st *utils.Stopper, certFile log.Fatal(err) } } + +// tlsClientConfig initializes a *tls.Config using the given CA. The resulting +// *tls.Config is meant to be used to configure an HTTP server to do client +// certificate authentication. +// +// If no CA is given, a nil *tls.Config is returned; no client certificate will +// be required and verified. In other words, authentification will be disabled. +func tlsClientConfig(caPath string) (*tls.Config, error) { + if caPath == "" { + return nil, nil + } + + caCert, err := ioutil.ReadFile(caPath) + if err != nil { + return nil, err + } + + caCertPool := x509.NewCertPool() + caCertPool.AppendCertsFromPEM(caCert) + + tlsConfig := &tls.Config{ + ClientCAs: caCertPool, + ClientAuth: tls.RequireAndVerifyClientCert, + } + + return tlsConfig, nil +} diff --git a/utils/http/http.go b/utils/http/http.go index b06c94ac..360c7ad8 100644 --- a/utils/http/http.go +++ b/utils/http/http.go @@ -16,11 +16,8 @@ package http import ( - "crypto/tls" - "crypto/x509" "encoding/json" "io" - "io/ioutil" "net/http" "github.com/coreos/clair/database" @@ -31,31 +28,6 @@ import ( // MaxPostSize is the maximum number of bytes that ParseHTTPBody reads from an http.Request.Body. const MaxBodySize int64 = 1048576 -// LoadTLSClientConfigForServer initializes a *tls.Config using the given CA, that can be used to -// configure http server to do client certificate authentification. -// -// If no CA is given, a nil *tls.Config is returned: no client certificate will be required and -// verified. In other words, authentification will be disabled. -func LoadTLSClientConfigForServer(caFile string) (*tls.Config, error) { - if len(caFile) == 0 { - return nil, nil - } - - caCert, err := ioutil.ReadFile(caFile) - if err != nil { - return nil, err - } - caCertPool := x509.NewCertPool() - caCertPool.AppendCertsFromPEM(caCert) - - tlsConfig := &tls.Config{ - ClientCAs: caCertPool, - ClientAuth: tls.RequireAndVerifyClientCert, - } - - return tlsConfig, nil -} - // WriteHTTP writes a JSON-encoded object to a http.ResponseWriter, as well as // a HTTP status code. func WriteHTTP(w http.ResponseWriter, httpStatus int, v interface{}) {