Skip to content

Instantly share code, notes, and snippets.

@nielsmaerten
Last active January 1, 2024 11:23
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save nielsmaerten/c9bf195bb0c5e2a9e8653f4302a384c2 to your computer and use it in GitHub Desktop.
Save nielsmaerten/c9bf195bb0c5e2a9e8653f4302a384c2 to your computer and use it in GitHub Desktop.
MongoDB + MongoExpress over TLS on Docker

Secure MongoDB on Docker (incl MongoExpress, Let's Encrypt)

This gist contains files and instructions to setup MongoDB on a simple docker host.
Connections to Mongo are secured using TLS, and the certificate is signed using Let's Encrypt.
MongoExpress is included as well, but should only be used over an SSH tunnel.

  1. Update .env

  2. Make sure docker-compose is available

    # Required on GCP Cloud Optimized OS
    
    # Add alias for docker-compose that runs the tool inside a container 
    
    echo alias docker-compose="'"'docker run --rm -v /var/run/docker.sock:/var/run/docker.sock -v "$PWD:$PWD" -w="$PWD" docker/compose:1.24.0'"'" >> ~/.bashrc
    
    # Reload to make the alias available
    source ~/.bashrc
  3. Run: docker-compose up [-d]
    This will take a long time to initialize. Here's what it's doing: (cursive denotes a docker container)

    • cloudflare registers an A record in Cloudflare that points to this machine
    • letsencrypt generates a certificate, and gets it signed by Let'sEncrypt
    • mongo uses the private key to start up a TLS secured MongoDB instance
    • mongo-express spins up a HTML frontend to mongo

    mongo and mongo-express will keep restarting until the private key becomes available.

  4. Open port 27017 (MongoDB) to the Internet.
    Also open ports 443 and 80 so Let's Encrypt can authenticate over HTTP
    Do not open port 81 (MongoExpress).
    Use an SSH tunnel if you want to access MongoExpress.

  5. Connect to Mongo and create desired users and databases

# Opens the terminal of the container running mongo
docker exec -it mongo bash

# Connects to mongo client
mongo -u root --authenticationDatabase admin --ssl --sslAllowInvalidHostnames

# Creates a database 'mydatabase'
use mydatabase 

# Adds a user 'USER123' with password 'ABC123' to database 'mydatabase'
db.createUser(db.createUser({user: "USER123",pwd: "ABC123",roles:[{role:"readWrite",db:"mydatabase"}]})
COMPOSE_PROJECT_NAME=nightscout-mongo
# All these vars are required:
# Run `id $user` to find PUID and PGID of the current user
PUID=5000
PGID=5000
SUBDOMAIN=
DOMAIN=
STAGING=true
TIMEZONE=Europe/Brussels
MONGO_ROOT_PASSWORD=SET_SECURE_PW_HERE
CLOUDFLARE_API_KEY=
# Optional, will receive updates on letsencrypt certificate expiry
EMAIL=
# Don't change this unless you know what you're doing
DATA_DIR=./data/
version: "3.1"
volumes:
mongodb: null
services:
## Mongo Express
## !! WARNING: Do not open this port in the firewall
## !! You should only be able to reach MongoExpress over an SSH tunnel
mongo-express:
image: mongo-express:0.54.0
container_name: mongo-express
restart: unless-stopped
depends_on:
- mongo
ports:
- 81:8081
volumes:
- ${DATA_DIR}/lets-encrypt-config/keys/:/keys/
# Thanks to
# https://github.com/mongo-express/mongo-express/issues/484#issuecomment-511480169
- "./mongo-express-config.js:/node_modules/mongo-express/config.js:ro"
environment:
- ME_CONFIG_MONGODB_CA_FILE=/keys/cert.crt
- ME_CONFIG_MONGODB_ADMINUSERNAME=root
- ME_CONFIG_MONGODB_ADMINPASSWORD=${MONGO_ROOT_PASSWORD}
## MongoDB
mongo:
image: mongo:4.2.3-bionic
container_name: mongo
restart: unless-stopped
depends_on:
- letsencrypt
ports:
- 27017:27017
environment:
- MONGO_INITDB_ROOT_USERNAME=root
- MONGO_INITDB_ROOT_PASSWORD=${MONGO_ROOT_PASSWORD}
volumes:
- ${DATA_DIR}/mongo-db/:/data/db
- ${DATA_DIR}/lets-encrypt-config/keys/letsencrypt/:/keys/
healthcheck:
test: echo 'db.runCommand("ping").ok' | mongo 127.0.0.1:27017/test --tls --tlsAllowInvalidCertificates --quiet
interval: 10s
timeout: 5s
retries: 5
command:
[
"--tlsMode",
"requireTLS",
"--tlsCertificateKeyFile",
"/keys/priv-fullchain-bundle.pem",
]
## Let's Encrypt
letsencrypt:
image: linuxserver/letsencrypt:latest
container_name: letsencrypt
restart: unless-stopped
depends_on:
- cloudflare
environment:
- PUID=${PUID}
- PGID=${PGID}
- TZ=${TIMEZONE}
- URL=${DOMAIN}
- SUBDOMAINS=${SUBDOMAIN},
- VALIDATION=http
# - DNSPLUGIN=cloudflare #optional
# - DUCKDNSTOKEN=<token> #optional
- EMAIL=${EMAIL} #optional
# - DHLEVEL=2048 #optional
- ONLY_SUBDOMAINS=true #optional
# - EXTRA_DOMAINS=<extradomains> #optional
- STAGING=${STAGING} #optional
volumes:
- ${DATA_DIR}/lets-encrypt-config/:/config/
- ./index.html:/config/www/index.html
- ./robots.txt:/config/www/robots.txt
ports:
- 443:443
- 80:80
## DDNS with Cloudflare
cloudflare:
image: oznu/cloudflare-ddns:latest
container_name: cloudflare
restart: unless-stopped
environment:
- API_KEY=${CLOUDFLARE_API_KEY}
- ZONE=${DOMAIN}
- SUBDOMAIN=${SUBDOMAIN}
- PROXIED=false
module.exports = {
mongodb: {
ssl: true,
sslValidate: false,
sslCA: [
require('fs').readFileSync(process.env.ME_CONFIG_MONGODB_CA_FILE).toString()
],
}
};
User-agent: *
Disallow: /
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment