Todos

flask soc site todos

Steps to deploy to sec.joostagterhoek.nl

Manual

Install and configure a Flask app on my server

Following the steps described in this Yunohost-forum-post, I managed to install a ‘Hello World’-app on sec.joostagterhoek.nl.

Deploy to production according to Flask-documentation

source: link to Flask-documentation on production deployment

To deploy a Flask app, the documentation recommends to:

  1. Build a wheel (.whl) file.
  2. Configure a secret key
  3. Run the app with a production server

How to build a wheel

  • Look this up and note here

Docker

Dockerize the Flask application

Enable the container registry in Gitlab

The commands/configuration code below works, but I had to rework the paths to the *.pem files. Below is what I found in this Yunohost-package issue:

/etc/gitlab/gitlab-persistent.rb
registry_external_url 'https://git.example.com:5050'
registry_nginx['ssl_certificate'] = "/etc/ssl/private/yunohost_crt.pem"
registry_nginx['ssl_certificate_key'] = "/etc/ssl/private/yunohost_key.pem"

Then
sudo gitlab-ctl reconfigure

I then had to find out where the actual current pem files are:

/etc/ssl/certs/yunohost_crt.pem
/etc/ssl/private/yunohost_key.pem

Configure the registry to receive Docker containers

Authentication:
  • With only a username and password:
    • docker login registry.example.com -u -p
    • Probably unsafe (you also get a warning that your hashed password is stored in home/$USER/.docker/config.json).
  • Use Gitlab CI/CD to authenticate
    • Upside: per-job user and password managed by Gitlab CI/CD pipeline.
  • Authenticate with a token
    • Downside: token management (tokens should have a limited lifespan)
Approach
  • Build and push the local dockerized Flask image with a temporary token
  • Create a Gitlab deployment pipeline that does the following:
    • SSH into production server as Gitlab Runner-account
    • Use the CI job token (unique to each CI/CD pipeline) which only has read_registry scope for read (pull) access.

Managing Docker secrets

Adding secrets to Docker

From https://docs.docker.com/engine/swarm/secrets/#simple-example-get-started-with-secrets:

printf "This is a secret" | docker secret create my_secret_data -

(…). The docker secret create command reads standard input because the last argument, which represents the file to read the secret from, is set to -.

Thinking it through: in no way would adding ‘reusable’ secrets like an API key or a Flask secret key in an image build be a good idea: even if someone would not be able to access those secret values directly because of Docker’s encryption or access levels, the built image would still allow the secrets to be used by others (and for example exceed API key usage limits). Instead, it would be better to add those secrets at runtime, in the case of my preferred DevOps-flow, when the Docker Compose file uses the prebuilt image from the most current code (which both have no baked in secrets) and adds the secret at/before runtime.

Like this flowchart:

flowchart TD
code --> pipeline --> container_registry --> compose -->|secrets injected| production_server
code[Code commited and pushed to remote]
pipeline[GitLab pipeline to build Docker image]
container_registry[image pushed to Docker container registry]
compose[Docker Compose runs built image on production server]
production_server[Provides Flask app]

What works: version your image, use Docker service

This is just a quick note to make sure I’m not missing anything: the website actually freaking works! To make sure I’m not forgetting anything:

  • Use the Python module get-docker-secrets
  • Use docker secret create <secret-name> <path-to-secret-file> to create secrets
  • Build the container on your working computer! A bloody genius idea of course! 💫 Worry about the Rpi Gitlab Runner build issues later (differing architectures 😤)
  • Put the version number of the app/container in the container’s tag (currently 1.0)
  • Push that versioned tag of your image to the Gitlab container registry
  • Pull the versioned tag of the image to your production server (log in first)
  • Use this command to run the container as a service and have it gain access to the docker secrets: docker service create --name flask-soc-service --secret vt_api --secret abuseipdb_api --secret secret_key --publish 8888:8000 code.joostagterhoek.nl:5050/web-development/flask-soc-site:1.0 (I forgot how horrible copying and pasting is from and to an actual terminal, Emacs, please never leave me…🤩)