feat(registry): add support for custom CA certificates and TLS validation

- Introduced `--registry-ca` and `--registry-ca-validate` flags for configuring TLS verification with private registries.
- Implemented in-memory token caching with expiration handling.
- Updated documentation to reflect new CLI options and usage examples.
- Added tests for token cache concurrency and expiry behavior.
This commit is contained in:
kalvinparker 2025-11-14 14:30:37 +00:00
parent 76f9cea516
commit e1f67fc3d0
18 changed files with 738 additions and 17 deletions

View file

@ -12,6 +12,7 @@ import (
"time"
"github.com/containrrr/watchtower/internal/meta"
"github.com/containrrr/watchtower/pkg/registry"
"github.com/containrrr/watchtower/pkg/registry/auth"
"github.com/containrrr/watchtower/pkg/registry/manifest"
"github.com/containrrr/watchtower/pkg/types"
@ -76,19 +77,7 @@ func TransformAuth(registryAuth string) string {
// GetDigest from registry using a HEAD request to prevent rate limiting
func GetDigest(url string, token string) (string, error) {
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
}
tr := newTransport()
client := &http.Client{Transport: tr}
req, _ := http.NewRequest("HEAD", url, nil)
@ -124,3 +113,35 @@ func GetDigest(url string, token string) (string, error) {
}
return res.Header.Get(ContentDigestHeader), nil
}
// newTransport constructs an *http.Transport used for registry HEAD/token requests.
// It respects the package-level `registry.InsecureSkipVerify` toggle.
func newTransport() *http.Transport {
tr := &http.Transport{
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
ForceAttemptHTTP2: true,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
}
certPool := registry.GetRegistryCertPool()
if registry.InsecureSkipVerify {
// Insecure mode requested: disable verification entirely
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
} else if certPool != nil {
// Create TLS config with custom root CAs merged into system pool
tr.TLSClientConfig = &tls.Config{RootCAs: certPool}
}
return tr
}
// NewTransportForTest exposes the transport construction for unit tests.
func NewTransportForTest() *http.Transport {
return newTransport()
}