Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Redis sessions #224

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open

Redis sessions #224

wants to merge 5 commits into from

Conversation

sporkmonger
Copy link
Contributor

Problem

Session state can become quite large and can easily exceed the maximum cookie size. I previously tried to fix it with #150 but that proved to be a bad solution prone to failures.

Solution

This introduces optional support for a Redis backend for sessions. Setting a SESSION_REDIS_CONNECTION environment variable to e.g. redis://primary.sso-sessions.example-cloud.com:6379 will switch the session backend from cookie sessions to redis sessions. The default remains cookie sessions.

Notes

This also avoids the issue of large amounts of session state being retransmitted on every request. Session state is encrypted using the existing cipher mechanism and session keys. As discussed in #158, I recommend renaming the cipher key configuration name to be more generic, but this PR doesn't make that change. This PR does not use the ticket system mentioned in #158, and instead opts for the simpler approach of encrypting using the session secret held on the auth server. I have tested this PR in conjunction with #118 and it works well.

@codecov
Copy link

codecov bot commented Jul 12, 2019

Codecov Report

Merging #224 into master will decrease coverage by 2.61%.
The diff coverage is 20%.

@@            Coverage Diff             @@
##           master     #224      +/-   ##
==========================================
- Coverage   62.23%   59.62%   -2.62%     
==========================================
  Files          50       51       +1     
  Lines        4067     4225     +158     
==========================================
- Hits         2531     2519      -12     
- Misses       1349     1510     +161     
- Partials      187      196       +9
Impacted Files Coverage Δ
internal/proxy/options.go 83.59% <ø> (ø) ⬆️
internal/proxy/proxy.go 22.72% <0%> (-1.67%) ⬇️
internal/auth/options.go 52.89% <0%> (-25.68%) ⬇️
internal/proxy/oauthproxy.go 41.27% <0%> (-9.71%) ⬇️
internal/pkg/sessions/redis_store.go 29.72% <29.72%> (ø)
internal/auth/configuration.go 49.26% <43.75%> (-0.47%) ⬇️
internal/auth/mux.go 71.18% <50%> (-3.82%) ⬇️
internal/pkg/sessions/session_state.go 48.57% <0%> (-36.43%) ⬇️
... and 8 more

@jphines
Copy link
Contributor

jphines commented Jul 12, 2019

@sporkmonger this is much cleaner than I expected, which is fantastic!

I'll take a look at this today/this weekend, provide some review, and kick the tires.

Thank you for your work on this effort, it is a fantastic set of functionality. I (we) greatly appreciate your contributions!

@sporkmonger
Copy link
Contributor Author

Sure thing. I had some trouble figuring out how to mock the Redis client, which is why there's a bit of a coverage drop I believe.

@@ -35,6 +38,9 @@ import (
// PROVIDER_*_CLIENT_SECRET
// PROVIDER_*_SCOPE
//
// PROVIDER_*_AZURE_TENANT
// PROVIDER_*_AZURE_PROMPT
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This snuck in somehow, not sure how critical it is to keep this isolated since this PR is the main blocker for Azure, but I can remove if needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not critical to remove, but I think I'd personally prefer if it was pulled back into the Azure PR and removed here, if that's ok!

@sporkmonger
Copy link
Contributor Author

@jphines Any updates?

@sylr
Copy link

sylr commented Sep 9, 2019

@sporkmonger it seems there currently is a conflict, could you resolve it ?

@jphines Do you have time to review this ?

@jphines
Copy link
Contributor

jphines commented Sep 9, 2019

My apologizes, I've failed to prioritize review here appropriately. I've asked @Jusshersmith to review these changes in light of my inability to find the time to do so.

@sporkmonger
Copy link
Contributor Author

Yeah, I'll try to address the conflict shortly.

@@ -84,6 +90,9 @@ func DefaultAuthConfig() Configuration {
Secure: true,
HTTPOnly: true,
},
RedisConfig: RedisConfig{
UseSentinel: false,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To clarify: is this just included for explicitness around this setting? (Unless I'm mistaken, it's not strictly required, right?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's correct. It's been awhile but I think it might fail if the RedisConfig struct doesn't get initialized, but the UseSentinel default is the false zero value.

} else {
opts = append(opts, SetCookieStore(config.SessionConfig, idpSlug))
}
authenticator, err := NewAuthenticator(config, opts...)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you think about maybe creating a new SetSessionStore function that houses this logic, which would be passed in with the slice of functions to NewAuthenticator?

Which either essentially calls the above, or returns one of the SetRedisStore or SetCookieStore funcs?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, this kind of pattern is used elsewhere in the codebase! 🤔

@@ -35,6 +38,9 @@ import (
// PROVIDER_*_CLIENT_SECRET
// PROVIDER_*_SCOPE
//
// PROVIDER_*_AZURE_TENANT
// PROVIDER_*_AZURE_PROMPT
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Definitely not critical to remove, but I think I'd personally prefer if it was pulled back into the Azure PR and removed here, if that's ok!

setSessionStore := SetCookieStore
if opts.RedisConnectionURL != "" || opts.RedisUseSentinel {
setSessionStore = SetRedisStore
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is nice and concise, but I wonder if it's worth standardising to match the other similar use cases above, e.g:

if opts.RedisConnectionURL != "" || opts.RedisUseSentinel {
    optFuncs = append(optFuncs, SetRedisStore(opts)
} else {
    optFuncs = append(optFuncs, SetCookieStore(opts)
}

Or, at least closer to whatever method we end up using on the authenticator side to set the session store.

CheckFunc: func(c Configuration, t *testing.T) {
redisConf := c.SessionConfig.RedisConfig
assertEq([]string{"redis://master.example.com:6379"}, redisConf.ConnectionURLs, t)
},
Copy link
Contributor

@Jusshersmith Jusshersmith Sep 13, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if it's also worth adding in a call to RedisConfig.Validate() and/or RedisConfig.Enabled() here? It seems like it could be included fairly easily, and I think would help get the error cases within them tested.

@Jusshersmith
Copy link
Contributor

This looks great @sporkmonger, and thank you for your patience! I've left some comments to start with!

@sylr
Copy link

sylr commented Oct 3, 2019

Any updates ?

@sylr
Copy link

sylr commented Oct 22, 2019

Guys ?

@sporkmonger
Copy link
Contributor Author

It's taken long enough that... we ended up moving from Azure AD to Okta in the meantime.

@sylr
Copy link

sylr commented Oct 24, 2019

😭

@Jusshersmith
Copy link
Contributor

Thank you for all of your great work on this @sporkmonger! This seems like it could still be an extremely valuable addition, and would be a shame for all of this work to go to waste. I don't think we at BuzzFeed necessarily have the capacity to push it over the finish line in the immediate future, but I think we can keep this open for a while still to allow someone to pick it up if possible -- providing you're happy with that @sporkmonger.

@sporkmonger
Copy link
Contributor Author

Totally. My bandwidth for stuff like this goes up and down a lot, but the next time I've got some spare cycles I'll try to get this addressed. If someone else wants to pick it up and get it over the finish line, I'm equally OK with that too.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants