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

Refactor scrypt hashing and source app session mgmt pt 2/3 #5694

Merged

Conversation

nabla-c0d3
Copy link
Contributor

@nabla-c0d3 nabla-c0d3 commented Jan 11, 2021

Status

Ready.

Description of Changes

This PR follows #5692 .

@nabla-c0d3 nabla-c0d3 changed the title [WIP] Refactor scrypt hashing pt 2/3 Refactor scrypt hashing and source app session mgmt pt 2/3 Jan 11, 2021
@nabla-c0d3 nabla-c0d3 force-pushed the refactor-scrypt-hashing-pt-2 branch from acbf43f to 3083589 Compare January 12, 2021 05:39
@classmethod
def log_user_in(cls, user: SourceUser, user_passphrase: "DicewarePassphrase") -> None:
# Save the passphrase in the user's session cookie
session[cls._KEY_IN_SESSION_COOKIE] = user_passphrase
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 follows the same logic as the current code base, which is to put the user's passphrase in their session cookie (which is encrypted). I don't know if this is useful or not, but we could replace the passphrase in the cookie with a randomly-generated session ID.

@nabla-c0d3 nabla-c0d3 force-pushed the refactor-scrypt-hashing-pt-2 branch from 3083589 to e320129 Compare March 26, 2021 02:14
@nabla-c0d3 nabla-c0d3 force-pushed the refactor-scrypt-hashing-pt-2 branch 3 times, most recently from d87ae38 to ce9cbbe Compare August 17, 2021 20:46
@eloquence eloquence added this to the 2.1.0 milestone Aug 18, 2021
# ignore_static here because `crypto_util.hash_codename` is scrypt
# (very time consuming), and we don't need to waste time running if
# we're just serving a static resource that won't need to access
# these common values.
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 is no longer needed since I removed the "time consuming" scrypt call from the code that handles incoming requests.

del session['logged_in']
del session['codename']
return redirect(url_for('main.index'))
g.loc = app.storage.path(g.filesystem_id)
Copy link
Contributor Author

@nabla-c0d3 nabla-c0d3 Aug 19, 2021

Choose a reason for hiding this comment

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

All the authentication and session logic in this file is now handled by the SessionManager, which is called when the @login_required decorator is used in the Flask routes. This ensures that all authentication / session management logic happens in a single code location, thereby reducing complexity.

except UserNotLoggedIn:
return redirect(url_for("main.login"))

return f(*args, **kwargs, logged_in_source=logged_in_source)
Copy link
Contributor Author

@nabla-c0d3 nabla-c0d3 Aug 19, 2021

Choose a reason for hiding this comment

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

The decorator directly passes the source user to the Flask route/code (to make the "data flow" more obvious/simple), so using flask.g or session (and checking that they are in the right "state") is no longer needed.

@kushaldas kushaldas self-assigned this Aug 25, 2021
@nabla-c0d3 nabla-c0d3 force-pushed the refactor-scrypt-hashing-pt-2 branch from ce9cbbe to 01675a4 Compare August 25, 2021 20:18
@nabla-c0d3 nabla-c0d3 marked this pull request as ready for review August 25, 2021 20:20
@nabla-c0d3 nabla-c0d3 requested a review from a team as a code owner August 25, 2021 20:20
logged_in_source = SessionManager.get_logged_in_user()

except (UserSessionExpired, UserHasBeenDeleted):
return clear_session_and_redirect_to_logged_out_page(session)
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 way of handling expiring sessions should fix the following issues: #5741, #5716

Add compatibility layer for session management to reduce size of changes

Make passphrase as the session cookie more obvious

Cleanup test_source fixture and explicitely pass a db_session

Clarify name

Consolidate logout behavior

Run black formatting

Add tests for SessionManager

Fix tests

securedrop/requirements/python3/securedrop-app-code-requirements.in
securedrop/requirements/python3/securedrop-app-code-requirements.txt
@nabla-c0d3 nabla-c0d3 force-pushed the refactor-scrypt-hashing-pt-2 branch from 01675a4 to 7762ec2 Compare September 1, 2021 02:40
Copy link
Contributor

@kushaldas kushaldas left a comment

Choose a reason for hiding this comment

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

Running it on staging for a few days, no issues shown.

Include tests, and also updates the apparmor entries. Merging this now and then moving to the last PR in this set.

@kushaldas kushaldas merged commit 6020958 into freedomofpress:develop Sep 7, 2021
@nabla-c0d3
Copy link
Contributor Author

Awesome!

@kushaldas
Copy link
Contributor

We may have to revert this. While reviewing #5695 I noticed that sometimes using a second or third source at the same time is causing logout due to inactivity. And a a few times after updating to this session manager code, I can see that the source interface is not showing the source passphrase on the top.

For example:
Screenshot from 2021-09-09 13-00-44

@nabla-c0d3
Copy link
Contributor Author

I can look into this.

@zenmonkeykstop
Copy link
Contributor

Thanks @nabla-c0d3 - if you wanna chat about it (even just for rubber-duck mode) folks are around on SD Gitter: https://gitter.im/freedomofpress/securedrop

@nabla-c0d3
Copy link
Contributor Author

Thanks @nabla-c0d3 - if you wanna chat about it (even just for rubber-duck mode) folks are around on SD Gitter: https://gitter.im/freedomofpress/securedrop

Thanks! It was the morning for me so I only got to this just now. Some thoughts/questions:

Using a second or third source at the same time is causing logout due to inactivity

  • I tried on the dev server and wasn't able to reproduce this. I was logged in as 4 different sources, uploading documents and such, but never got logged out on any of them.
  • This brings the following question: @kushaldas were you using the dev server or a production setup? If it was production, which web server is used to serve the source interface, and does the web server use multiple processes to process requests? If that's the case the cache at SessionManager._CACHE would not be shared across processes, so users would see the "logged out" message if their request hit a different process.
  • I could revert the logic to not cache anything and only use the session cookie, but this means that every request will trigger one call to scrypt to derive the filesystem_id from the passphrase in the cookie, as it was previously.

source interface is not showing the source passphrase on the top

  • I tried on the dev server and the passphrase is displayed right after user creation. It is not displayed on subsequent logins, which, to me, is expected; this seemed to be the intent of the code when I refactored it. It would be easy tho to update the logic to always show the passphrase after logging in (instead of just after user creation).
  • Or is it that right after you created a source user, the passphrase still wasn't displayed? This would be unexpected to me and a bug.

That's all I have for now... let me know your thoughts @kushaldas @zenmonkeykstop . Thanks!

@zenmonkeykstop
Copy link
Contributor

For the first issue, I managed to get the "logged out due to inactivity" error intermittently (about half the time) in a staging env by:

  1. logging in as a new source (/generate -> /lookup)
  2. logging out and then going back to the home page (without clicking the Tor Browser New Identity button)
  3. clicking through to /generate and getting a new codename
  4. clicking thru to /lookup - instead of getting the /lookup page I get redirected to home with the "logged out" error.

This isn't precisely what Kushal saw, though, and I haven't found a way to consistently replicate that yet. (Also as STRs go, it is as the kids say, kinda sus)

Production (and staging) instances use Apache and mod_wsgi, and 2 processes are being spawned, so it's possible different requests will be served by different child processes. It might be better to back that change out as you mentioned. We could look at alternatives for subsequent releases. (The application uses Redis to cache GPG fingerprints already, but I think more work/research would be required before using it for full-on session management.)

For the second issue (no passphrase on /lookup) - my understanding is that the scenario Kushal is describing is the passphrase not showing up on /lookup on first login. I haven't managed to replicate this yet though, and it could just be a misunderstanding, so focusing on the first issue seems like the best idea for now.

@nabla-c0d3
Copy link
Contributor Author

I went through the steps you described a few times but I haven't been able to reproduce the logout errors/bugs. My environment might be different. Is your staging environment the same as running make dev ?

Regardless, I've updated the next PR at #5695 to remove the local cache, so that the whole session information is stored in the session cookie, like it was previously. This should get rid of these errors; I've tried it in my dev environment and it seemed to work fine.

@zenmonkeykstop
Copy link
Contributor

Hey @nabla-c0d3 - we have 3 types of environments, each with their own foibles:

  • dev env (make dev): Docker-based, single-server environment, that just runs the two wsgi apps directly, exposing the services via open Docker ports. This is the simplest to run and most useful for web development, downside is that architecture-wise it's not a good match for production.

  • staging env (make staging): libvirt-based, multiserver environment, this is closer to production in that it includes both separate app and mon servers, proxies the apps via Apache, and makes the services available over Tor. It's more hassle to set up and requires that you build deb packages to push changes, rather than live-editing code, but it does catch the kind of issues that will only appear once the app is running against the prod-like server config.

  • prod VM env (molecule create -s libvirt-prod-focal): this is mostly used for upgrade testing (ie. 2.0.1 to 2.0.2). It is a virtualized production environment that requires a (usually also virtualized) Tails admin workstation to set up. It will catch any issues that you'll see in staging, but can also be used to test the effects of new packaging scripts on an existing system.

Thanks for the updates (and for the knock-on updates to pr number 3 :) )! We'll take a pass through ASAP.

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