Skip to content
This repository has been archived by the owner on Apr 17, 2023. It is now read-only.

SECRET_KEY_BASE in .env of Docker compose examples? #2285

Closed
ogra opened this issue Feb 17, 2020 · 6 comments
Closed

SECRET_KEY_BASE in .env of Docker compose examples? #2285

ogra opened this issue Feb 17, 2020 · 6 comments

Comments

@ogra
Copy link

ogra commented Feb 17, 2020

I'm new to Portus and trying to deploy it to a production environment using example Docker compose files.
What is SECRET_KEY_BASE string in .env file and how should I generate it?

https://github.com/SUSE/Portus/blob/master/examples/compose/.env

@Jean-Baptiste-Lasselle
Copy link

Jean-Baptiste-Lasselle commented Feb 18, 2020

Hi @ogra it is a value :

  • which can be set in docker-compose.yml using PORTUS_SECRET_KEY_BASE env variable. (not SECRET_KEY_BASE )
  • used by the portus container, because it is a ruby on rails web application (and all ruby and rails web app require what the Rails framework call secret key base)
  • all ruby and rails web application sign their cookies, for example to protect session ids against (wo)men in the middle, using a signing key. That singing key is generated from the value of the secret base key.
  • So you have to generate its value, to start with at installation time of your portus.
  • But most importantly :
    • you have to rotate its value, on the run, like certificates and let's encryt, to take full advantage of session protection
    • And it's really easy, once you have re-generated that secret using rails secret, all you have to do is to change the old, to the new value inside your configuration file, is to restart the rails, so here restart portus.

here are my recommendations, in the case of the use of a docker-compose.yml file :

  • don't use PORTUS_SECRET_KEY_BASE, use PORTUS_SECRET_KEY_BASE_FILE instead
  • mapped the path PORTUS_SECRET_KEY_BASE_FILE, inside your portus container, to the path you like, outside your container, example $FOLDER_WHERE_MY_DOCKER_COMPOSE_YML_IS/secrets/portus/secret_key_base:$PORTUS_SECRET_KEY_BASE_FILE
  • now, every time you rotate the secret base key :
    • you re-generate a value (I'll show you how to do that last)
    • you put that value inside the $FOLDER_WHERE_MY_DOCKER_COMPOSE_YML_IS/secrets/portus/secret_key_base
    • and you restart the rails webapp portus

And On that road, you will meet several technical difficulties not discussed anywhere, to my knowledge, bu there is one solution for all of those:

Ok the solution for the big problem of generating the right value, while new versions of portus are released in the future

So ok, here is the problem I solved :

  • to generate a value, you have to run rails secret
  • Ok, but where do I find this rails executable ?
  • Yeah, I amaware it is a dev toool for portus as a rails web app
  • but my problem is not during development, it is a pure production issue : how do I rotate that secret ?
  • Plus, you might want to check that inside the portus container, there is a rails executable, but to generate a secret, there is no way I want to disturb the portus process. No, I really just want change the content of a file and restart the portus service, without touching one hair on its head.

Ok, i'll sum you up my solution :

  • you need a dedicated container, responsible for just generating that secret. It knows how to generate it, and it does it well.
  • that container will have two parameters : RUBY_VERSION, RAILS_VERSIONS
  • everytime a new portus release is out, you have to realease a new version of that container, so that RUBY_VERSION, RAILS_VERSIONS always exactly match the versions of ruby, and rails, inside the portus container.

Docker recipe

There are a couple of other things to do, but hey I won't give you everything just now, I'm already giving you up :) how to generate the secret, and i'm currently implementing a rotator for that secret. Just know that regarding security, you also have to integrate to a secret manager

Ok, so now here is the code for the docker container you'll need to generate /rotate value of PORTUS_SECRET_BASE_KEY

  • the ./docker-compose.yml (just the part of the secret generator) :
  # ---
  # Use this one to update [PORTUS_SECRET_KEY_BASE]
  # (and restart portus to catch the new secret)
  # Also used to generate the initial value, before
  # bootstraping Portus,
  # cf. [./generate-portus-secret-key-base.sh]
  # ---
  # Should be upgraded with portus, and match with portus ' :
  # -> ruby, and rails version,
  # ---
  portus_secret_base_key_generator:
    image: railsecretmngr:0.0.1
    build:
      context: secrets-management/rails_secret_base_key
      args:
        # Actually, we change the Dockerfile, to
        # change ruby version.
        # But we still keep the infos in the buildargs,so thatit can be used ascontainer meta-data
        - RUBY_VERSION=2.5.0
        - RAILS_VERSION=5.0.1
    environment:

      # The container will store the generated secret base key intoa file with thisname, inside
      # the [/usr/src/portusecretkeybase/generator] folder.
      #
      - PORTUS_SECRET_KEY_BASE_FILE_NAME=portus.generated.secret.key.base
      # The container will store the generated secret base key into a Key Value Engine in HashiCrop Vault.
      # The secret_key_base willthere be stored to a PATH , using a keyname
      # the [/usr/src/portusecretkeybase/generator] folder.
      #
      # The HashiCorp Vault KV engine to use
      - VAULT_KV_ENGINE=dev_culturebase_org
      # The path where to store the secret
      - VAULT_KV_ENGINE_PATH=production/portus/rails
      # The key with which to store the secret value
      - VAULT_KV_ENGINE_KEY=secret_base_key
      - VAULT_ADDR=
      - VAULT_TOKEN_FILE=
    volumes:
      - $PWD/secrets-management/rails_secret_base_key/.generation/portus:/usr/src/portusecretkeybase/share
      # I don't use NGINX as a reverse proxy anymore. I use Traefik instead
      # - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      # - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
  • the ./secrets-management/rails_secret_base_key/Dockerfile:
FROM ruby:2.5.0
# FROM ruby:2.4

# see update.sh for why all "apt-get install"s have to stay as one long line
RUN apt-get update -y && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/*

# see http://guides.rubyonrails.org/command_line.html#rails-dbconsole
# RUN apt-get update -y && apt-get install -y mysql-client postgresql-client sqlite3 --no-install-recommends && rm -rf /var/lib/apt/lists/*
# because in debian buster the package name is 'default-mysql-client'
# https://packages.debian.org/search?searchon=names&keywords=mysql-client
RUN apt-get update -y && apt-get install -y default-mysql-client postgresql-client sqlite3 --no-install-recommends && rm -rf /var/lib/apt/lists/*

#
ARG RUBY_VERSION=2.5.0
# ENV RUBY_VERSION=2.5.0

ARG RAILS_VERSION=5.0.1
ENV RAILS_VERSION 5.0.1

# ARG PORTUS_SECRET_KEY_BASE_FILE_NAME
ENV PORTUS_SECRET_KEY_BASE_FILE_NAME=$PORTUS_SECRET_KEY_BASE_FILE_NAME

RUN gem install rails --version "$RAILS_VERSION"

# folder used to do the generation work
RUN mkdir -p /usr/src/portusecretkeybase/generator

# folder used to share the generation work
RUN mkdir -p /usr/src/portusecretkeybase/share
VOLUME /usr/src/portusecretkeybase/share

RUN rails new --skip-bundle /usr/src/portusecretkeybase/generator
COPY generate_secret_key_base.sh /usr/src/portusecretkeybase/generator
RUN chmod +x /usr/src/portusecretkeybase/generator/generate_secret_key_base.sh
RUN pwd && ls -allh /usr/src/portusecretkeybase/generator
RUN echo "ensuite dans [$(pwd)] "
WORKDIR /usr/src/portusecretkeybase/generator
USER root
RUN gem install rake -v '13.0.1'
RUN pwd && ls -allh .
RUN bundle install


# CMD ["/bin/bash"]
# CMD ["/usr/local/bundle/bin/rails", "secret"]
CMD ["/usr/src/portusecretkeybase/generator/generate_secret_key_base.sh"]
  • the ./secrets-management/rails_secret_base_key/generate_secret_key_base.sh script :
#!/bin/bash

# defaults to 'portus.generated.secret.key.base'
export PORTUS_SECRET_KEY_BASE_FILE_NAME=${PORTUS_SECRET_KEY_BASE_FILE_NAME:-'portus.generated.secret.key.base'}
rails secret > ./$PORTUS_SECRET_KEY_BASE_FILE_NAME

echo "$(pwd)/$PORTUS_SECRET_KEY_BASE_FILE_NAME"
ls -allh $PORTUS_SECRET_KEY_BASE_FILE_NAME
cat $(pwd)/$PORTUS_SECRET_KEY_BASE_FILE_NAME

cp $(pwd)/$PORTUS_SECRET_KEY_BASE_FILE_NAME /usr/src/portusecretkeybase/share

ls -allh /usr/src/portusecretkeybase/share

echo "Now will store that secret into HashiCorp Vaultin a KV engine dedicated for the infrastructure"

Once you have your 3 files docker-compose.yml, ./secrets-management/rails_secret_base_key/Dockerfile, and ./secrets-management/rails_secret_base_key/generate_secret_key_base.sh, just run :

# here you are in the folder where the |docker-compose.yml] file is located
export PORTUS_SECRETS_GEN_FOLDER=$PWD/secrets-management/rails_secret_base_key/.generation/portus
mkdir -p $PORTUS_SECRETS_GEN_FOLDER


docker-compose build portus_secret_base_key_generator

docker-compose up -d portus_secret_base_key_generator
docker-compose logs -f portus_secret_base_key_generator

export PORTUS_SECRET_KEY_BASE_FILE_NAME=portus.generated.secret.key.base
export PORTUS_SECRET_KEY_BASE_GEN_PATH=$PORTUS_SECRETS_GEN_FOLDER/portus.generated.secret.key.base

echo " And now the generated [secret_key_base] is stored in "
echo "[$PORTUS_SECRET_KEY_BASE_GEN_PATH]"
ls -allh $PORTUS_SECRETS_GEN_FOLDER
ls -allh $PORTUS_SECRET_KEY_BASE_GEN_PATH

export PORTUS_SECRET_KEY_BASE_GEN_VALUE=$(cat $PORTUS_SECRET_KEY_BASE_GEN_PATH)

echo " ---"
echo " Just generated PORTUS_SECRET_KEY_BASE value : "
echo "[$PORTUS_SECRET_KEY_BASE_GEN_VALUE]"
echo " ---"

Docs references

The portus Documentation says almost nothing about that :

Good to know

This signing cookies, especially session cookies, is a security measure enhanced by rails to reach compliance to OWASP security standards (which is quite a good,widely known security standard)

I think the exact mechanism implemented in rails with the secret base key, is the following : https://github.com/OWASP/CheatSheetSeries/blob/master/cheatsheets/JSON_Web_Token_Cheat_Sheet_for_Java.md#token-sidejacking

Last word

Problem here, as you'll find out, is taht there is today in February 2020, no ruby:2.5 container on docker hub, so I had to build my own, so I can then install the right version of rails, and finally generate a secret base key .

You're welcome ;)

@Jean-Baptiste-Lasselle
Copy link

Jean-Baptiste-Lasselle commented Feb 18, 2020

example output for me :

jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ docker-compose up -d portus_secret_base_key_generator && docker-compose logs -f portus_secret_base_key_generator 
Creating network "portusautopilotdev_pipeline_portus" with driver "bridge"
Creating network "portusautopilotdev_default" with the default driver
Creating portusautopilotdev_portus_secret_base_key_generator_1 ... done
Attaching to portusautopilotdev_portus_secret_base_key_generator_1
portus_secret_base_key_generator_1  | /usr/src/portusecretkeybase/generator/portus.generated.secret.key.base
portus_secret_base_key_generator_1  | -rw-r--r-- 1 root root 129 Feb 18 16:26 portus.generated.secret.key.base
portus_secret_base_key_generator_1  | a83355ec8dd33d4bed17e602e6bd9debabe55bcfd96b0d562af45334f67b973f4559d302cf74614b3f66cf4183e88cbc9a40c384e124d7ea50d62fa298f32ce7
portus_secret_base_key_generator_1  | total 12K
portus_secret_base_key_generator_1  | drwxr-xr-x 2 1000 1000 4.0K Feb 18 16:26 .
portus_secret_base_key_generator_1  | drwxr-xr-x 1 root root 4.0K Feb 18 16:24 ..
portus_secret_base_key_generator_1  | -rw-r--r-- 1 root root  129 Feb 18 16:26 portus.generated.secret.key.base
portus_secret_base_key_generator_1  | Now will store that secret into HashiCorp Vaultin a KV engine dedicated for the infrastructure
portusautopilotdev_portus_secret_base_key_generator_1 exited with code 0
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ ls -allh secrets-management/rails_secret_base_key/.generation/portus/
total 12K
drwxr-xr-x 2 jbl jbl 4.0K Feb 18 17:26 .
drwxr-xr-x 3 jbl jbl 4.0K Feb 18 16:46 ..
-rw-r--r-- 1 root root  129 Feb 18 17:26 portus.generated.secret.key.base
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ cat secrets-management/rails_secret_base_key/.generation/portus/portus.generated.secret.key.base 
a83355ec8dd33d4bed17e602e6bd9debabe55bcfd96b0d562af45334f67b973f4559d302cf74614b3f66cf4183e88cbc9a40c384e124d7ea50d62fa298f32ce7
jbl@poste-devops-jbl-16gbram:~/portus.autopilot.dev$ 

generated secret base key value generated above is :

a83355ec8dd33d4bed17e602e6bd9debabe55bcfd96b0d562af45334f67b973f4559d302cf74614b3f66cf4183e88cbc9a40c384e124d7ea50d62fa298f32ce7

@Jean-Baptiste-Lasselle
Copy link

Hi @ashtonian, I dumped this about PORTUS_SECRET_KEY_BASE ,as a thank you for making me understand your traefik-cert-dumper, sharing it with community.

@ogra
Copy link
Author

ogra commented Feb 19, 2020

Hi @Jean-Baptiste-Lasselle , thank you very much.
Your explanation is very helpful.

@Jean-Baptiste-Lasselle
Copy link

Jean-Baptiste-Lasselle commented Feb 19, 2020

Hi @Jean-Baptiste-Lasselle , thank you very much.
Your explanation is very helpful.

@ogra My pleasure, Test it, come back and :

  • give more details if any problem left,
  • if you don't have any problem left, close this issue :)

@ogra
Copy link
Author

ogra commented Feb 23, 2020

@Jean-Baptiste-Lasselle I tested your code on my Mac. Everything works fine. Thank you again!

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

No branches or pull requests

2 participants