Skip to content
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

_FILE environment variable support for Docker Secrets #227

Open
jasonrichdarmawan opened this issue May 29, 2020 · 3 comments
Open

_FILE environment variable support for Docker Secrets #227

jasonrichdarmawan opened this issue May 29, 2020 · 3 comments

Comments

@jasonrichdarmawan
Copy link

jasonrichdarmawan commented May 29, 2020

Getting started

A minor patch to support Docker Secrets, so you don't have to store unencrypted secrets in the env file.

The repository should look like this

/
├─ graphql
|  ├─ Dockerfile
|  └─ docker-entrypoint.sh

Dockerfile

FROM node:12-alpine
LABEL description="Instant high-performance GraphQL API for your PostgreSQL database https://github.com/graphile/postgraphile"

# Install PostGraphile and PostGraphile connection filter plugin
RUN npm install -g postgraphile@4

EXPOSE 5000

# patch postgraphile:4.7.1; DATABASE_URL_FILE environment variable implementation.
RUN apk add bash
COPY docker-entrypoint.sh /usr/local/bin/
RUN ln -s /usr/local/bin/docker-entrypoint.sh / # backwards compat
ENTRYPOINT ["docker-entrypoint.sh"] 
CMD ["postgraphile", "-n", "0.0.0.0"]

docker-entrypoint.sh

#!/usr/bin/env bash

# usage: file_env VAR [DEFAULT]
#    ie: file_env 'XYZ_DB_PASSWORD' 'example'
# (will allow for "$XYZ_DB_PASSWORD_FILE" to fill in the value of
#  "$XYZ_DB_PASSWORD" from a file, especially for Docker's secrets feature)
file_env() {
    local var="$1"
    local fileVar="${var}_FILE"
    local def="${2:-}"
    if [ "${!var:-}" ] && [ "${!fileVar:-}" ]; then
        echo >&2 "error: both $var and $fileVar are set (but are exclusive)"
        exit 1
    fi
    local val="$def"
    if [ "${!var:-}" ]; then
        val="${!var}"
    elif [ "${!fileVar:-}" ]; then
        val="$(< "${!fileVar}")"
    fi
    export "$var"="$val"
    unset "$fileVar"
}

# Loads various settings that are used elsewhere in the script
# This should be called before any other functions
docker_setup_env() {
    file_env 'DATABASE_URL'
}

# call the function
docker_setup_env

# call postgraphile
exec "$@"

How to use this patch

  1. Create the Docker Secret

First, generate the random strings.

$ python3 -c "import base64, os; print(base64.b64encode(os.urandom(24)).decode('ascii'))"

Then, manually create a Docker Secret file for the POSTGRES_PASSWORD and DATABASE_URL environment variables. Note that DATABASE_URL follows the syntax postgres://<POSTGRES_USER>:<POSTGRES_PASSWORD>@db:5432/<POSTGRES_DB>, POSTGRES_PASSWORD will be stored in environment variable unecrpyted.

$ echo <POSTGRES_PASSWORD> | docker secret create dbpw -
$ echo postgres://<POSTGRES_USER>:<POSTGRES_PASSWORD>@db:5432/<POSTGRES_DB> | docker secret create dburl -

Use docker exec -ti <CONTAINER ID> cat /run/secrets/<secert_name> to read the contents of the secret data file.

  1. Build the Docker Image
$ docker build -t postgraphile:4.7.0-alpine ./graphql
  1. ... via docker stack deploy or docker-compose

docker-compose.yml example

version: '3.7'

networks:
  backend:
  db:

secrets:
  dbpw:
    external: true
  dburl:
    external: true

volumes:
  dbdata:

services:
  db:
    image: postgres:12-alpine
    ports:
      - 5432:5432 # dev build only
    environment:
      - POSTGRES_DB=postgres
      - POSTGRES_USER=postgres
      - POSTGRES_PASSWORD_FILE=/run/secrets/dbpw
    networks:
      - backend
      - db
    secrets:
      - dbpw
    volumes:
      - dbdata:/var/lib/postgesql/data
  graphql:
    image: postgraphile:4.7.0-alpine
    ports:
      - 5000:5000 # dev build only.
    environment:
      - DATABASE_URL_FILE=/run/secrets/dburl
    networks:
      - backend
      - db
    secrets:
      - dburl
@jasonrichdarmawan jasonrichdarmawan changed the title [knowledge] _FILE environment variable support for Docker Secrets [Docs] _FILE environment variable support for Docker Secrets May 29, 2020
@jasonrichdarmawan jasonrichdarmawan changed the title [Docs] _FILE environment variable support for Docker Secrets _FILE environment variable support for Docker Secrets May 29, 2020
@benjie
Copy link
Member

benjie commented Jun 2, 2020

Thanks; this is really useful 🙌

@jasonrichdarmawan
Copy link
Author

jasonrichdarmawan commented Jun 8, 2020

I heard the CLI is depreceated for v5.

You can use Postgraphile as a library in Docker.

I use fs.readFileSync(process.env.DATABASE_URL_FILE, 'utf8') for Docker Secrets.

src/server.js

const express = require("express");
const { postgraphile } = require("postgraphile");
const fs = require("fs");

const app = express();

app.use(
  postgraphile(
    fs.readFileSync(process.env.DATABASE_URL_FILE, 'utf8') || process.env.DATABASE_URL,
    "public",
    {
      watchPg: true,
      graphiql: true,
      enhanceGraphiql: true,
    }
  )
);

app.listen(process.env.PORT);

Dockerfile

FROM node:12-alpine
LABEL description="Instant high-performance GraphQL API for your PostgreSQL database https://github.com/graphile/postgraphile"

# Set Node.js app folder
RUN mkdir -p /home/node/app/node_modules && chown -R node:node /home/node/app
WORKDIR /home/node/app

# Copy dependencies
COPY ./src/package*.json ./

# Install dependencies
USER node
RUN npm install

# Copy application files
COPY --chown=node:node ./src .

CMD [ "node", "server.js" ]

@benjie
Copy link
Member

benjie commented Jun 8, 2020

I heard the CLI is depreceated for v5.

This isn't the case; the .postgraphilerc.js file is likely being replaced with a better system though. https://www.graphile.org/postgraphile/usage-cli/#rc-file-options

That said, for advanced usage I'd generally advise the library because managing CLI flags becomes a chore quickly ;)

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

No branches or pull requests

2 participants