Nomad Passbolt Job

This commit is contained in:
Francisco Gaona
2025-10-29 08:20:26 +01:00
commit 5678e049af
4 changed files with 376 additions and 0 deletions

111
README.md Normal file
View File

@@ -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 <ALLOCATION_ID> 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:
- `<ALLOCATION_ID>` 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

View File

@@ -0,0 +1,3 @@
Kind = "service-defaults"
Name = "passbolt-app"
Protocol = "http"

214
passbolt.nomad Normal file
View File

@@ -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"
}
}
}
}

48
passbolt.yml Normal file
View File

@@ -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: