Skip to content

Make it possible to rotate the secret key gracefully for ActiveStorage #39623

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

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

Conversation

santib
Copy link
Contributor

@santib santib commented Jun 14, 2020

Summary

Currently, when for some reason we need to change the app secret key, ActiveStorage's previously generated urls will stop working. By supporting rotation in the ActiveStorage verifier, we can rotate the app secret key while still supporting old generated urls either for a grace period or forever.

Other Information

I based the solution on config.action_dispatch.cookies_rotations.

I wanted to validate the idea and solution before adding anything else (like a section in the rails guides).

I don't like how this looks app.config.active_storage.rotations.active_storage but couldn't think of a better name.

@santib
Copy link
Contributor Author

santib commented Aug 28, 2020

Hey @georgeclaghorn did you have a chance to take a look at it? I'd like to know if this PR makes sense or if I'm missing something, thanks 😄

@rails-bot
Copy link

rails-bot bot commented Nov 26, 2020

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contributions.

@rails-bot rails-bot bot added the stale label Nov 26, 2020
@dhh dhh requested a review from georgeclaghorn November 28, 2020 09:42
@rails-bot rails-bot bot removed the stale label Nov 28, 2020
@santib santib force-pushed the activestorage-rotation branch from 83321af to 0c74115 Compare January 6, 2021 02:56
Base automatically changed from master to main January 14, 2021 17:01
@georgeclaghorn georgeclaghorn self-assigned this Mar 9, 2021
@georgeclaghorn georgeclaghorn removed their assignment Apr 30, 2021
@rails-bot
Copy link

rails-bot bot commented Jul 29, 2021

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contributions.

@rails-bot rails-bot bot added the stale label Jul 29, 2021
@rails-bot rails-bot bot closed this Aug 5, 2021
Copy link
Contributor

@kaspth kaspth left a comment

Choose a reason for hiding this comment

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

Good idea! We should fix this. We also gotta find a place to document this in the Active Storage guide.

@kaspth kaspth reopened this Aug 24, 2021
@rails-bot rails-bot bot removed the stale label Aug 24, 2021
@santib santib force-pushed the activestorage-rotation branch from 0c74115 to 34928c7 Compare August 27, 2021 02:48
@santib
Copy link
Contributor Author

santib commented Aug 27, 2021

Good idea! We should fix this. We also gotta find a place to document this in the Active Storage guide.

@kaspth From your comments I understand that you:

  • Don't want to have a rotation configuration object, instead expose the verifier directly.
  • Want to expose the verifier object ASAP so it can be configured both in config/environments/* and in config/initializers/*

I tried using the before_configuration hook but then I realized it won't be possible since the secret key base can be configured afterwards. So in short I can't find any way to do this without the rotation configuration object.

That said, I agree that changing activesupport/lib/active_support/messages/rotation_configuration.rb didn't feel good. So I'm all ears how to proceed without the rotation configuration object if you have any ideas, otherwise my suggestion would be to create a rotation configuration class under the active_storage engine which would be similar to activesupport/lib/active_support/messages/rotation_configuration.rb but specific for our use case. Let me know your thoughts, thanks 😄

@rails-bot
Copy link

rails-bot bot commented Nov 25, 2021

This pull request has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
Thank you for your contributions.

@rails-bot rails-bot bot added the stale label Nov 25, 2021
@rails-bot rails-bot bot closed this Dec 2, 2021
@brunoprietog
Copy link
Contributor

What happened to this? It would be great to see it merged

@rafaelfranca rafaelfranca reopened this Sep 14, 2022
@rails-bot rails-bot bot removed the stale label Sep 14, 2022
@rafaelfranca
Copy link
Member

@santib do you want to finish this? I think we should add a new rotation object only for Active Storage with a simpler API. We don't need a kind argumentin therotate` method for example. Just an object that stores an array and have a rotate, so we can do:

app.config.active_storage.key_rotation.rotate "old secret"

@santib
Copy link
Contributor Author

santib commented Sep 15, 2022

Hey @rafaelfranca sure I can finish this but I'll need a couple of days to find some time if that's ok, thanks for the heads up

@santib santib force-pushed the activestorage-rotation branch from 34928c7 to 497ac32 Compare September 16, 2022 20:31
@rafaelfranca
Copy link
Member

@jonathanhefner I think you PR to add the message_verifies invalidate this one right?

@santib santib force-pushed the activestorage-rotation branch from 497ac32 to 9c5a26a Compare September 19, 2022 13:16
@jonathanhefner
Copy link
Member

Yes, with #44179, users could call Rails.application.message_verifiers.rotate(...) sometime before the "active_storage.verifier" initializer runs.

Note that the MessageVerifier returned by app.message_verifier("ActiveStorage") uses Rails.application.key_generator for its secret. In other words, the old secret_key_base must be processed by ActiveSupport::KeyGenerator#generate_key before it can be rotated.

So, if I understand this PR correctly, the configuration would look something like:

key_generator = ActiveSupport::KeyGenerator.new(old_secret_key_base, iterations: 1000)

config.active_storage.key_rotations.tap do |rotations|
  rotations.rotate(key_generator.generate_key("ActiveStorage"))
end

With #44179, the configuration would look something like:

key_generator = ActiveSupport::KeyGenerator.new(old_secret_key_base, iterations: 1000)

Rails.application.message_verifiers.rotate(secret_generator: key_generator.method(:generate_key))

We could possibly simplify these configurations by modifying Rails.application.key_generator to accept an optional base arg. If the arg is provided, key_generator would return a new ActiveSupport::KeyGenerator; otherwise, it would return @caching_key_generator.

Or we could add a Rails.application.secret_generator method that accepts a base arg and returns a proc. So the configuration would look something like:

Rails.application.message_verifiers.rotate(
  secret_generator: Rails.application.secret_generator(old_secret_key_base)
)

@shamas-
Copy link

shamas- commented Oct 3, 2023

One year ping on this in case it can still be completed

@collimarco
Copy link
Contributor

I just posted a related question on Reddit. It would be useful to have a plan or documentation in case you need to change the secret key.

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

Successfully merging this pull request may close these issues.

8 participants