Move Cipher intialisation to session store initialisation
This commit is contained in:
		
							parent
							
								
									d9af3ffc5e
								
							
						
					
					
						commit
						c8dbf1cf60
					
				|  | @ -1,12 +1,9 @@ | ||||||
| package options | package options | ||||||
| 
 | 
 | ||||||
| import "github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" |  | ||||||
| 
 |  | ||||||
| // SessionOptions contains configuration options for the SessionStore providers.
 | // SessionOptions contains configuration options for the SessionStore providers.
 | ||||||
| type SessionOptions struct { | type SessionOptions struct { | ||||||
| 	Type   string            `flag:"session-store-type" cfg:"session_store_type"` | 	Type  string            `flag:"session-store-type" cfg:"session_store_type"` | ||||||
| 	Cipher encryption.Cipher `cfg:",internal"` | 	Redis RedisStoreOptions `cfg:",squash"` | ||||||
| 	Redis  RedisStoreOptions `cfg:",squash"` |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // CookieSessionStoreType is used to indicate the CookieSessionStore should be
 | // CookieSessionStoreType is used to indicate the CookieSessionStore should be
 | ||||||
|  |  | ||||||
|  | @ -126,9 +126,9 @@ func (s *SessionStore) makeCookie(req *http.Request, name string, value string, | ||||||
| 
 | 
 | ||||||
| // NewCookieSessionStore initialises a new instance of the SessionStore from
 | // NewCookieSessionStore initialises a new instance of the SessionStore from
 | ||||||
| // the configuration given
 | // the configuration given
 | ||||||
| func NewCookieSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessions.SessionStore, error) { | func NewCookieSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions, cipher encryption.Cipher) (sessions.SessionStore, error) { | ||||||
| 	return &SessionStore{ | 	return &SessionStore{ | ||||||
| 		CookieCipher:  opts.Cipher, | 		CookieCipher:  cipher, | ||||||
| 		CookieOptions: cookieOpts, | 		CookieOptions: cookieOpts, | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ type SessionStore struct { | ||||||
| 
 | 
 | ||||||
| // NewRedisSessionStore initialises a new instance of the SessionStore from
 | // NewRedisSessionStore initialises a new instance of the SessionStore from
 | ||||||
| // the configuration given
 | // the configuration given
 | ||||||
| func NewRedisSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessions.SessionStore, error) { | func NewRedisSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions, cipher encryption.Cipher) (sessions.SessionStore, error) { | ||||||
| 	client, err := newRedisCmdable(opts.Redis) | 	client, err := newRedisCmdable(opts.Redis) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("error constructing redis client: %v", err) | 		return nil, fmt.Errorf("error constructing redis client: %v", err) | ||||||
|  | @ -47,7 +47,7 @@ func NewRedisSessionStore(opts *options.SessionOptions, cookieOpts *options.Cook | ||||||
| 
 | 
 | ||||||
| 	rs := &SessionStore{ | 	rs := &SessionStore{ | ||||||
| 		Client:        client, | 		Client:        client, | ||||||
| 		CookieCipher:  opts.Cipher, | 		CookieCipher:  cipher, | ||||||
| 		CookieOptions: cookieOpts, | 		CookieOptions: cookieOpts, | ||||||
| 	} | 	} | ||||||
| 	return rs, nil | 	return rs, nil | ||||||
|  |  | ||||||
|  | @ -1,6 +1,7 @@ | ||||||
| package redis | package redis | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"crypto/rand" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/httptest" | 	"net/http/httptest" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  | @ -10,11 +11,19 @@ import ( | ||||||
| 	"github.com/alicebob/miniredis/v2" | 	"github.com/alicebob/miniredis/v2" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
| 	"github.com/stretchr/testify/require" | 	"github.com/stretchr/testify/require" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func TestRedisStore(t *testing.T) { | func TestRedisStore(t *testing.T) { | ||||||
|  | 	secret := make([]byte, 32) | ||||||
|  | 	_, err := rand.Read(secret) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
|  | 	cipher, err := encryption.NewBase64Cipher(encryption.NewCFBCipher, encryption.SecretBytes(string(secret))) | ||||||
|  | 	assert.NoError(t, err) | ||||||
|  | 
 | ||||||
| 	t.Run("save session on redis standalone", func(t *testing.T) { | 	t.Run("save session on redis standalone", func(t *testing.T) { | ||||||
| 		redisServer, err := miniredis.Run() | 		redisServer, err := miniredis.Run() | ||||||
| 		require.NoError(t, err) | 		require.NoError(t, err) | ||||||
|  | @ -25,7 +34,7 @@ func TestRedisStore(t *testing.T) { | ||||||
| 			Host:   redisServer.Addr(), | 			Host:   redisServer.Addr(), | ||||||
| 		} | 		} | ||||||
| 		opts.Session.Redis.ConnectionURL = redisURL.String() | 		opts.Session.Redis.ConnectionURL = redisURL.String() | ||||||
| 		redisStore, err := NewRedisSessionStore(&opts.Session, &opts.Cookie) | 		redisStore, err := NewRedisSessionStore(&opts.Session, &opts.Cookie, cipher) | ||||||
| 		require.NoError(t, err) | 		require.NoError(t, err) | ||||||
| 		err = redisStore.Save( | 		err = redisStore.Save( | ||||||
| 			httptest.NewRecorder(), | 			httptest.NewRecorder(), | ||||||
|  | @ -49,7 +58,7 @@ func TestRedisStore(t *testing.T) { | ||||||
| 		opts.Session.Redis.SentinelConnectionURLs = []string{sentinelURL.String()} | 		opts.Session.Redis.SentinelConnectionURLs = []string{sentinelURL.String()} | ||||||
| 		opts.Session.Redis.UseSentinel = true | 		opts.Session.Redis.UseSentinel = true | ||||||
| 		opts.Session.Redis.SentinelMasterName = sentinel.MasterInfo().Name | 		opts.Session.Redis.SentinelMasterName = sentinel.MasterInfo().Name | ||||||
| 		redisStore, err := NewRedisSessionStore(&opts.Session, &opts.Cookie) | 		redisStore, err := NewRedisSessionStore(&opts.Session, &opts.Cookie, cipher) | ||||||
| 		require.NoError(t, err) | 		require.NoError(t, err) | ||||||
| 		err = redisStore.Save( | 		err = redisStore.Save( | ||||||
| 			httptest.NewRecorder(), | 			httptest.NewRecorder(), | ||||||
|  |  | ||||||
|  | @ -5,17 +5,22 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/sessions" | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/encryption" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/cookie" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/redis" | 	"github.com/oauth2-proxy/oauth2-proxy/pkg/sessions/redis" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // NewSessionStore creates a SessionStore from the provided configuration
 | // NewSessionStore creates a SessionStore from the provided configuration
 | ||||||
| func NewSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessions.SessionStore, error) { | func NewSessionStore(opts *options.SessionOptions, cookieOpts *options.CookieOptions) (sessions.SessionStore, error) { | ||||||
|  | 	cipher, err := encryption.NewBase64Cipher(encryption.NewCFBCipher, encryption.SecretBytes(cookieOpts.Secret)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("error initialising cipher: %v", err) | ||||||
|  | 	} | ||||||
| 	switch opts.Type { | 	switch opts.Type { | ||||||
| 	case options.CookieSessionStoreType: | 	case options.CookieSessionStoreType: | ||||||
| 		return cookie.NewCookieSessionStore(opts, cookieOpts) | 		return cookie.NewCookieSessionStore(opts, cookieOpts, cipher) | ||||||
| 	case options.RedisSessionStoreType: | 	case options.RedisSessionStoreType: | ||||||
| 		return redis.NewRedisSessionStore(opts, cookieOpts) | 		return redis.NewRedisSessionStore(opts, cookieOpts, cipher) | ||||||
| 	default: | 	default: | ||||||
| 		return nil, fmt.Errorf("unknown session store type '%s'", opts.Type) | 		return nil, fmt.Errorf("unknown session store type '%s'", opts.Type) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -353,24 +353,11 @@ var _ = Describe("NewSessionStore", func() { | ||||||
| 					SameSite: "strict", | 					SameSite: "strict", | ||||||
| 				} | 				} | ||||||
| 
 | 
 | ||||||
| 				var err error | 				// A secret is required but not defaulted
 | ||||||
| 				ss, err = sessions.NewSessionStore(opts, cookieOpts) |  | ||||||
| 				Expect(err).ToNot(HaveOccurred()) |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 			SessionStoreInterfaceTests(persistent) |  | ||||||
| 		}) |  | ||||||
| 
 |  | ||||||
| 		Context("with a cipher", func() { |  | ||||||
| 			BeforeEach(func() { |  | ||||||
| 				secret := make([]byte, 32) | 				secret := make([]byte, 32) | ||||||
| 				_, err := rand.Read(secret) | 				_, err := rand.Read(secret) | ||||||
| 				Expect(err).ToNot(HaveOccurred()) | 				Expect(err).ToNot(HaveOccurred()) | ||||||
| 				cookieOpts.Secret = base64.URLEncoding.EncodeToString(secret) | 				cookieOpts.Secret = base64.URLEncoding.EncodeToString(secret) | ||||||
| 				cipher, err := encryption.NewBase64Cipher(encryption.NewCFBCipher, encryption.SecretBytes(cookieOpts.Secret)) |  | ||||||
| 				Expect(err).ToNot(HaveOccurred()) |  | ||||||
| 				Expect(cipher).ToNot(BeNil()) |  | ||||||
| 				opts.Cipher = cipher |  | ||||||
| 
 | 
 | ||||||
| 				ss, err = sessions.NewSessionStore(opts, cookieOpts) | 				ss, err = sessions.NewSessionStore(opts, cookieOpts) | ||||||
| 				Expect(err).ToNot(HaveOccurred()) | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | @ -384,9 +371,16 @@ var _ = Describe("NewSessionStore", func() { | ||||||
| 		ss = nil | 		ss = nil | ||||||
| 		opts = &options.SessionOptions{} | 		opts = &options.SessionOptions{} | ||||||
| 
 | 
 | ||||||
|  | 		// A secret is required to create a Cipher, validation ensures it is the correct
 | ||||||
|  | 		// length before a session store is initialised.
 | ||||||
|  | 		secret := make([]byte, 32) | ||||||
|  | 		_, err := rand.Read(secret) | ||||||
|  | 		Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
| 		// Set default options in CookieOptions
 | 		// Set default options in CookieOptions
 | ||||||
| 		cookieOpts = &options.CookieOptions{ | 		cookieOpts = &options.CookieOptions{ | ||||||
| 			Name:     "_oauth2_proxy", | 			Name:     "_oauth2_proxy", | ||||||
|  | 			Secret:   base64.URLEncoding.EncodeToString(secret), | ||||||
| 			Path:     "/", | 			Path:     "/", | ||||||
| 			Expire:   time.Duration(168) * time.Hour, | 			Expire:   time.Duration(168) * time.Hour, | ||||||
| 			Refresh:  time.Duration(1) * time.Hour, | 			Refresh:  time.Duration(1) * time.Hour, | ||||||
|  |  | ||||||
|  | @ -37,8 +37,6 @@ func Validate(o *options.Options) error { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	msgs := make([]string, 0) | 	msgs := make([]string, 0) | ||||||
| 
 |  | ||||||
| 	var cipher encryption.Cipher |  | ||||||
| 	if o.Cookie.Secret == "" { | 	if o.Cookie.Secret == "" { | ||||||
| 		msgs = append(msgs, "missing setting: cookie-secret") | 		msgs = append(msgs, "missing setting: cookie-secret") | ||||||
| 	} else { | 	} else { | ||||||
|  | @ -60,12 +58,6 @@ func Validate(o *options.Options) error { | ||||||
| 			msgs = append(msgs, | 			msgs = append(msgs, | ||||||
| 				fmt.Sprintf("Cookie secret must be 16, 24, or 32 bytes to create an AES cipher. Got %d bytes.%s", | 				fmt.Sprintf("Cookie secret must be 16, 24, or 32 bytes to create an AES cipher. Got %d bytes.%s", | ||||||
| 					len(encryption.SecretBytes(o.Cookie.Secret)), suffix)) | 					len(encryption.SecretBytes(o.Cookie.Secret)), suffix)) | ||||||
| 		} else { |  | ||||||
| 			var err error |  | ||||||
| 			cipher, err = encryption.NewBase64Cipher(encryption.NewCFBCipher, encryption.SecretBytes(o.Cookie.Secret)) |  | ||||||
| 			if err != nil { |  | ||||||
| 				msgs = append(msgs, fmt.Sprintf("cookie-secret error: %v", err)) |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -218,7 +210,6 @@ func Validate(o *options.Options) error { | ||||||
| 	} | 	} | ||||||
| 	msgs = parseProviderInfo(o, msgs) | 	msgs = parseProviderInfo(o, msgs) | ||||||
| 
 | 
 | ||||||
| 	o.Session.Cipher = cipher |  | ||||||
| 	sessionStore, err := sessions.NewSessionStore(&o.Session, &o.Cookie) | 	sessionStore, err := sessions.NewSessionStore(&o.Session, &o.Cookie) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		msgs = append(msgs, fmt.Sprintf("error initialising session storage: %v", err)) | 		msgs = append(msgs, fmt.Sprintf("error initialising session storage: %v", err)) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue