Build-time secrets

I’m trying to deploy a Rails app with Puma & Sidekiq. I was reading Rails on Fly.io · Fly Docs. I don’t know where to set my Sidekiq license key.

bundle install is giving this error:

Authentication is required for enterprise.contribsys.com.
Please supply credentials for this source. You can do this by running:
bundle config enterprise.contribsys.com username:password

I tried setting a secret of BUNDLE_ENTERPRISE__CONTRIBSYS__COM. The trouble is, I need this secret set both a build time to install the gem, as well as runtime because the gem verifies the license.

Additionally I was reading this Preview: multi process apps (get your workers here!) which mentions sidekiqswarm, which is a paid feature that requires a sidekiq license to be added.

Hi!

Build-time secrets are indeed a tad different than run-time secrets (due to how we use Docker to build a container image that we later deploy).

We recently added documentation on that (Build Secrets · Fly Docs) as it’s a common point of confusion!

If you’re using a Nixpack or moving from Heroku, it may be a tad different - let me know if that’s the case!)

1 Like

I’m not sure how to use that. Does this secret automatically get added to the ENV during build?

I have this in a file called fly-builder.toml and ran fly deploy --remote-only -c fly-builder.toml --build-secret BUNDLE_ENTERPRISE__CONTRIBSYS__COM="abc:qwerty123".

It’s still giving me the same bundler config error.

Authentication is required for enterprise.contribsys.com.
Please supply credentials for this source. You can do this by running:
bundle config enterprise.contribsys.com username:password

fly-builder.toml

app = "dwell-web"

[processes]
web = "bundle exec rails server -b [::] -p 8080"
worker = "bundle exec sidekiqswarm"

[build]
  builder = "heroku/buildpacks:20"

  [build.args]
    RAILS_ENV = "development"
    RAILS_SERVE_STATIC_FILES = "true"

[env]
  PORT = "8080"
  RAILS_ENV = "development"
  RAILS_SERVE_STATIC_FILES = "true"
  PRIMARY_REGION = "ord"

[[services]]
  processes = ["web"] # this service only applies to the web block
  http_checks = []
  internal_port = 8080
  protocol = "tcp"
  script_checks = []

  [services.concurrency]
    hard_limit = 25
    soft_limit = 20
    type = "connections"

  [[services.ports]]
    handlers = ["http"]
    port = 80

  [[services.ports]]
    handlers = ["tls", "http"]
    port = 443

  [[services.tcp_checks]]
    grace_period = "1s"
    interval = "15s"
    restart_limit = 6
    timeout = "2s"

Nope! You have to you adjust your Dockerfile. That’s the first code block in the docs on build-time secrets: Build Secrets · Fly Docs

I got this to work! So regarding secrets, do we want to avoid secrets being in the Docker image?

Right now in my Dockerfile I’ve got this:

# This is a default line
RUN gem install -N bundler -v ${BUNDLER_VERSION}

# I added this line, which will write a file to ~/.bundle/config
RUN --mount=type=secret,id=BUNDLE_ENTERPRISE__CONTRIBSYS__COM \
    bundle config enterprise.contribsys.com $(cat /run/secrets/BUNDLE_ENTERPRISE__CONTRIBSYS__COM)

# This is a default line
COPY Gemfile* ./

# This is a default line, bundle config gets used here
RUN bundle install &&  rm -rf vendor/bundle/ruby/*/cache

# I added this line, as my Rails initializers use Rails Credentials,
# and precompiling boots the app and runs the initializers.
RUN --mount=type=secret,id=RAILS_MASTER_KEY \
    cat /run/secrets/RAILS_MASTER_KEY > config/credentials/${RAILS_ENV}.key

# This is a default line
RUN bundle exec rails assets:precompile

Is it bad to keep the ~/.bundle/config file (which includes my Sidekiq enterprise key), and config/credentials/production.key in the image, copied from the secrets? Or should I be setting them as environment variables instead?

Bundler does support setting a BUNDLE_ENTERPRISE__CONTRIBSYS__COM environment variable, and Rails supports the RAILS_MASTER_KEY environment variable.

The Build Secrets · Fly Docs page says:

RUN --mount=type=secret,id=MY_SUPER_SECRET \
    MY_SUPER_SECRET="$(cat /run/secrets/MY_SUPER_SECRET)" some_command \
    && more_commands_maybe

If I then do another line of RUN echo $MY_SUPER_SECRET, would I see that, or do I need to mount the secret in every command that needs it?

I’m trying to keep the setup simple. I plan to use GitHub Actions to deploy, and store my secrets in GitHub Actions, and make GitHub Actions run:

fly deploy --remote-only -c fly.toml \
  --build-secret BUNDLE_ENTERPRISE__CONTRIBSYS__COM="qwerty123"
  --build-secret RAILS_MASTER_KEY="abcdef"

If I do this, I don’t need to do this too:

fly secrets set RAILS_MASTER_KEY="abcdef"
fly secrets set BUNDLE_ENTERPRISE__CONTRIBSYS__COM="qwerty123"

Though maybe I should?

1 Like