mirror of
https://github.com/pomerium/pomerium.git
synced 2025-07-01 00:48:17 +02:00
* chore(deps): bump the go group with 27 updates Bumps the go group with 27 updates: | Package | From | To | | --- | --- | --- | | [cloud.google.com/go/storage](https://github.com/googleapis/google-cloud-go) | `1.40.0` | `1.41.0` | | [github.com/aws/aws-sdk-go-v2](https://github.com/aws/aws-sdk-go-v2) | `1.26.1` | `1.27.0` | | [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) | `1.27.11` | `1.27.16` | | [github.com/aws/aws-sdk-go-v2/service/s3](https://github.com/aws/aws-sdk-go-v2) | `1.53.1` | `1.54.3` | | [github.com/caddyserver/certmagic](https://github.com/caddyserver/certmagic) | `0.20.0` | `0.21.2` | | [github.com/docker/docker](https://github.com/docker/docker) | `26.1.1+incompatible` | `26.1.3+incompatible` | | [github.com/jackc/pgx/v5](https://github.com/jackc/pgx) | `5.5.5` | `5.6.0` | | [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) | `0.64.1` | `0.65.0` | | [github.com/prometheus/client_golang](https://github.com/prometheus/client_golang) | `1.19.0` | `1.19.1` | | [github.com/prometheus/procfs](https://github.com/prometheus/procfs) | `0.14.0` | `0.15.1` | | [github.com/rs/zerolog](https://github.com/rs/zerolog) | `1.32.0` | `1.33.0` | | [github.com/shirou/gopsutil/v3](https://github.com/shirou/gopsutil) | `3.24.4` | `3.24.5` | | [go.opentelemetry.io/otel](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/exporters/otlp/otlptrace](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/metric](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/sdk](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/sdk/metric](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [go.opentelemetry.io/otel/trace](https://github.com/open-telemetry/opentelemetry-go) | `1.26.0` | `1.27.0` | | [golang.org/x/crypto](https://github.com/golang/crypto) | `0.22.0` | `0.23.0` | | [golang.org/x/net](https://github.com/golang/net) | `0.24.0` | `0.25.0` | | [golang.org/x/oauth2](https://github.com/golang/oauth2) | `0.19.0` | `0.20.0` | | [google.golang.org/api](https://github.com/googleapis/google-api-go-client) | `0.177.0` | `0.178.0` | | [google.golang.org/genproto/googleapis/rpc](https://github.com/googleapis/go-genproto) | `0.0.0-20240429193739-8cf5692501f6` | `0.0.0-20240515191416-fc5f0ca64291` | | [google.golang.org/grpc](https://github.com/grpc/grpc-go) | `1.63.2` | `1.64.0` | | google.golang.org/protobuf | `1.34.0` | `1.34.1` | Updates `cloud.google.com/go/storage` from 1.40.0 to 1.41.0 - [Release notes](https://github.com/googleapis/google-cloud-go/releases) - [Changelog](https://github.com/googleapis/google-cloud-go/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-cloud-go/compare/spanner/v1.40.0...spanner/v1.41.0) Updates `github.com/aws/aws-sdk-go-v2` from 1.26.1 to 1.27.0 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/v1.26.1...v1.27.0) Updates `github.com/aws/aws-sdk-go-v2/config` from 1.27.11 to 1.27.16 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.11...config/v1.27.16) Updates `github.com/aws/aws-sdk-go-v2/service/s3` from 1.53.1 to 1.54.3 - [Release notes](https://github.com/aws/aws-sdk-go-v2/releases) - [Commits](https://github.com/aws/aws-sdk-go-v2/compare/service/s3/v1.53.1...service/s3/v1.54.3) Updates `github.com/caddyserver/certmagic` from 0.20.0 to 0.21.2 - [Release notes](https://github.com/caddyserver/certmagic/releases) - [Commits](https://github.com/caddyserver/certmagic/compare/v0.20.0...v0.21.2) Updates `github.com/docker/docker` from 26.1.1+incompatible to 26.1.3+incompatible - [Release notes](https://github.com/docker/docker/releases) - [Commits](https://github.com/docker/docker/compare/v26.1.1...v26.1.3) Updates `github.com/jackc/pgx/v5` from 5.5.5 to 5.6.0 - [Changelog](https://github.com/jackc/pgx/blob/master/CHANGELOG.md) - [Commits](https://github.com/jackc/pgx/compare/v5.5.5...v5.6.0) Updates `github.com/open-policy-agent/opa` from 0.64.1 to 0.65.0 - [Release notes](https://github.com/open-policy-agent/opa/releases) - [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-policy-agent/opa/compare/v0.64.1...v0.65.0) Updates `github.com/prometheus/client_golang` from 1.19.0 to 1.19.1 - [Release notes](https://github.com/prometheus/client_golang/releases) - [Changelog](https://github.com/prometheus/client_golang/blob/main/CHANGELOG.md) - [Commits](https://github.com/prometheus/client_golang/compare/v1.19.0...v1.19.1) Updates `github.com/prometheus/procfs` from 0.14.0 to 0.15.1 - [Release notes](https://github.com/prometheus/procfs/releases) - [Commits](https://github.com/prometheus/procfs/compare/v0.14.0...v0.15.1) Updates `github.com/rs/zerolog` from 1.32.0 to 1.33.0 - [Commits](https://github.com/rs/zerolog/compare/v1.32.0...v1.33.0) Updates `github.com/shirou/gopsutil/v3` from 3.24.4 to 3.24.5 - [Release notes](https://github.com/shirou/gopsutil/releases) - [Commits](https://github.com/shirou/gopsutil/compare/v3.24.4...v3.24.5) Updates `go.opentelemetry.io/otel` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlptrace` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/metric` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/sdk` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/sdk/metric` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `go.opentelemetry.io/otel/trace` from 1.26.0 to 1.27.0 - [Release notes](https://github.com/open-telemetry/opentelemetry-go/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-go/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-go/compare/v1.26.0...v1.27.0) Updates `golang.org/x/crypto` from 0.22.0 to 0.23.0 - [Commits](https://github.com/golang/crypto/compare/v0.22.0...v0.23.0) Updates `golang.org/x/net` from 0.24.0 to 0.25.0 - [Commits](https://github.com/golang/net/compare/v0.24.0...v0.25.0) Updates `golang.org/x/oauth2` from 0.19.0 to 0.20.0 - [Commits](https://github.com/golang/oauth2/compare/v0.19.0...v0.20.0) Updates `google.golang.org/api` from 0.177.0 to 0.178.0 - [Release notes](https://github.com/googleapis/google-api-go-client/releases) - [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md) - [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.177.0...v0.178.0) Updates `google.golang.org/genproto/googleapis/rpc` from 0.0.0-20240429193739-8cf5692501f6 to 0.0.0-20240515191416-fc5f0ca64291 - [Commits](https://github.com/googleapis/go-genproto/commits) Updates `google.golang.org/grpc` from 1.63.2 to 1.64.0 - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.63.2...v1.64.0) Updates `google.golang.org/protobuf` from 1.34.0 to 1.34.1 --- updated-dependencies: - dependency-name: cloud.google.com/go/storage dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/aws/aws-sdk-go-v2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/aws/aws-sdk-go-v2/config dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go - dependency-name: github.com/aws/aws-sdk-go-v2/service/s3 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/caddyserver/certmagic dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/docker/docker dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go - dependency-name: github.com/jackc/pgx/v5 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/open-policy-agent/opa dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/prometheus/client_golang dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go - dependency-name: github.com/prometheus/procfs dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/rs/zerolog dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: github.com/shirou/gopsutil/v3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go - dependency-name: go.opentelemetry.io/otel dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/metric dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/sdk dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/sdk/metric dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: go.opentelemetry.io/otel/trace dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: golang.org/x/crypto dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: golang.org/x/net dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: golang.org/x/oauth2 dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: google.golang.org/api dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: google.golang.org/genproto/googleapis/rpc dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor dependency-group: go - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch dependency-group: go ... Signed-off-by: dependabot[bot] <support@github.com> * change acme pkg --------- Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Denis Mishin <dmishin@pomerium.com>
471 lines
12 KiB
Go
471 lines
12 KiB
Go
// Package autocert implements automatic management of TLS certificates.
|
|
package autocert
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"encoding/base64"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"sort"
|
|
"strings"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"github.com/caddyserver/certmagic"
|
|
"github.com/mholt/acmez/v2/acme"
|
|
"github.com/rs/zerolog"
|
|
|
|
"github.com/pomerium/pomerium/config"
|
|
"github.com/pomerium/pomerium/internal/httputil"
|
|
"github.com/pomerium/pomerium/internal/log"
|
|
"github.com/pomerium/pomerium/internal/telemetry/metrics"
|
|
"github.com/pomerium/pomerium/internal/urlutil"
|
|
"github.com/pomerium/pomerium/pkg/cryptutil"
|
|
)
|
|
|
|
var (
|
|
errObtainCertFailed = errors.New("obtain cert failed")
|
|
errRenewCertFailed = errors.New("renew cert failed")
|
|
|
|
// RenewCert is not thread-safe
|
|
renewCertLock sync.Mutex
|
|
)
|
|
|
|
const (
|
|
ocspRespCacheSize = 50000
|
|
renewalInterval = time.Minute * 10
|
|
renewalTimeout = time.Hour
|
|
)
|
|
|
|
// Manager manages TLS certificates.
|
|
type Manager struct {
|
|
src config.Source
|
|
acmeTemplate certmagic.ACMEIssuer
|
|
|
|
mu sync.RWMutex
|
|
config *config.Config
|
|
certmagic *certmagic.Config
|
|
acmeMgr atomic.Pointer[certmagic.ACMEIssuer]
|
|
srv *http.Server
|
|
acmeTLSALPNListener net.Listener
|
|
|
|
*ocspCache
|
|
|
|
config.ChangeDispatcher
|
|
}
|
|
|
|
// New creates a new autocert manager.
|
|
func New(src config.Source) (*Manager, error) {
|
|
return newManager(context.Background(), src, certmagic.DefaultACME, renewalInterval)
|
|
}
|
|
|
|
func newManager(ctx context.Context,
|
|
src config.Source,
|
|
acmeTemplate certmagic.ACMEIssuer,
|
|
checkInterval time.Duration,
|
|
) (*Manager, error) {
|
|
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
|
return c.Str("service", "autocert-manager")
|
|
})
|
|
|
|
ocspRespCache, err := newOCSPCache(ocspRespCacheSize)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
logger := getCertMagicLogger()
|
|
acmeTemplate.Logger = logger
|
|
|
|
mgr := &Manager{
|
|
src: src,
|
|
acmeTemplate: acmeTemplate,
|
|
ocspCache: ocspRespCache,
|
|
}
|
|
|
|
// set certmagic default storage cache, otherwise cert renewal loop will be based off
|
|
// certmagic's own default location
|
|
certmagicStorage, err := GetCertMagicStorage(ctx, src.GetConfig().Options.AutocertOptions.Folder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mgr.certmagic = certmagic.New(certmagic.NewCache(certmagic.CacheOptions{
|
|
GetConfigForCert: func(_ certmagic.Certificate) (*certmagic.Config, error) {
|
|
return mgr.certmagic, nil
|
|
},
|
|
Logger: logger,
|
|
}), certmagic.Config{
|
|
Logger: logger,
|
|
Storage: certmagicStorage,
|
|
})
|
|
|
|
err = mgr.update(ctx, src.GetConfig())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mgr.src.OnConfigChange(ctx, func(ctx context.Context, cfg *config.Config) {
|
|
err := mgr.update(ctx, cfg)
|
|
if err != nil {
|
|
log.Error(ctx).Err(err).Msg("autocert: error updating config")
|
|
return
|
|
}
|
|
|
|
cfg = mgr.GetConfig()
|
|
mgr.Trigger(ctx, cfg)
|
|
})
|
|
go func() {
|
|
ticker := time.NewTicker(checkInterval)
|
|
defer ticker.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
err := mgr.renewConfigCerts(ctx)
|
|
if err != nil {
|
|
log.Error(ctx).Err(err).Msg("autocert: error updating config")
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
return mgr, nil
|
|
}
|
|
|
|
func (mgr *Manager) getCertMagicConfig(ctx context.Context, cfg *config.Config) (*certmagic.Config, error) {
|
|
mgr.certmagic.MustStaple = cfg.Options.AutocertOptions.MustStaple
|
|
mgr.certmagic.OnDemand = nil // disable on-demand
|
|
var err error
|
|
mgr.certmagic.Storage, err = GetCertMagicStorage(ctx, cfg.Options.AutocertOptions.Folder)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
certs, err := cfg.AllCertificates()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
// add existing certs to the cache, and staple OCSP
|
|
for _, cert := range certs {
|
|
if _, err := mgr.certmagic.CacheUnmanagedTLSCertificate(ctx, cert, nil); err != nil {
|
|
return nil, fmt.Errorf("config: failed caching cert: %w", err)
|
|
}
|
|
}
|
|
acmeMgr := certmagic.NewACMEIssuer(mgr.certmagic, mgr.acmeTemplate)
|
|
err = configureCertificateAuthority(acmeMgr, cfg.Options.AutocertOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = configureExternalAccountBinding(acmeMgr, cfg.Options.AutocertOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
err = configureTrustedRoots(acmeMgr, cfg.Options.AutocertOptions)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
mgr.certmagic.Issuers = []certmagic.Issuer{acmeMgr}
|
|
mgr.acmeMgr.Store(acmeMgr)
|
|
|
|
return mgr.certmagic, nil
|
|
}
|
|
|
|
func (mgr *Manager) renewConfigCerts(ctx context.Context) error {
|
|
ctx, cancel := context.WithTimeout(ctx, renewalTimeout)
|
|
defer cancel()
|
|
|
|
mgr.mu.Lock()
|
|
defer mgr.mu.Unlock()
|
|
|
|
cfg := mgr.config
|
|
cm, err := mgr.getCertMagicConfig(ctx, cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
needsReload := false
|
|
var renew, ocsp []string
|
|
log.Debug(ctx).Strs("domains", sourceHostnames(cfg)).Msg("checking domains")
|
|
for _, domain := range sourceHostnames(cfg) {
|
|
cert, err := cm.CacheManagedCertificate(ctx, domain)
|
|
if err != nil {
|
|
// this happens for unmanaged certificates
|
|
continue
|
|
}
|
|
if cert.NeedsRenewal(cm) {
|
|
renew = append(renew, domain)
|
|
needsReload = true
|
|
}
|
|
if mgr.ocspCache.updated(domain, cert.OCSPStaple) {
|
|
ocsp = append(ocsp, domain)
|
|
needsReload = true
|
|
}
|
|
}
|
|
if !needsReload {
|
|
return nil
|
|
}
|
|
|
|
ctx = log.WithContext(ctx, func(c zerolog.Context) zerolog.Context {
|
|
if len(renew) > 0 {
|
|
c = c.Strs("renew_domains", renew)
|
|
}
|
|
if len(ocsp) > 0 {
|
|
c = c.Strs("ocsp_refresh", ocsp)
|
|
}
|
|
return c
|
|
})
|
|
log.Info(ctx).Msg("updating certificates")
|
|
|
|
cfg = mgr.src.GetConfig().Clone()
|
|
mgr.updateServer(ctx, cfg)
|
|
mgr.updateACMETLSALPNServer(ctx, cfg)
|
|
if err := mgr.updateAutocert(ctx, cfg); err != nil {
|
|
return err
|
|
}
|
|
|
|
mgr.config = cfg
|
|
mgr.Trigger(ctx, cfg)
|
|
return nil
|
|
}
|
|
|
|
func (mgr *Manager) update(ctx context.Context, cfg *config.Config) error {
|
|
cfg = cfg.Clone()
|
|
|
|
mgr.mu.Lock()
|
|
defer mgr.mu.Unlock()
|
|
defer func() { mgr.config = cfg }()
|
|
|
|
mgr.updateServer(ctx, cfg)
|
|
mgr.updateACMETLSALPNServer(ctx, cfg)
|
|
return mgr.updateAutocert(ctx, cfg)
|
|
}
|
|
|
|
// obtainCert obtains a certificate for given domain, use cached manager if cert exists there.
|
|
func (mgr *Manager) obtainCert(ctx context.Context, domain string, cm *certmagic.Config) (certmagic.Certificate, error) {
|
|
cert, err := cm.CacheManagedCertificate(ctx, domain)
|
|
if err != nil {
|
|
log.Info(ctx).Str("domain", domain).Msg("obtaining certificate")
|
|
err = cm.ObtainCertSync(ctx, domain)
|
|
if err != nil {
|
|
log.Error(ctx).Err(err).Msg("autocert failed to obtain client certificate")
|
|
return certmagic.Certificate{}, errObtainCertFailed
|
|
}
|
|
metrics.RecordAutocertRenewal()
|
|
cert, err = cm.CacheManagedCertificate(ctx, domain)
|
|
}
|
|
return cert, err
|
|
}
|
|
|
|
// renewCert attempts to renew given certificate.
|
|
func (mgr *Manager) renewCert(ctx context.Context, domain string, cert certmagic.Certificate, cm *certmagic.Config) (certmagic.Certificate, error) {
|
|
expired := time.Now().After(cert.Leaf.NotAfter)
|
|
log.Info(ctx).Str("domain", domain).Msg("renewing certificate")
|
|
renewCertLock.Lock()
|
|
err := cm.RenewCertSync(ctx, domain, false)
|
|
renewCertLock.Unlock()
|
|
if err != nil {
|
|
if expired {
|
|
return certmagic.Certificate{}, errRenewCertFailed
|
|
}
|
|
log.Warn(ctx).Err(err).Msg("renew client certificated failed, use existing cert")
|
|
}
|
|
return cm.CacheManagedCertificate(ctx, domain)
|
|
}
|
|
|
|
func (mgr *Manager) updateAutocert(ctx context.Context, cfg *config.Config) error {
|
|
if !cfg.Options.AutocertOptions.Enable {
|
|
mgr.acmeMgr.Store(nil)
|
|
return nil
|
|
}
|
|
|
|
cm, err := mgr.getCertMagicConfig(ctx, cfg)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, domain := range sourceHostnames(cfg) {
|
|
cert, err := mgr.obtainCert(ctx, domain, cm)
|
|
if err == nil && cert.NeedsRenewal(cm) {
|
|
cert, err = mgr.renewCert(ctx, domain, cert, cm)
|
|
}
|
|
if err != nil {
|
|
log.Error(ctx).Err(err).Msg("autocert: failed to obtain client certificate")
|
|
continue
|
|
}
|
|
|
|
log.Info(ctx).Strs("names", cert.Names).Msg("autocert: added certificate")
|
|
cfg.AutoCertificates = append(cfg.AutoCertificates, cert.Certificate)
|
|
}
|
|
|
|
metrics.RecordAutocertCertificates(cfg.AutoCertificates)
|
|
|
|
return nil
|
|
}
|
|
|
|
func (mgr *Manager) updateServer(ctx context.Context, cfg *config.Config) {
|
|
if mgr.srv != nil {
|
|
// nothing to do if the address hasn't changed
|
|
if mgr.srv.Addr == cfg.Options.HTTPRedirectAddr {
|
|
return
|
|
}
|
|
// close immediately, don't care about the error
|
|
_ = mgr.srv.Close()
|
|
mgr.srv = nil
|
|
}
|
|
|
|
if cfg.Options.HTTPRedirectAddr == "" {
|
|
return
|
|
}
|
|
|
|
redirect := httputil.RedirectHandler()
|
|
|
|
hsrv := &http.Server{
|
|
Addr: cfg.Options.HTTPRedirectAddr,
|
|
Handler: http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if mgr.handleHTTPChallenge(w, r) {
|
|
return
|
|
}
|
|
redirect.ServeHTTP(w, r)
|
|
}),
|
|
}
|
|
go func() {
|
|
log.Info(ctx).Str("addr", hsrv.Addr).Msg("starting http redirect server")
|
|
err := hsrv.ListenAndServe()
|
|
if err != nil {
|
|
log.Error(ctx).Err(err).Msg("failed to run http redirect server")
|
|
}
|
|
}()
|
|
mgr.srv = hsrv
|
|
}
|
|
|
|
func (mgr *Manager) updateACMETLSALPNServer(ctx context.Context, cfg *config.Config) {
|
|
addr := net.JoinHostPort("127.0.0.1", cfg.ACMETLSALPNPort)
|
|
if mgr.acmeTLSALPNListener != nil {
|
|
_ = mgr.acmeTLSALPNListener.Close()
|
|
mgr.acmeTLSALPNListener = nil
|
|
}
|
|
|
|
tlsConfig := mgr.certmagic.TLSConfig()
|
|
ln, err := tls.Listen("tcp", addr, tlsConfig)
|
|
if err != nil {
|
|
log.Error(ctx).Err(err).Msg("failed to run acme tls alpn server")
|
|
return
|
|
}
|
|
mgr.acmeTLSALPNListener = ln
|
|
|
|
go func() {
|
|
for {
|
|
conn, err := ln.Accept()
|
|
if errors.Is(err, net.ErrClosed) {
|
|
return
|
|
} else if err != nil {
|
|
continue
|
|
}
|
|
_ = conn.Close()
|
|
}
|
|
}()
|
|
}
|
|
|
|
func (mgr *Manager) handleHTTPChallenge(w http.ResponseWriter, r *http.Request) bool {
|
|
return mgr.acmeMgr.Load().HandleHTTPChallenge(w, r)
|
|
}
|
|
|
|
// GetConfig gets the config.
|
|
func (mgr *Manager) GetConfig() *config.Config {
|
|
mgr.mu.RLock()
|
|
defer mgr.mu.RUnlock()
|
|
|
|
return mgr.config
|
|
}
|
|
|
|
// configureCertificateAuthority configures the acmeMgr ACME Certificate Authority settings.
|
|
func configureCertificateAuthority(acmeMgr *certmagic.ACMEIssuer, opts config.AutocertOptions) error {
|
|
acmeMgr.Agreed = true
|
|
if opts.UseStaging {
|
|
acmeMgr.CA = acmeMgr.TestCA
|
|
}
|
|
if opts.CA != "" {
|
|
acmeMgr.CA = opts.CA // when a CA is specified, it overrides the staging setting
|
|
}
|
|
if opts.Email != "" {
|
|
acmeMgr.Email = opts.Email
|
|
} else {
|
|
acmeMgr.Email = " " // intentionally set to a space so that certmagic doesn't prompt for an email address
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// configureExternalAccountBinding configures the acmeMgr ACME External Account Binding settings.
|
|
func configureExternalAccountBinding(acmeMgr *certmagic.ACMEIssuer, opts config.AutocertOptions) error {
|
|
if opts.EABKeyID != "" || opts.EABMACKey != "" {
|
|
acmeMgr.ExternalAccount = &acme.EAB{}
|
|
}
|
|
if opts.EABKeyID != "" {
|
|
acmeMgr.ExternalAccount.KeyID = opts.EABKeyID
|
|
}
|
|
if opts.EABMACKey != "" {
|
|
_, err := base64.RawURLEncoding.DecodeString(opts.EABMACKey)
|
|
if err != nil {
|
|
return fmt.Errorf("config: decoding base64-urlencoded MAC Key: %w", err)
|
|
}
|
|
acmeMgr.ExternalAccount.MACKey = opts.EABMACKey
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// configureTrustedRoots configures the acmeMgr x509 roots to trust when communicating with an ACME CA.
|
|
func configureTrustedRoots(acmeMgr *certmagic.ACMEIssuer, opts config.AutocertOptions) error {
|
|
if opts.TrustedCA != "" {
|
|
// pool effectively contains the certificate(s) in the TrustedCA base64 PEM appended to the system roots
|
|
pool, err := cryptutil.GetCertPool(opts.TrustedCA, "")
|
|
if err != nil {
|
|
return fmt.Errorf("config: creating trusted certificate pool: %w", err)
|
|
}
|
|
acmeMgr.TrustedRoots = pool
|
|
}
|
|
if opts.TrustedCAFile != "" {
|
|
// pool effectively contains the certificate(s) in TrustedCAFile appended to the system roots
|
|
pool, err := cryptutil.GetCertPool("", opts.TrustedCAFile)
|
|
if err != nil {
|
|
return fmt.Errorf("config: creating trusted certificate pool: %w", err)
|
|
}
|
|
acmeMgr.TrustedRoots = pool
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func sourceHostnames(cfg *config.Config) []string {
|
|
policies := cfg.Options.GetAllPolicies()
|
|
|
|
if len(policies) == 0 {
|
|
return nil
|
|
}
|
|
|
|
dedupe := map[string]struct{}{}
|
|
for _, p := range policies {
|
|
if u, _ := urlutil.ParseAndValidateURL(p.From); u != nil && !strings.Contains(u.Host, "*") {
|
|
dedupe[u.Hostname()] = struct{}{}
|
|
}
|
|
}
|
|
if cfg.Options.AuthenticateURLString != "" {
|
|
if u, _ := cfg.Options.GetAuthenticateURL(); u != nil {
|
|
dedupe[u.Hostname()] = struct{}{}
|
|
}
|
|
}
|
|
|
|
// remove any hosted authenticate URLs
|
|
for _, domain := range urlutil.HostedAuthenticateDomains {
|
|
delete(dedupe, domain)
|
|
}
|
|
|
|
var h []string
|
|
for k := range dedupe {
|
|
h = append(h, k)
|
|
}
|
|
sort.Strings(h)
|
|
|
|
return h
|
|
}
|