Specifically, I want them to run the following: https://django-q.readthedocs.io/en/latest/monitor.html
I am looking to write a minimal wrapper-script which would simply allow them to run a single command, for example, myproject-admin manage qmonitor
from their own machines (connecting to live Docker swarm as necessary) and returning the output they need to their machines.
Currently, the networks I have created are not attachable by non-swarm containers.
Is there a security risk to allowing docker swarm networks to become attachable?
The solution I can think about is to reconfigure the network to be attachable so that the wrapper script could via ssh, execute docker run -it --rm --network database --name task-name_2020-05-29_12-51-00 django_image:2020-05-29 /app/manage.py qmonitor
Without changing the network to be attachable, is there another way of accessing the database?
I know I could exec against the running web server but isn't that bad practice?
In the future, if this project generates income, the database would move to Amazon RDS or similar. For security, I would want to limit access of the database to the Docker swarm database
network as well so that the database is not accessible via the host (is that possible? If so, the scenario I laid out here still reflects the nature of the problem)
Another possible solution I thought about is creating a temporary network link to the database via ssh port forwarding (or another means), and then getting the admins to run the command locally (Django docker container run locally) but communicating to the database. This is preferable because it would allow the devs to use their own system resources (but might not be ideal if bandwidth becomes the constraint).
Any alternate suggestions on my architecture to circumvent this problem would also be appreciated. Currently, this is a pet project to develop my skills. Docker swarm is probably not yet required, but I require a live project to practice my skills, hence why it is being used in this case.
For context, here is my working docker-compose file which I use for the project (slightly modified):
The only commands done as a pre-requisite are docker secrets create ...
, docker network create -d overlay --scope swarm database
and docker network create -d overlay --scope swarm traefik
version: "3.8"
networks:
traefik:
external: true
name: traefik
database:
external: true
name: database
secrets:
DJANGO_SECRETS_FILE:
external: true
HASURA_SECRETS_FILE:
external: true
POSTGRES_PASSWORD_FILE:
external: true
CF_API_EMAIL_FILE:
external: true
CF_API_KEY_FILE:
external: true
volumes:
staticfiles: {}
database: {}
services:
django:
command: gunicorn config.wsgi:app -w 4 -b 0.0.0.0:8000
depends_on:
- postgres
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.django.entrypoints: http
traefik.http.routers.django.rule: Host(`admin.myproject.***.dev`)
traefik.http.routers.django-secure.entrypoints: https
traefik.http.routers.django-secure.rule: Host(`admin.myproject.***.dev`)
traefik.http.routers.django-secure.tls.certresolver: dns
traefik.http.services.django.loadbalancer.server.port: 8000
restart_policy:
condition: on-failure
environment:
ALLOWED_HOSTS: admin.myproject.***.dev
DEBUG: "true"
DJANGO_CONFIGURATION: Staging
DJANGO_SECRETS_FILE: /run/secrets/DJANGO_SECRETS_FILE
image: django_image:2020-05-29
networks:
- traefik
- database
secrets:
- DJANGO_SECRETS_FILE
volumes:
- staticfiles:/static
working_dir: /app
hasura:
command:
- graphql-engine
- serve
- --server-port=5000
depends_on:
- postgres
deploy:
labels:
traefik.enable: "true"
traefik.http.routers.hasura.entrypoints: http
traefik.http.routers.hasura.rule: Host(`api.myproject.***.dev`)
traefik.http.routers.hasura-secure.entrypoints: https
traefik.http.routers.hasura-secure.rule: Host(`api.myproject.***.dev`)
traefik.http.routers.hasura-secure.tls.certresolver: dns
traefik.http.services.hasura.loadbalancer.server.port: 5000
restart_policy:
condition: on-failure
entrypoint:
- sh
- /hasura-entrypoint.sh
environment:
HASURA_GRAPHQL_ENABLE_CONSOLE: "true"
HASURA_SECRETS_FILE: /run/secrets/HASURA_SECRETS_FILE
image: hasura/graphql-engine:v1.2.1
networks:
- traefik
- database
secrets:
- HASURA_SECRETS_FILE
volumes:
- ./scripts/hasura-entrypoint.sh:/hasura-entrypoint.sh
postgres:
deploy:
restart_policy:
condition: on-failure
environment:
POSTGRES_PASSWORD_FILE: /run/secrets/POSTGRES_PASSWORD_FILE
POSTGRES_USER: staging
POSTGRES_DB: staging
image: postgres:12.3-alpine
networks:
- database
volumes:
- database:/var/lib/postgresql/data
secrets:
- POSTGRES_PASSWORD_FILE
traefik:
command:
- --api.insecure=true
- --api.dashboard=true
- --api.debug=true
- --certificatesResolvers.dns.acme.dnsChallenge.delayBeforeCheck=0
- --certificatesResolvers.dns.acme.dnsChallenge.provider=cloudflare
- --entrypoints.http.address=:80
- --entrypoints.https.address=:443
- --log.level=DEBUG
- --providers.docker=true
- --providers.docker.endpoint=unix:///var/run/docker.sock
- --providers.docker.exposedbydefault=false
- --providers.docker.network=traefik
- --providers.docker.swarmMode=true
deploy:
labels:
traefik.enable: "true"
traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme: https
traefik.http.routers.traefik.entrypoints: http
traefik.http.routers.traefik.middlewares: traefik-https-redirect
traefik.http.routers.traefik.rule: Host(`dash.myproject.***.dev`)
traefik.http.routers.traefik-secure.entrypoints: https
traefik.http.routers.traefik-secure.rule: Host(`dash.myproject.***.dev`)
traefik.http.routers.traefik-secure.service: api@internal
traefik.http.routers.traefik-secure.tls: "true"
traefik.http.routers.traefik-secure.tls.certresolver: dns
traefik.http.services.traefik.loadbalancer.server.port: 8080
placement:
constraints:
- node.role == manager
restart_policy:
condition: on-failure
environment:
CF_API_EMAIL_FILE: /run/secrets/CF_API_EMAIL_FILE
CF_API_KEY_FILE: /run/secrets/CF_API_KEY_FILE
image: traefik:v2.2.1
networks:
- traefik
ports:
- 80:80
- 443:443
secrets:
- CF_API_EMAIL_FILE
- CF_API_KEY_FILE
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./acme.json:/acme.json