mirror of
https://github.com/pomerium/pomerium.git
synced 2025-04-28 18:06:34 +02:00
config: generate cookie secret if not set in all-in-one mode (#3742)
* config: generate cookie secret if not set in all-in-one mode * fix tests * config: add warning about cookie_secret * breakup lines
This commit is contained in:
parent
2c9087f5e7
commit
9413123c0f
8 changed files with 111 additions and 17 deletions
|
@ -32,8 +32,6 @@ func TestOptions_Validate(t *testing.T) {
|
|||
emptyClientID.ClientID = ""
|
||||
emptyClientSecret := newTestOptions(t)
|
||||
emptyClientSecret.ClientSecret = ""
|
||||
emptyCookieSecret := newTestOptions(t)
|
||||
emptyCookieSecret.CookieSecret = ""
|
||||
invalidCookieSecret := newTestOptions(t)
|
||||
invalidCookieSecret.CookieSecret = "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw^"
|
||||
shortCookieLength := newTestOptions(t)
|
||||
|
@ -53,7 +51,6 @@ func TestOptions_Validate(t *testing.T) {
|
|||
}{
|
||||
{"minimum options", good, false},
|
||||
{"nil options", &config.Options{}, true},
|
||||
{"no cookie secret", emptyCookieSecret, true},
|
||||
{"invalid cookie secret", invalidCookieSecret, true},
|
||||
{"short cookie secret", shortCookieLength, true},
|
||||
{"no shared secret", badSharedKey, true},
|
||||
|
|
|
@ -100,7 +100,7 @@ func getUpstreamProtocolForPolicy(ctx context.Context, policy *config.Policy) up
|
|||
upstreamProtocol := upstreamProtocolAuto
|
||||
if policy.AllowWebsockets {
|
||||
// #2388, force http/1 when using web sockets
|
||||
log.Info(ctx).Msg("envoyconfig: forcing http/1.1 due to web socket support")
|
||||
log.WarnWebSocketHTTP1_1(getClusterID(policy))
|
||||
upstreamProtocol = upstreamProtocolHTTP1
|
||||
}
|
||||
return upstreamProtocol
|
||||
|
|
|
@ -985,7 +985,7 @@ func (o *Options) GetSharedKey() ([]byte, error) {
|
|||
sharedKey = string(bs)
|
||||
}
|
||||
// mutual auth between services on the same host can be generated at runtime
|
||||
if IsAll(o.Services) && o.SharedKey == "" && o.DataBrokerStorageType == StorageInMemoryName {
|
||||
if IsAll(o.Services) && sharedKey == "" {
|
||||
sharedKey = randomSharedKey
|
||||
}
|
||||
if sharedKey == "" {
|
||||
|
@ -1188,6 +1188,15 @@ func (o *Options) GetCookieSecret() ([]byte, error) {
|
|||
}
|
||||
cookieSecret = string(bs)
|
||||
}
|
||||
|
||||
if IsAll(o.Services) && cookieSecret == "" {
|
||||
log.WarnCookieSecret()
|
||||
cookieSecret = randomSharedKey
|
||||
}
|
||||
if cookieSecret == "" {
|
||||
return nil, errors.New("empty cookie secret")
|
||||
}
|
||||
|
||||
return base64.StdEncoding.DecodeString(cookieSecret)
|
||||
}
|
||||
|
||||
|
|
|
@ -53,11 +53,6 @@ func Test_Validate(t *testing.T) {
|
|||
badSignoutRedirectURL := testOptions()
|
||||
badSignoutRedirectURL.SignOutRedirectURLString = "--"
|
||||
|
||||
missingSharedSecretWithPersistence := testOptions()
|
||||
missingSharedSecretWithPersistence.SharedKey = ""
|
||||
missingSharedSecretWithPersistence.DataBrokerStorageType = StorageRedisName
|
||||
missingSharedSecretWithPersistence.DataBrokerStorageConnectionString = "redis://somehost:6379"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
testOpts *Options
|
||||
|
@ -71,7 +66,6 @@ func Test_Validate(t *testing.T) {
|
|||
{"invalid databroker storage type", invalidStorageType, true},
|
||||
{"missing databroker storage dsn", missingStorageDSN, true},
|
||||
{"invalid signout redirect url", badSignoutRedirectURL, true},
|
||||
{"no shared key with databroker persistence", missingSharedSecretWithPersistence, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
@ -776,6 +770,36 @@ func TestOptions_GetSetResponseHeaders(t *testing.T) {
|
|||
})
|
||||
}
|
||||
|
||||
func TestOptions_GetSharedKey(t *testing.T) {
|
||||
t.Run("default", func(t *testing.T) {
|
||||
o := NewDefaultOptions()
|
||||
bs, err := o.GetSharedKey()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, randomSharedKey, base64.StdEncoding.EncodeToString(bs))
|
||||
})
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
o := NewDefaultOptions()
|
||||
o.Services = ServiceProxy
|
||||
_, err := o.GetSharedKey()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func TestOptions_GetCookieSecret(t *testing.T) {
|
||||
t.Run("default", func(t *testing.T) {
|
||||
o := NewDefaultOptions()
|
||||
bs, err := o.GetCookieSecret()
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, randomSharedKey, base64.StdEncoding.EncodeToString(bs))
|
||||
})
|
||||
t.Run("missing", func(t *testing.T) {
|
||||
o := NewDefaultOptions()
|
||||
o.Services = ServiceProxy
|
||||
_, err := o.GetCookieSecret()
|
||||
assert.Error(t, err)
|
||||
})
|
||||
}
|
||||
|
||||
func encodeCert(cert *tls.Certificate) []byte {
|
||||
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Certificate[0]})
|
||||
}
|
||||
|
|
42
internal/log/warnings.go
Normal file
42
internal/log/warnings.go
Normal file
|
@ -0,0 +1,42 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
|
||||
"github.com/pomerium/pomerium/internal/syncutil"
|
||||
)
|
||||
|
||||
var warnCookieSecretOnce sync.Once
|
||||
|
||||
// WarnCookieSecret warns about the cookie secret.
|
||||
func WarnCookieSecret() {
|
||||
warnCookieSecretOnce.Do(func() {
|
||||
Warn(context.Background()).
|
||||
Msg("using a generated COOKIE_SECRET. " +
|
||||
"Set the COOKIE_SECRET to avoid users being logged out on restart. " +
|
||||
"https://www.pomerium.com/docs/reference/cookie-secret")
|
||||
})
|
||||
}
|
||||
|
||||
var warnNoTLSCertificateOnce syncutil.OnceMap[string]
|
||||
|
||||
// WarnNoTLSCertificate warns about no TLS certificate.
|
||||
func WarnNoTLSCertificate(domain string) {
|
||||
warnNoTLSCertificateOnce.Do(domain, func() {
|
||||
Warn(context.Background()).
|
||||
Str("domain", domain).
|
||||
Msg("no TLS certificate found for domain, using a self-signed certificate")
|
||||
})
|
||||
}
|
||||
|
||||
var warnWebSocketHTTP1_1Once syncutil.OnceMap[string]
|
||||
|
||||
// WarnWebSocketHTTP1_1 warns about falling back to http 1.1 due to web socket support.
|
||||
func WarnWebSocketHTTP1_1(clusterID string) {
|
||||
warnWebSocketHTTP1_1Once.Do(clusterID, func() {
|
||||
Warn(context.Background()).
|
||||
Str("cluster-id", clusterID).
|
||||
Msg("forcing http/1.1 due to web socket support")
|
||||
})
|
||||
}
|
27
internal/syncutil/syncutil.go
Normal file
27
internal/syncutil/syncutil.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
// Package syncutil contains methods for working with sync code.
|
||||
package syncutil
|
||||
|
||||
import (
|
||||
"sync"
|
||||
)
|
||||
|
||||
// A OnceMap is a collection sync.Onces accessible by a key. The zero value is usable.
|
||||
type OnceMap[T comparable] struct {
|
||||
mu sync.Mutex
|
||||
m map[T]*sync.Once
|
||||
}
|
||||
|
||||
// Do runs f once.
|
||||
func (o *OnceMap[T]) Do(key T, f func()) {
|
||||
o.mu.Lock()
|
||||
if o.m == nil {
|
||||
o.m = make(map[T]*sync.Once)
|
||||
}
|
||||
oo, ok := o.m[key]
|
||||
if !ok {
|
||||
oo = new(sync.Once)
|
||||
o.m[key] = oo
|
||||
}
|
||||
o.mu.Unlock()
|
||||
oo.Do(f)
|
||||
}
|
|
@ -55,9 +55,7 @@ func GetCertificateForDomain(certificates []tls.Certificate, domain string) (*tl
|
|||
}
|
||||
}
|
||||
|
||||
log.Error(context.Background()).
|
||||
Str("domain", domain).
|
||||
Msg("cryptutil: no TLS certificate found for domain, using self-signed certificate")
|
||||
log.WarnNoTLSCertificate(domain)
|
||||
|
||||
// finally fall back to a generated, self-signed certificate
|
||||
return GenerateSelfSignedCertificate(domain)
|
||||
|
|
|
@ -41,8 +41,6 @@ func TestOptions_Validate(t *testing.T) {
|
|||
badAuthURL.AuthenticateURLString = "BAD_URL"
|
||||
authenticateBadScheme := testOptions(t)
|
||||
authenticateBadScheme.AuthenticateURLString = "authenticate.corp.beyondperimeter.com"
|
||||
emptyCookieSecret := testOptions(t)
|
||||
emptyCookieSecret.CookieSecret = ""
|
||||
invalidCookieSecret := testOptions(t)
|
||||
invalidCookieSecret.CookieSecret = "OromP1gurwGWjQPYb1nNgSxtbVB5NnLzX6z5WOKr0Yw^"
|
||||
shortCookieLength := testOptions(t)
|
||||
|
@ -62,7 +60,6 @@ func TestOptions_Validate(t *testing.T) {
|
|||
}{
|
||||
{"good - minimum options", good, false},
|
||||
{"nil options", &config.Options{}, true},
|
||||
{"no cookie secret", emptyCookieSecret, true},
|
||||
{"invalid cookie secret", invalidCookieSecret, true},
|
||||
{"short cookie secret", shortCookieLength, true},
|
||||
{"no shared secret", badSharedKey, true},
|
||||
|
|
Loading…
Add table
Reference in a new issue