Created
June 9, 2025 19:07
-
-
Save oidebrett/5eb260124513f71674b5534c45da67cc to your computer and use it in GitHub Desktop.
Setting up Komodo and Pangolin
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| I want you to help me writing another article in markdown format for a foundational article on how to install and set up komodo and pangolin | |
| komodo is a tool to build and deploy software on many servers | |
| Komodo is a web app to provide structure for managing your servers, builds, deployments, and automated procedures. | |
| With Komodo you can: | |
| Connect all of your servers, alert on CPU usage, memory usage, and disk usage, and connect to shell sessions. | |
| Create, start, stop, and restart Docker containers on the connected servers, view their status and logs, and connect to container shell. | |
| Deploy docker compose stacks. The file can be defined in UI, or in a git repo, with auto deploy on git push. | |
| Build application source into auto-versioned Docker images, auto built on webhook. Deploy single-use AWS instances for infinite capacity. | |
| Manage repositories on connected servers, which can perform automation via scripting / webhooks. | |
| Manage all your configuration / environment variables, with shared global variable and secret interpolation. | |
| Keep a record of all the actions that are performed and by whom. | |
| Using a VPS | |
| If you need a VPS to run Pangolin so we will use this for our komodo server too | |
| Database choice | |
| You need to choose which DB to use | |
| Using MongoDB | |
| Lower CPU usage, Higher RAM usage. | |
| Some systems do not support running the latest MongoDB versions. | |
| Using FerretDB (Postgres) | |
| Lower RAM usage, Higher CPU usage. | |
| I will use FerretDB in this example | |
| Installation Steps | |
| 0. First we will correct the docker network that will underpin all our docker contains | |
| ``` | |
| docker network create pangolin | |
| ``` | |
| 1. Downloading and Running the Installer | |
| Copy komodo/ferretdb.compose.yaml and komodo/compose.env to your host: | |
| wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/ferretdb.compose.yaml && \ | |
| wget -P komodo https://raw.githubusercontent.com/moghtech/komodo/main/compose/compose.env | |
| we will do a minor change to the compose file to add a network at the end of the file | |
| 2. Edit the variables in komodo/compose.env. | |
| I will leave the DB credentials for username as admin but use `openssl rand -base64 10` to generate my password | |
| ``` | |
| ## DB credentials | |
| KOMODO_DB_USERNAME=admin | |
| KOMODO_DB_PASSWORD=CHANGEME | |
| ``` | |
| I will set the a secure passkey to authenticate between Core / Periphery using `openssl rand -base64 32` | |
| ``` | |
| KOMODO_PASSKEY=a_random_passkey | |
| ``` | |
| I will change the `KOMODO_HOST=https://demo.komo.do` to the url of my pangolin server with | |
| ``` | |
| KOMODO_HOST=https://komodo.yourdomain.com | |
| ``` | |
| I will change the webhook and jwt tokens again using `openssl rand -base64 32` | |
| ## Used to auth incoming webhooks. Alt: KOMODO_WEBHOOK_SECRET_FILE | |
| KOMODO_WEBHOOK_SECRET=a_random_secret | |
| ## Used to generate jwt. Alt: KOMODO_JWT_SECRET_FILE | |
| KOMODO_JWT_SECRET=a_random_jwt_secret | |
| I will leave the local auth and we could use Pangolin for this also | |
| ``` | |
| KOMODO_LOCAL_AUTH=true | |
| ``` | |
| 3. I will start the docker stack | |
| ``` | |
| cd komodo | |
| ``` | |
| Deploy: | |
| ``` | |
| docker compose -p komodo -f ferretdb.compose.yaml --env-file compose.env up -d | |
| ``` | |
| 4. We then confirm its all working by going to | |
| ``` | |
| http://youripaddress:9120 | |
| ``` | |
| you will pick a username/password and hit signup | |
| 4. Now I will use komodo to start install my pangolin instance | |
| I will create a new stack and name it `pangolin-setup` | |
| I Choose Mode and select `UI Defined` and paste in the following | |
| ``` | |
| services: | |
| # Setup container that creates folder structure and config files | |
| setup: | |
| image: alpine:latest | |
| container_name: pangolin-setup | |
| volumes: | |
| - ./:/host-setup | |
| - /var/run/docker.sock:/var/run/docker.sock | |
| environment: | |
| - DOMAIN=${DOMAIN:-} | |
| - EMAIL=${EMAIL:-} | |
| - ADMIN_PASSWORD=${ADMIN_PASSWORD:-} | |
| - ADMIN_SUBDOMAIN=${ADMIN_SUBDOMAIN:-pangolin} | |
| - GITHUB_USER=${GITHUB_USER:-oidebrett} | |
| - GITHUB_REPO=${GITHUB_REPO:-getcontextware} | |
| - GITHUB_BRANCH=${GITHUB_BRANCH:-main} | |
| command: | | |
| sh -c " | |
| echo 'π Starting Pangolin setup container...' | |
| # Install required tools | |
| apk add --no-cache curl docker-cli openssl | |
| # Validate required environment variables | |
| if [ -z \"$$DOMAIN\" ] || [ -z \"$$EMAIL\" ] || [ -z \"$$ADMIN_PASSWORD\" ]; then | |
| echo 'β Error: Required environment variables not set!' | |
| echo 'Usage: DOMAIN=example.com [email protected] ADMIN_PASSWORD=mypassword docker compose -f docker-compose-setup.yml up' | |
| echo 'Required variables:' | |
| echo ' DOMAIN - Your domain name (e.g., example.com)' | |
| echo ' EMAIL - Email for Lets Encrypt certificates' | |
| echo ' ADMIN_PASSWORD - Admin password for Pangolin (min 8 chars)' | |
| echo 'Optional variables:' | |
| echo ' ADMIN_SUBDOMAIN - Subdomain for admin portal (default: pangolin)' | |
| exit 1 | |
| fi | |
| # Check if config folder already exists | |
| if [ -d \"/host-setup/config\" ]; then | |
| echo 'β οΈ Config folder already exists!' | |
| echo 'To avoid overwriting your configuration, setup will not proceed.' | |
| echo 'If you want to run setup again, please remove or rename the existing config folder.' | |
| exit 1 | |
| fi | |
| # Validate domain format | |
| if ! echo \"$$DOMAIN\" | grep -E '^[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9]?\\.[a-zA-Z]{2,}$$' > /dev/null; then | |
| echo 'β Error: Invalid domain format' | |
| exit 1 | |
| fi | |
| # Validate email format | |
| if ! echo \"$$EMAIL\" | grep -E '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$$' > /dev/null; then | |
| echo 'β Error: Invalid email format' | |
| exit 1 | |
| fi | |
| # Validate password length | |
| if [ $${#ADMIN_PASSWORD} -lt 8 ]; then | |
| echo 'β Error: Password must be at least 8 characters long' | |
| exit 1 | |
| fi | |
| echo 'β Environment variables validated' | |
| # Download container setup script from GitHub | |
| echo 'π₯ Downloading setup script from GitHub...' | |
| BASE_URL=\"https://raw.githubusercontent.com/$$GITHUB_USER/$$GITHUB_REPO/$$GITHUB_BRANCH\" | |
| if ! curl -fsSL \"$$BASE_URL/container-setup.sh\" -o /container-setup.sh; then | |
| echo 'β Failed to download setup script from GitHub' | |
| echo 'Make sure the repository exists and is accessible:' | |
| echo \"$$BASE_URL/container-setup.sh\" | |
| exit 1 | |
| fi | |
| chmod +x /container-setup.sh | |
| echo 'β Setup script downloaded' | |
| # Run the setup script | |
| echo 'π§ Running setup script...' | |
| /container-setup.sh | |
| # Create docker-compose.yml for services | |
| echo 'π Creating docker-compose.yml for services...' | |
| cat > /host-setup/docker-compose.yml << 'EOF' | |
| services: | |
| # Main Pangolin application | |
| pangolin: | |
| image: fosrl/pangolin:1.5.0 | |
| container_name: pangolin | |
| restart: unless-stopped | |
| volumes: | |
| - ./config:/app/config | |
| healthcheck: | |
| test: ["CMD", "curl", "-f", "http://localhost:3001/api/v1/"] | |
| interval: "3s" | |
| timeout: "3s" | |
| retries: 15 | |
| # Gerbil WireGuard management | |
| gerbil: | |
| image: fosrl/gerbil:1.0.0 | |
| container_name: gerbil | |
| restart: unless-stopped | |
| depends_on: | |
| pangolin: | |
| condition: service_healthy | |
| command: | |
| - --reachableAt=http://gerbil:3003 | |
| - --generateAndSaveKeyTo=/var/config/key | |
| - --remoteConfig=http://pangolin:3001/api/v1/gerbil/get-config | |
| - --reportBandwidthTo=http://pangolin:3001/api/v1/gerbil/receive-bandwidth | |
| volumes: | |
| - ./config/:/var/config | |
| cap_add: | |
| - NET_ADMIN | |
| - SYS_MODULE | |
| ports: | |
| - 51820:51820/udp | |
| - 443:443 # Port for traefik because of the network_mode | |
| - 80:80 # Port for traefik because of the network_mode | |
| # Traefik reverse proxy | |
| traefik: | |
| image: traefik:v3.4.0 | |
| container_name: traefik | |
| restart: unless-stopped | |
| network_mode: service:gerbil # Ports appear on the gerbil service | |
| depends_on: | |
| pangolin: | |
| condition: service_healthy | |
| command: | |
| - --configFile=/etc/traefik/traefik_config.yml | |
| volumes: | |
| - ./config/traefik:/etc/traefik:ro # Volume to store the Traefik configuration | |
| - ./config/letsencrypt:/letsencrypt # Volume to store the Lets Encrypt certificates | |
| networks: | |
| default: | |
| driver: bridge | |
| name: pangolin | |
| EOF | |
| echo 'β Setup completed! The stack is ready to start.' | |
| echo 'π Start your services with: docker compose up -d' | |
| echo 'π Access at: https://'"$$ADMIN_SUBDOMAIN"'.'"$$DOMAIN" | |
| echo 'π€ Admin login: admin@'"$$DOMAIN" | |
| # Keep container running briefly to show completion message | |
| sleep 5 | |
| " | |
| restart: "no" | |
| ``` | |
| I then add the environment variables for my pangolin install | |
| ``` | |
| DOMAIN=example.com | |
| [email protected] | |
| ADMIN_PASSWORD=mypassword | |
| ADMIN_SUBDOMAIN=pangolin | |
| ``` | |
| Hit Update and Confirm and then select Deploy | |
| You should look in the folder `/etc/komodo/stacks/pangolin-setup` and the pangolin folder will be there | |
| ``` | |
| /etc/komodo/stacks/pangolin-setup | |
| βββ compose.yaml | |
| βββ config | |
| β βββ config.yml | |
| β βββ letsencrypt | |
| β βββ traefik | |
| β βββ dynamic_config.yml | |
| β βββ traefik_config.yml | |
| βββ DEPLOYMENT_INFO.txt | |
| βββ docker-compose.yml | |
| ``` | |
| 5. Now we will start the pangolin stack | |
| Create a new stack called "pangolin-stack" | |
| choose option "file on server" | |
| and select the path | |
| ``` | |
| /etc/komodo/stacks/pangolin-setup | |
| ``` | |
| select the file path to be | |
| `docker-compose.yml` | |
| Then deploy the stack. This should start everything correctly and you should see the containers | |
| ``` | |
| gerbil | |
| komodo-core-1 | |
| komodo-ferretdb-1 | |
| komodo-postgres-1 | |
| pangolin | |
| traefik | |
| ``` | |
| 6. Clean up | |
| You can now destory the pangolin-setup stack as its not needed anymore | |
| 7. Secure your komodo install | |
| Now we are going to secure this by adding a pangolin resource for this. We do this by adding a new resource, give it a name like komodo.yourdomain.com | |
| and make sure you make this on the local site. Also set the target to be "komodo-core-1" and port 9120. Leave this resource as a protected resource | |
| with pangolin's authentication | |
| see screenshot of target setup | |
| You should now be able to navigate to https://komodo.yourdomain.com and see the komodo login | |
| 8. Make sure you protect the komodo so it cant be accessed directly using the ip address and port | |
| if you already set up a rule to allow port 3456 then deny it | |
| ``` | |
| sudo ufw delete allow 9120/tcp | |
| ``` | |
| or, if you dont already have it set up, then deny the port by using | |
| ``` | |
| sudo ufw deny 9120/tcp | |
| ``` | |
| then make sure you enable for firewall | |
| ``` | |
| sudo ufw enable | |
| ``` | |
| you must also remove the ports from the core docker compose section of the ferretdb.compose.yaml | |
| remove these 2 lines | |
| ``` | |
| ports: | |
| - "9120:9120" | |
| ``` | |
| and then use komodo to stop and restart the komodo-core-1 so that the port changes takes effect. When redeployed check that you cant access the komodo portal via the ip/port | |
| ``` | |
| docker compose -p komodo -f ferretdb.compose.yaml --env-file compose.env down | |
| docker compose -p komodo -f ferretdb.compose.yaml --env-file compose.env up -d --force-recreate | |
| ``` | |
| ``` | |
| http://youripaddress:9120 | |
| ``` | |
| 9. Extra steps if you installed crowdsec | |
| stop the entire pangolin-stack | |
| you will have previously set up the parameter/variables like this: | |
| ``` | |
| DOMAIN=example.com | |
| [email protected] | |
| ADMIN_PASSWORD=mypassword | |
| ADMIN_SUBDOMAIN=pangolin | |
| CROWDSEC_ENROLLMENT_KEY=your-key-here | |
| ``` | |
| cd into `/etc/komodo/stacks/pangolin-setup` | |
| update the docker-compose.yml section for crowdsec and update the ENROLLMENTKEY | |
| change | |
| ``` | |
| ENROLL_KEY: INSERT-ENROLLMENT-KEY-HERE | |
| ``` | |
| and go into the shell of the crowdsec container | |
| ``` | |
| docker run --rm -it \ | |
| --name crowdsec-shell \ | |
| --entrypoint /bin/sh \ | |
| -e GID="1000" \ | |
| -e COLLECTIONS="crowdsecurity/traefik crowdsecurity/appsec-virtual-patching crowdsecurity/appsec-generic-rules" \ | |
| -e ENROLL_INSTANCE_NAME="pangolin-crowdsec" \ | |
| -e PARSERS="crowdsecurity/whitelists" \ | |
| -e ENROLL_KEY="REMOVED" \ | |
| -e ACQUIRE_FILES="/var/log/traefik/access.log" \ | |
| -e ENROLL_TAGS="docker" \ | |
| -v "$(pwd)/config/crowdsec:/etc/crowdsec" \ | |
| -v "$(pwd)/config/crowdsec/db:/var/lib/crowdsec/data" \ | |
| -v "$(pwd)/config/crowdsec_logs/auth.log:/var/log/auth.log:ro" \ | |
| -v "$(pwd)/config/crowdsec_logs/syslog:/var/log/syslog:ro" \ | |
| -v "$(pwd)/config/crowdsec_logs:/var/log" \ | |
| -v "$(pwd)/config/traefik/logs:/var/log/traefik" \ | |
| -v "$(pwd)/config/traefik/conf/captcha.html:/etc/traefik/conf/captcha.html" \ | |
| crowdsecurity/crowdsec:latest | |
| ``` | |
| you can then run | |
| `cscli hub update` | |
| you will see | |
| `Downloading /etc/crowdsec/hub/.index.json` | |
| then Generate the online_api_credentials | |
| You need to regenerate the /etc/crowdsec/online_api_credentials.yaml. The easiest way is rm /etc/crowdsec/online_api_credentials.yaml and register again using the enrolment key from the previous step | |
| ``` | |
| touch /etc/crowdsec/online_api_credentials.yaml | |
| cscli capi register | |
| cscli console enroll <id> | |
| ``` | |
| now check that you have all the patterns | |
| ls /etc/crowdsec/patterns/ | |
| if you donβt see any folders your crowdsec doesnβt have the required patterns | |
| Hereβs a working around to download them | |
| ``` | |
| wget -P /opt https://github.com/crowdsecurity/crowdsec/archive/refs/tags/v1.6.9-rc2.zip | |
| unzip /opt/v1.6.9-rc2.zip -d /opt | |
| cp -r /opt/crowdsec-1.6.9-rc2/config/patterns/* /etc/crowdsec/patterns/ | |
| rm -rf /opt/crowdsec-1.6.9-rc2 /opt/v1.6.9-rc2.zip | |
| ``` | |
| Everything should be working fine now so restart the pangolin-stack. Check by looking at the logs docker logs crowdsec | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment