Skip to content

Commit 0d563bd

Browse files
committed
Support listening on TLS
Allows specifying a certificate&key pair by file paths, as well as an optional CA file. Enforce/allow mutual-TLS handshake behaviour too. Signed-off-by: Joe Groocock <me@frebib.net>
1 parent c15f44e commit 0d563bd

1 file changed

Lines changed: 88 additions & 1 deletion

File tree

cmd/cadvisor.go

Lines changed: 88 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,10 @@ package main
1616

1717
import (
1818
"crypto/tls"
19+
"crypto/x509"
1920
"flag"
2021
"fmt"
22+
"net"
2123
"net/http"
2224
"net/http/pprof"
2325
"os"
@@ -55,6 +57,11 @@ var httpAuthRealm = flag.String("http_auth_realm", "localhost", "HTTP auth realm
5557
var httpDigestFile = flag.String("http_digest_file", "", "HTTP digest file for the web UI")
5658
var httpDigestRealm = flag.String("http_digest_realm", "localhost", "HTTP digest file for the web UI")
5759

60+
var tlsCertPath = flag.String("tls_cert_path", "", "TLS certificate file path to serve cadvisor HTTPS with. Must be specified along with -tls_key_path")
61+
var tlsKeyPath = flag.String("tls_key_path", "", "TLS key file path to serve cadvisor HTTPS with. Must be specified along with -tls_cert_path")
62+
var tlsCAPath = flag.String("tls_ca_path", "", "TLS certificate authority file path. Used to verify TLS clients connecting to cadvisor. System CA roots will be used if this is not specified.")
63+
var tlsClientAuth = flag.String("tls_client_auth", "require", "TLS authentication mode, must be one of request, optional, requireany, require or none.")
64+
5865
var prometheusEndpoint = flag.String("prometheus_endpoint", "/metrics", "Endpoint to expose Prometheus metrics on")
5966

6067
var enableProfiling = flag.Bool("profiling", false, "Enable profiling via web interface host:port/debug/pprof/")
@@ -152,6 +159,9 @@ func main() {
152159
klog.Fatalf("Failed to register HTTP handlers: %v", err)
153160
}
154161

162+
// Generate tls.Config if it was requested by flags
163+
tlsConfig := createTLSConfig(*tlsCertPath, *tlsKeyPath, *tlsCAPath, *tlsClientAuth)
164+
155165
containerLabelFunc := metrics.DefaultContainerLabels
156166
if !*storeContainerLabels {
157167
whitelistedLabels := strings.Split(*whitelistedContainerLabels, ",")
@@ -179,7 +189,23 @@ func main() {
179189
rootMux.Handle(*urlBasePrefix+"/", http.StripPrefix(*urlBasePrefix, mux))
180190

181191
addr := fmt.Sprintf("%s:%d", *argIP, *argPort)
182-
klog.Fatal(http.ListenAndServe(addr, rootMux))
192+
listener, err := net.Listen("tcp", addr)
193+
if err != nil {
194+
klog.Fatal(err)
195+
}
196+
197+
// Wrap in a TLS listener if any TLS configuration was specified
198+
if tlsConfig != nil {
199+
listener = tls.NewListener(listener, tlsConfig)
200+
klog.V(1).Infof("Listening for TLS connections")
201+
}
202+
203+
server := &http.Server{
204+
Addr: addr,
205+
Handler: rootMux,
206+
TLSConfig: tlsConfig,
207+
}
208+
klog.Fatal(server.Serve(listener))
183209
}
184210

185211
func setMaxProcs() {
@@ -240,3 +266,64 @@ func createCollectorHTTPClient(collectorCert, collectorKey string) http.Client {
240266

241267
return http.Client{Transport: transport}
242268
}
269+
270+
func createTLSConfig(tlsCertPath, tlsKeyPath, tlsCAPath, tlsClientAuth string) *tls.Config {
271+
var tlsConfig *tls.Config
272+
273+
if tlsCertPath != "" || tlsKeyPath != "" {
274+
if tlsCertPath == "" || tlsKeyPath == "" {
275+
// Fail if one is set but not the other
276+
klog.Fatal("Both tls_cert_path and tls_key_path are required at the same time")
277+
}
278+
279+
// Verify that the key/certificate load and are valid before starting up
280+
_, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath)
281+
if err != nil {
282+
klog.Fatalf("Failed to load TLS certificate/key pair: %v", err)
283+
}
284+
285+
tlsConfig = &tls.Config{
286+
GetCertificate: func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
287+
cert, err := tls.LoadX509KeyPair(tlsCertPath, tlsKeyPath)
288+
if err != nil {
289+
return nil, err
290+
}
291+
return &cert, nil
292+
},
293+
}
294+
}
295+
296+
if tlsCAPath != "" {
297+
clientCAPool := x509.NewCertPool()
298+
clientCAFile, err := os.ReadFile(tlsCAPath)
299+
if err != nil {
300+
klog.Fatalf("Failed to load TLS CA file: %v", err)
301+
}
302+
clientCAPool.AppendCertsFromPEM(clientCAFile)
303+
304+
if tlsConfig == nil {
305+
tlsConfig = new(tls.Config)
306+
}
307+
tlsConfig.ClientCAs = clientCAPool
308+
}
309+
310+
// This is only meaningful if we have TLS server certs, a CA cert to verify clients, or both.
311+
if tlsConfig != nil {
312+
switch tlsClientAuth {
313+
case "request":
314+
tlsConfig.ClientAuth = tls.RequestClientCert
315+
case "optional":
316+
tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven
317+
case "requireany":
318+
tlsConfig.ClientAuth = tls.RequireAnyClientCert
319+
case "require":
320+
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
321+
case "none":
322+
tlsConfig.ClientAuth = tls.NoClientCert
323+
default:
324+
klog.Fatalf("Invalid tls_client_auth: %s", tlsClientAuth)
325+
}
326+
}
327+
328+
return tlsConfig
329+
}

0 commit comments

Comments
 (0)