From 5678e049afffdca57a917bc03fc413956bc0e3c5 Mon Sep 17 00:00:00 2001 From: Francisco Gaona Date: Wed, 29 Oct 2025 08:20:26 +0100 Subject: [PATCH] Nomad Passbolt Job --- README.md | 111 ++++++++++++++++++ passbolt-service-defaults.hcl | 3 + passbolt.nomad | 214 ++++++++++++++++++++++++++++++++++ passbolt.yml | 48 ++++++++ 4 files changed, 376 insertions(+) create mode 100644 README.md create mode 100644 passbolt-service-defaults.hcl create mode 100644 passbolt.nomad create mode 100644 passbolt.yml diff --git a/README.md b/README.md new file mode 100644 index 0000000..899a617 --- /dev/null +++ b/README.md @@ -0,0 +1,111 @@ +# Passbolt Nomad Deployment + +This repository contains the Nomad job specification for deploying Passbolt password manager on a Nomad cluster with Consul Connect and Traefik integration. + +## Prerequisites + +Before deploying Passbolt, ensure you have: + +1. A Nomad cluster with Consul Connect enabled +2. Traefik configured for service routing +3. Docker volumes directory created on the target node + +## Deployment Steps + +### 1. Register Consul Service Defaults + +Before deploying the Nomad job, you must register the service defaults with Consul: + +```bash +consul config write passbolt-service-defaults.hcl +``` + +This configures the protocol for the `passbolt-app` service to work with the Consul Connect ingress gateway. + +### 2. Set Up Volume Permissions + +The GPG volume directory requires specific permissions for Passbolt to function correctly: + +```bash +# Create the directory if it doesn't exist +sudo mkdir -p /root/nomadvolumes/passbolt/data/gpg + +# Set ownership to root with www-data group +sudo chown root:www-data /root/nomadvolumes/passbolt/data/gpg + +# Set permissions to 774 +sudo chmod 774 /root/nomadvolumes/passbolt/data/gpg +``` + +### 3. Deploy the Nomad Job + +```bash +nomad run passbolt.nomad +``` + +## Post-Deployment Configuration + +### Creating the First Admin User + +After the deployment is successful, you need to create the first admin user. Connect to the Passbolt container and run: + +```bash +nomad alloc exec -task passbolt-app su -m -c "/usr/share/php/passbolt/bin/cake \ + passbolt register_user \ + -u YOUR_EMAIL \ + -f YOUR_NAME \ + -l YOUR_LASTNAME \ + -r admin" -s /bin/sh www-data +``` + +Replace: +- `` with the actual allocation ID (find it with `nomad job status passbolt`) +- `YOUR_EMAIL` with the admin email address +- `YOUR_NAME` with the admin first name +- `YOUR_LASTNAME` with the admin last name + +This command will output a registration link. Follow the link to complete the user setup process, including: +- Installing the Passbolt browser extension +- Creating your passphrase +- Generating your recovery kit + +For detailed instructions, see: [Passbolt Docker Installation Guide](https://www.passbolt.com/docs/hosting/install/ce/docker/) + +### Importing Passwords from CSV + +If you're migrating from another password manager (like Dropbox Passwords), you can import passwords using a CSV file. + +Follow this community guide for the import process: +[Migrating from Dropbox Passwords to Passbolt](https://community.passbolt.com/t/migrating-from-dropbox-passwords-to-passbolt/13653) + +## Access + +Once deployed and configured, Passbolt will be available at: + +**https://passbolt.neogenia.com.mx** + +## Architecture + +The deployment consists of three main groups: + +1. **ingress-group**: Consul Connect ingress gateway with Traefik integration for external routing +2. **database**: MariaDB 10.11 for data persistence +3. **passbolt**: The main Passbolt application with Consul Connect sidecar for secure service mesh communication + +## Volumes + +The deployment uses the following persistent volumes: + +- `/root/nomadvolumes/passbolt/data/mysql` - MariaDB database files +- `/root/nomadvolumes/passbolt/data/gpg` - GPG keys (requires special permissions) +- `/root/nomadvolumes/passbolt/data/jwt` - JWT tokens + +## Security Notes + +- Database credentials are configured in the Nomad job file. Update these for production use. +- All service-to-service communication uses Consul Connect for mTLS encryption +- Traefik handles TLS termination with automatic certificate generation via Let's Encrypt + +## Additional Notes + +- This repo contains the original docker-compose yml file as a reference but it is not needed to run the nomad job diff --git a/passbolt-service-defaults.hcl b/passbolt-service-defaults.hcl new file mode 100644 index 0000000..69a1677 --- /dev/null +++ b/passbolt-service-defaults.hcl @@ -0,0 +1,3 @@ +Kind = "service-defaults" +Name = "passbolt-app" +Protocol = "http" diff --git a/passbolt.nomad b/passbolt.nomad new file mode 100644 index 0000000..da3a7e9 --- /dev/null +++ b/passbolt.nomad @@ -0,0 +1,214 @@ +job "passbolt" { + + datacenters = ["dc1"] + type = "service" + + constraint { + attribute = "${node.unique.name}" + value = "mercury-nomad" + } + + group "ingress-group" { + count = 1 + + network { + mode = "bridge" + + port "inbound" { + to = 8080 + } + } + + service { + name = "passbolt-ingress" + port = "inbound" + + tags = [ + "traefikcontabo.enable=true", + "traefikcontabo.http.middlewares.passbolt-redirectscheme.redirectscheme.scheme=https", + "traefikcontabo.http.middlewares.passbolt-redirectscheme.redirectscheme.permanent=true", + "traefikcontabo.http.routers.passbolt-http.rule=Host(`passbolt.neogenia.com.mx`)", + "traefikcontabo.http.routers.passbolt-http.entrypoints=http", + "traefikcontabo.http.routers.passbolt-http.middlewares=passbolt-redirectscheme", + "traefikcontabo.http.routers.passbolt-https.rule=Host(`passbolt.neogenia.com.mx`)", + "traefikcontabo.http.routers.passbolt-https.entrypoints=https", + "traefikcontabo.http.routers.passbolt-https.tls=true", + "traefikcontabo.http.routers.passbolt-https.tls.certresolver=myresolver", + ] + + connect { + gateway { + ingress { + listener { + port = 8080 + protocol = "http" + service { + name = "passbolt-app" + hosts = ["passbolt.neogenia.com.mx"] + } + } + } + } + } + } + } + + group "database" { + count = 1 + + update { + max_parallel = 1 + min_healthy_time = "30s" + healthy_deadline = "5m" + progress_deadline = "10m" + auto_revert = true + } + + restart { + attempts = 3 + interval = "5m" + delay = "30s" + mode = "delay" + } + + network { + mode = "bridge" + } + + task "mariadb" { + driver = "docker" + + config { + image = "mariadb:10.11" + volumes = [ + "/root/nomadvolumes/passbolt/data/mysql:/var/lib/mysql" + ] + } + + env { + MYSQL_RANDOM_ROOT_PASSWORD = "true" + MYSQL_DATABASE = "passbolt" + MYSQL_USER = "passbolt" + MYSQL_PASSWORD = "akjsdhfkrTThasgh5qw7" + } + + resources { + cpu = 500 + memory = 512 + } + } + + service { + name = "passbolt-db" + port = 3306 + + connect { + sidecar_service {} + } + } + } + + group "passbolt" { + count = 1 + + update { + max_parallel = 1 + min_healthy_time = "30s" + healthy_deadline = "10m" + progress_deadline = "15m" + auto_revert = true + } + + restart { + attempts = 5 + interval = "5m" + delay = "30s" + mode = "delay" + } + + reschedule { + attempts = 10 + interval = "1h" + delay = "30s" + delay_function = "exponential" + max_delay = "120s" + unlimited = false + } + + network { + mode = "bridge" + port "expose" { + to = -1 + } + } + + task "passbolt-app" { + driver = "docker" + + config { + image = "passbolt/passbolt:latest-ce" + volumes = [ + "/root/nomadvolumes/passbolt/data/gpg:/etc/passbolt/gpg", + "/root/nomadvolumes/passbolt/data/jwt:/etc/passbolt/jwt" + ] + command = "/usr/bin/wait-for.sh" + args = [ + "-t", + "0", + "127.0.0.1:3306", + "--", + "/docker-entrypoint.sh" + ] + } + + env { + APP_FULL_BASE_URL = "https://passbolt.neogenia.com.mx" + DATASOURCES_DEFAULT_HOST = "127.0.0.1" + DATASOURCES_DEFAULT_PORT = "3306" + DATASOURCES_DEFAULT_USERNAME = "passbolt" + DATASOURCES_DEFAULT_PASSWORD = "akjsdhfkrTThasgh5qw7" + DATASOURCES_DEFAULT_DATABASE = "passbolt" + } + + resources { + cpu = 500 + memory = 512 + } + } + + service { + name = "passbolt-app" + port = 80 + + connect { + sidecar_service { + proxy { + upstreams { + destination_name = "passbolt-db" + local_bind_port = 3306 + } + expose { + path { + path = "/healthcheck/status.json" + protocol = "http" + local_path_port = 80 + listener_port = "expose" + } + } + } + } + } + + check { + expose = true + type = "http" + path = "/healthcheck/status.json" + interval = "30s" + timeout = "5s" + } + + } + + } + +} diff --git a/passbolt.yml b/passbolt.yml new file mode 100644 index 0000000..2a4d3e4 --- /dev/null +++ b/passbolt.yml @@ -0,0 +1,48 @@ +services: + db: + image: mariadb:10.11 + restart: unless-stopped + environment: + MYSQL_RANDOM_ROOT_PASSWORD: "true" + MYSQL_DATABASE: "passbolt" + MYSQL_USER: "passbolt" + MYSQL_PASSWORD: "P4ssb0lt" + volumes: + - database_volume:/var/lib/mysql + + passbolt: + image: passbolt/passbolt:latest-ce + #Alternatively you can use rootless: + #image: passbolt/passbolt:latest-ce-non-root + restart: unless-stopped + depends_on: + - db + environment: + APP_FULL_BASE_URL: https://passbolt.local + DATASOURCES_DEFAULT_HOST: "db" + DATASOURCES_DEFAULT_USERNAME: "passbolt" + DATASOURCES_DEFAULT_PASSWORD: "P4ssb0lt" + DATASOURCES_DEFAULT_DATABASE: "passbolt" + volumes: + - gpg_volume:/etc/passbolt/gpg + - jwt_volume:/etc/passbolt/jwt + command: + [ + "/usr/bin/wait-for.sh", + "-t", + "0", + "db:3306", + "--", + "/docker-entrypoint.sh", + ] + ports: + - 80:80 + - 443:443 + #Alternatively for non-root images: + # - 80:8080 + # - 443:4433 + +volumes: + database_volume: + gpg_volume: + jwt_volume: