@@ -18,6 +18,7 @@ package proxy
1818
1919import (
2020 "crypto/tls"
21+ "crypto/x509"
2122 "fmt"
2223 "io"
2324 "net"
@@ -31,7 +32,7 @@ import (
3132 "github.com/dragonflyoss/Dragonfly/dfdaemon/downloader"
3233 "github.com/dragonflyoss/Dragonfly/dfdaemon/downloader/dfget"
3334 "github.com/dragonflyoss/Dragonfly/dfdaemon/transport"
34-
35+ "github.com/golang/groupcache/lru"
3536 "github.com/pkg/errors"
3637 "github.com/sirupsen/logrus"
3738)
@@ -72,6 +73,11 @@ func WithCertFromFile(certFile, keyFile string) Option {
7273 return errors .Wrap (err , "load cert" )
7374 }
7475 logrus .Infof ("use self-signed certificate (%s, %s) for https hijacking" , certFile , keyFile )
76+ leaf , err := x509 .ParseCertificate (cert .Certificate [0 ])
77+ if err != nil {
78+ return errors .Wrap (err , "load leaf cert" )
79+ }
80+ cert .Leaf = leaf
7581 p .cert = & cert
7682 return nil
7783 }
@@ -168,6 +174,8 @@ type Proxy struct {
168174 httpsHosts []* config.HijackHost
169175 // cert is the certificate used to hijack https proxy requests
170176 cert * tls.Certificate
177+ // certCache is a in-memory cache store for TLS certs used in HTTPS hijack. Lazy init.
178+ certCache * lru.Cache
171179 // directHandler are used to handle non proxy requests
172180 directHandler http.Handler
173181 // downloadFactory returns the downloader used for p2p downloading
@@ -314,7 +322,38 @@ func (proxy *Proxy) handleHTTPS(w http.ResponseWriter, r *http.Request) {
314322 logrus .Debugln ("hijack https request to" , r .Host )
315323
316324 sConfig := new (tls.Config )
317- sConfig .Certificates = []tls.Certificate {* proxy .cert }
325+ if proxy .cert .Leaf != nil && proxy .cert .Leaf .IsCA {
326+ if proxy .certCache == nil { // Initialize proxy.certCache on first access. (Lazy init)
327+ proxy .certCache = lru .New (100 ) // Default max entries size = 100
328+ }
329+ logrus .Debugf ("hijack https request with CA <%s>" , proxy .cert .Leaf .Subject .CommonName )
330+ leafCertSpec := LeafCertSpec {
331+ proxy .cert .Leaf .PublicKey ,
332+ proxy .cert .PrivateKey ,
333+ proxy .cert .Leaf .SignatureAlgorithm }
334+ host , _ , _ := net .SplitHostPort (r .Host )
335+ sConfig .GetCertificate = func (hello * tls.ClientHelloInfo ) (* tls.Certificate , error ) {
336+ cConfig .ServerName = host
337+ logrus .Debugf ("Generate temporal leaf TLS cert for ServerName <%s>, host <%s>" , hello .ServerName , host )
338+ // It's assumed that `hello.ServerName` is always same as `host`, in practice.
339+ cacheKey := host
340+ cached , hit := proxy .certCache .Get (cacheKey )
341+ if hit && time .Now ().Before (cached .(* tls.Certificate ).Leaf .NotAfter ) { // If cache hit and the cert is not expired
342+ logrus .Debugf ("TLS Cache hit, cacheKey = <%s>" , cacheKey )
343+ return cached .(* tls.Certificate ), nil
344+ }
345+ cert , err := genLeafCert (proxy .cert , & leafCertSpec , host )
346+ if err == nil {
347+ // Put cert in cache only if there is no error. So all certs in cache are always valid.
348+ // But certs in cache maybe expired (After 24 hours, see the default duration of generated certs)
349+ proxy .certCache .Add (cacheKey , cert )
350+ }
351+ // If err != nil, means unrecoverable error happened in genLeafCert(...)
352+ return cert , err
353+ }
354+ } else {
355+ sConfig .Certificates = []tls.Certificate {* proxy .cert }
356+ }
318357
319358 sConn , err := handshake (w , sConfig )
320359 if err != nil {
0 commit comments