mirror of
https://github.com/pomerium/pomerium.git
synced 2025-06-03 11:22:45 +02:00
Add an option to request certificate with Must-Staple. (#697)
This commit is contained in:
parent
8856577f39
commit
8fc1e9cca8
5 changed files with 92 additions and 23 deletions
|
@ -11,6 +11,31 @@ import (
|
|||
"github.com/pomerium/pomerium/internal/log"
|
||||
)
|
||||
|
||||
// AutocertOptions contains the options to control the behavior of autocert.
|
||||
type AutocertOptions struct {
|
||||
// Enable enables fully automated certificate management including issuance
|
||||
// and renewal from LetsEncrypt. Must be used in conjunction with Folder.
|
||||
Enable bool `mapstructure:"autocert" yaml:"autocert,omitempty"`
|
||||
|
||||
// UseStaging tells autocert to use Let's Encrypt's staging CA which
|
||||
// has less strict usage limits then the (default) production CA.
|
||||
//
|
||||
// https://letsencrypt.org/docs/staging-environment/
|
||||
UseStaging bool `mapstructure:"autocert_use_staging" yaml:"autocert_use_staging,omitempty"`
|
||||
|
||||
// MustStaple will cause autocert to request a certificate with
|
||||
// status_request extension. This will allow the TLS client (the browser)
|
||||
// to fail immediately if Pomerium failed to get an OCSP staple.
|
||||
// See also https://tools.ietf.org/html/rfc7633
|
||||
// Only used when Enable is true.
|
||||
MustStaple bool `mapstructure:"autocert_must_staple" yaml:"autocert_must_staple,omitempty"`
|
||||
|
||||
// Folder specifies the location to store, and load autocert managed
|
||||
// TLS certificates.
|
||||
// defaults to $XDG_DATA_HOME/pomerium
|
||||
Folder string `mapstructure:"autocert_dir" yaml:"autocert_dir,omitempty"`
|
||||
}
|
||||
|
||||
// AutocertManager manages Let's Encrypt certificates based on configuration options.
|
||||
var AutocertManager = newAutocertManager()
|
||||
|
||||
|
@ -32,10 +57,11 @@ func (mgr *autocertManager) getConfig(options *Options) (*certmagic.Config, erro
|
|||
cm := mgr.certmagic
|
||||
if cm == nil {
|
||||
cm = certmagic.NewDefault()
|
||||
cm.MustStaple = options.AutocertOptions.MustStaple
|
||||
}
|
||||
|
||||
cm.OnDemand = nil // disable on-demand
|
||||
cm.Storage = &certmagic.FileStorage{Path: options.AutoCertFolder}
|
||||
cm.Storage = &certmagic.FileStorage{Path: options.AutocertOptions.Folder}
|
||||
// add existing certs to the cache, and staple OCSP
|
||||
for _, cert := range options.Certificates {
|
||||
if err := cm.CacheUnmanagedTLSCertificate(cert, nil); err != nil {
|
||||
|
@ -44,7 +70,7 @@ func (mgr *autocertManager) getConfig(options *Options) (*certmagic.Config, erro
|
|||
}
|
||||
acmeMgr := certmagic.NewACMEManager(cm, certmagic.DefaultACME)
|
||||
acmeMgr.Agreed = true
|
||||
if options.AutoCertUseStaging {
|
||||
if options.AutocertOptions.UseStaging {
|
||||
acmeMgr.CA = certmagic.LetsEncryptStagingCA
|
||||
}
|
||||
acmeMgr.DisableTLSALPNChallenge = true
|
||||
|
@ -55,7 +81,7 @@ func (mgr *autocertManager) getConfig(options *Options) (*certmagic.Config, erro
|
|||
}
|
||||
|
||||
func (mgr *autocertManager) update(options *Options) error {
|
||||
if !options.AutoCert {
|
||||
if !options.AutocertOptions.Enable {
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -69,21 +69,6 @@ type Options struct {
|
|||
// This should be used only for testing.
|
||||
InsecureServer bool `mapstructure:"insecure_server" yaml:"insecure_server,omitempty"`
|
||||
|
||||
// AutoCert enables fully automated certificate management including issuance
|
||||
// and renewal from LetsEncrypt. Must be used in conjunction with AutoCertFolder.
|
||||
AutoCert bool `mapstructure:"autocert" yaml:"autocert,omitempty"`
|
||||
|
||||
// AutoCertFolder specifies the location to store, and load autocert managed
|
||||
// TLS certificates.
|
||||
// defaults to $XDG_DATA_HOME/pomerium
|
||||
AutoCertFolder string `mapstructure:"autocert_dir" yaml:"autocert_dir,omitempty"`
|
||||
|
||||
// AutoCertUseStaging tells autocert to use Let's Encrypt's staging CA which
|
||||
// has less strict usage limits then the (default) production CA.
|
||||
//
|
||||
// https://letsencrypt.org/docs/staging-environment/
|
||||
AutoCertUseStaging bool `mapstructure:"autocert_use_staging" yaml:"autocert_use_staging,omitempty"`
|
||||
|
||||
CertificateFiles []certificateFilePair `mapstructure:"certificates" yaml:"certificates,omitempty"`
|
||||
|
||||
// Cert and Key is the x509 certificate used to create the HTTPS server.
|
||||
|
@ -245,6 +230,8 @@ type Options struct {
|
|||
ClientCAFile string `mapstructure:"client_ca_file" yaml:"client_ca_file,omitempty"`
|
||||
|
||||
viper *viper.Viper
|
||||
|
||||
AutocertOptions `mapstructure:",squash" yaml:",inline"`
|
||||
}
|
||||
|
||||
type certificateFilePair struct {
|
||||
|
@ -280,8 +267,11 @@ var defaultOptions = Options{
|
|||
GRPCServerMaxConnectionAgeGrace: 5 * time.Minute,
|
||||
CacheStore: "autocache",
|
||||
AuthenticateCallbackPath: "/oauth2/callback",
|
||||
AutoCertFolder: dataDir(),
|
||||
TracingSampleRate: 0.0001,
|
||||
|
||||
AutocertOptions: AutocertOptions{
|
||||
Folder: dataDir(),
|
||||
},
|
||||
}
|
||||
|
||||
// NewDefaultOptions returns a copy the default options. It's the caller's
|
||||
|
@ -448,6 +438,16 @@ func bindEnvs(o *Options, v *viper.Viper) error {
|
|||
if err != nil {
|
||||
return fmt.Errorf("failed to bind field 'HeadersEnv' to env var 'HEADERS': %w", err)
|
||||
}
|
||||
// autocert options
|
||||
ao := reflect.TypeOf(o.AutocertOptions)
|
||||
for i := 0; i < ao.NumField(); i++ {
|
||||
field := ao.Field(i)
|
||||
envName := field.Tag.Get(tagName)
|
||||
err := v.BindEnv(envName)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to bind field '%s' to env var '%s': %w", field.Name, envName, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -314,6 +314,35 @@ func Test_NewOptionsFromConfigEnvVar(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func Test_AutoCertOptionsFromEnvVar(t *testing.T) {
|
||||
envs := map[string]string{
|
||||
"AUTOCERT": "true",
|
||||
"AUTOCERT_DIR": "/test",
|
||||
"AUTOCERT_MUST_STAPLE": "true",
|
||||
|
||||
"INSECURE_SERVER": "true",
|
||||
}
|
||||
for k, v := range envs {
|
||||
os.Setenv(k, v)
|
||||
defer os.Unsetenv(k)
|
||||
}
|
||||
|
||||
o, err := NewOptionsFromConfig("")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !o.AutocertOptions.Enable {
|
||||
t.Error("o.AutocertOptions.Enable: want true, got false")
|
||||
}
|
||||
if !o.AutocertOptions.MustStaple {
|
||||
t.Error("o.AutocertOptions.MustStaple: want true, got false")
|
||||
}
|
||||
if o.AutocertOptions.Folder != "/test" {
|
||||
t.Errorf("o.AutocertOptions.Folder: want /test, got %s", o.AutocertOptions.Folder)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type mockService struct {
|
||||
fail bool
|
||||
Updated bool
|
||||
|
|
|
@ -67,6 +67,24 @@ Autocert requires that ports `80`/`443` be accessible from the internet in order
|
|||
|
||||
:::
|
||||
|
||||
### Autocert Must-Staple
|
||||
|
||||
- Environmental Variable: `AUTOCERT_MUST_STAPLE`
|
||||
- Config File Key: `autocert_must_staple`
|
||||
- Type: `bool`
|
||||
- Optional
|
||||
|
||||
If true, cause autocert to request a certificate with `status_request`
|
||||
extension (commonly called `Must-Staple`). This allows the TLS client
|
||||
(the browser) to fail immediately if the TLS handshake doesn't include
|
||||
OCSP stapling information. Only used when [Autocert](./#autocert) is
|
||||
true.
|
||||
|
||||
NOTE: this only takes effect the next time Pomerium renews your
|
||||
certificates.
|
||||
|
||||
See also https://tools.ietf.org/html/rfc7633 for more context.
|
||||
|
||||
### Autocert Directory
|
||||
|
||||
- Environmental Variable: either `AUTOCERT_DIR`
|
||||
|
|
4
go.sum
4
go.sum
|
@ -735,7 +735,6 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
|||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5 h1:tycE03LOZYQNhDpS27tcQdAzLCVMaj7QT2SXxebnpCM=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
@ -788,12 +787,10 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQ
|
|||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/cookieo9/resources-go.v2 v2.0.0-20150225115733-d27c04069d0d h1:YjTGSRV59gG1DHCq68v2B771I9dGFxvMkugf7OKglpk=
|
||||
gopkg.in/cookieo9/resources-go.v2 v2.0.0-20150225115733-d27c04069d0d/go.mod h1:kbUs813+JgwKQdecaTv87br/FZUaSEuPj8vbr2vq8sY=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/h2non/gock.v1 v1.0.15/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE=
|
||||
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
|
@ -806,7 +803,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
|||
gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w=
|
||||
gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue