18

I have been trying (unsuccessfully) to access the main app's application secrets from within my Rails mountable engine. The whole point of a mountable engine is to provide modularity. Therefore a common pattern would be to provide configurable parameters, some of which need to be secret, in the main app, which would then be used by the engine.

In my specific case, I am using carrierwave and fog in my engine to upload files to an AWS bucket. The exact bucket and AWS credentials are not specified in the engine, but in the main app, since they will vary providing which app is mounting the engine.

However, the initializer for carrierwave when mounted in the engine fails as it cannot find the Rails.application.secrets for the main app:

require 'carrierwave'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
  config.fog_provider = 'fog/aws'

  config.fog_credentials = {
    :provider               => 'AWS',
  :aws_access_key_id      => Rails.application.secrets.S3_AWS_ACCESS_KEY_ID,
  :aws_secret_access_key  => Rails.application.secrets.S3_AWS_SECRET_ACCESS_KEY
  }
  config.fog_directory  = Rails.application.secrets.CARRIERWAVE_CONFIG_FOG_DIRECTORY
  config.storage = :fog
end

This fails when engine is started with

Missing required arguments: aws_access_key_id, aws_secret_access_key (ArgumentError)

as in fact Rails.application.secrets.S3_AWS_ACCESS_KEY_ID (and the others) evaluates to nil in the initializer. It does evaluate correctly inside the engine's controllers once the app is running, but in the initializer it is nil.

I have modified this like the following:

:aws_access_key_id      => Rails.application.secrets.S3_AWS_ACCESS_KEY_ID || ENV["S3_AWS_ACCESS_KEY_ID"]

and exported the ENV VARIABLE in each production environment for use with the engine, but this is less than ideal. Any solution would be appreciated.

  • 1
    Hi, are you storing the `aws_access_key_id` and `aws_secret_access_key` values directly on the `config/secrets.yml` file? Or, are you using the `config/application.yml` to do that? – Danger Rodriguez Gálvez Apr 06 '21 at 13:23
  • 1
    No longer doing either but pretty sure I was using config/secrets.yml at that point. It was the recommended solution for Rails 5.1 – Augusto Samamé Barrientos Apr 06 '21 at 15:57
  • 1
    Yes, I understand. I had a similar issue, and I solved it using `application.yml` to store my secret credentials and access them through the ENV VARIABLES. See code here – Danger Rodriguez Gálvez Apr 06 '21 at 20:03
  • Yes. I'm using .env files via dotenv myself now, as I kind of lost track of all the Rails recommended solutions that kept changing with every minor version increase. Still looking for a solution using the "Rails way" that works with recommended secrets strategy for Rails 6. – Augusto Samamé Barrientos Apr 07 '21 at 02:06
  • How about trying to use Rails credentials feature? – kevinluo201 Aug 25 '21 at 09:35

2 Answers2

1
  1. In your engine's initializer (config/initializers/engine_name.rb), define a configuration option to store the AWS credentials and fog directory:
# config/initializers/engine_name.rb
EngineName.configure do |config|
  config.aws_access_key_id = nil
  config.aws_secret_access_key = nil
  config.fog_directory = nil
end

  1. In the main app, provide the values for these configuration options in an initializer:
# config/initializers/engine_name.rb
EngineName.configure do |config|
  config.aws_access_key_id = Rails.application.secrets.S3_AWS_ACCESS_KEY_ID
  config.aws_secret_access_key = Rails.application.secrets.S3_AWS_SECRET_ACCESS_KEY
  config.fog_directory = Rails.application.secrets.CARRIERWAVE_CONFIG_FOG_DIRECTORY
end

  1. Modify your carrier wave initializer in the engine to use the configuration options:
# engine_name/config/initializers/carrierwave.rb
require 'carrierwave'
require 'carrierwave/storage/fog'

CarrierWave.configure do |config|
  config.fog_provider = 'fog/aws'

  config.fog_credentials = {
    provider: 'AWS',
    aws_access_key_id: EngineName.configuration.aws_access_key_id,
    aws_secret_access_key: EngineName.configuration.aws_secret_access_key
  }

  config.fog_directory = EngineName.configuration.fog_directory
  config.storage = :fog
end

By using this approach, you allow the main app to provide the necessary secrets through the engine's configuration, which avoids the issue of accessing the main app's secrets directly from the engine.

0

You should be able to have access to the secrets from initializer of your engine.


# path/to/your/engine/my_engine/lib/my_engine/engine.rb
module MyEngine
    class Engine
        initializer :my_custom_initializer do |app|
            puts app.secrets
        end
    end
end
brcebn
  • 1,571
  • 1
  • 23
  • 46
  • Interesting. I'll put the Carrierwave config code in there and accept the answer if it works. I still think this should be cleaner though and will raise it with the Rails team at some point. – Augusto Samamé Barrientos Feb 24 '22 at 16:18