enCompass + enCapsule + enCryptor + puppet-enc

preface

I started coding this project alone, but I crossed paths with Copilot and we decided to join forces and work on it together.
It has been a great help for coding javascript, jquery, as well as testing, debugging, and documentation.

This project is mirrored on Codeberg and Github, and the artifacts are pushed to Codeberg/GEANT/packages.
If Open Source is your thing, please consider starring and contributing on Codeberg: codeberg/GEANT/docker-encompass.

If you are searching for an OpenVox / Puppet ENC implementation, this repository provides an
OpenVox / Puppet External Node Classifier with Docker deployment, host/group classification, and ENC
API endpoints.

enCompass is a Django-based OpenVox / Puppet External Node Classifier (ENC) packaged for Docker.
It provides a web UI to manage hosts and groups, plus read-only ENC endpoints exposed by both enCompass
and enCapsule.

enCapsule is an optional agent for enCompass that can be used to provide high availability for the ENC API.
It does not depend on a database and boots up in just 1 second, making it ideal for an autoscaling setup.

This repository also includes optional enCryptor and deCryptor components that enable certificate
auto-signing flows through CSR challengePassword generation and validation.

An optional puppet-enc Go binary is also provided as a faster, dependency-free drop-in replacement for
the puppet-enc.sh shell script used to integrate Puppet Server with the ENC API.

Demo site: encompass-demo.geant.org
Repository URL: codeberg.org/GEANT/docker-encompass
site.pp vs enCompass ENC: codeberg.org/GEANT/docker-encompass/docs/SITEPP_VS_ENC.md

enCompass screenshot

Index

Development Guide

Features

Application Flow

Architecture diagram

Deployment

Nomad Deployment

You can use encompass.nomad and encapsule.nomad and adjust them to your needs.

The job contains:

Kubernetes Deployment

Help needed!

Docker Compose

Quick Start (Docker)

The following instructions are not intended for a production grade deployment.

  1. Copy environment variables file:

    bash cp vars.example vars

  2. Review and update vars for your environment (LDAP host, secrets, allowed hosts, SSL paths, etc.).

  3. Build and start:

    bash docker compose up --build

  4. Open UI:

    • http://localhost:8080/encompass/

Endpoints

Additional enCompass CSR endpoints (usable for external provisioning):

Response example:

---
custom_attributes:
  challengePassword: secure_password

CSR endpoint authentication:

Example:

curl -s -H "X-CSR-API-KEY: $CSR_API_KEY" \
  http://enc.example.org:8081/hosts/node1.example.org/csr_attributes

enCryptor + deCryptor (Optional)

enCryptor is an optional helper component for provisioning workflows that retrieve CSR challengePassword values from enCompass/enCapsule and write the expected YAML blob for autosign use cases.

deCryptor is an optional Puppet autosign policy helper that validates incoming CSR challengePassword values against enCompass/enCapsule.

Component names are enCryptor and deCryptor; executable names are encryptor and decryptor.

Full documentation:

puppet-enc (Optional)

puppet-enc is a compiled Go binary that calls the ENC API and serves as a drop-in replacement for puppet-enc.sh. On busy Puppet Servers it is roughly 2–3× faster and uses 3–4× less CPU per invocation compared to the shell script.

Full documentation:

Puppet ENC Keywords

This project is relevant to searches and documentation around:

Puppet ENC Integration

In principle you can simply use curl against the ENC endpoint as follows:

curl -s http://enc.example.org:8081/hosts/\$1

For production use on a busy Puppet Server, the recommended approach is the puppet-enc Go binary (see puppet-enc (Optional) and docs/PUPPET-ENC.md). If deploying a compiled binary is not practical, the shell script puppet-enc.sh is available as a fallback.

sudo install -m 0755 puppet-enc /etc/puppetlabs/puppet/enc/puppet-enc

No external dependencies required.

Create a wrapper so Puppet can pass the node certname ($1):

sudo tee /etc/puppetlabs/puppet/enc/enc-wrapper.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
exec /etc/puppetlabs/puppet/enc/puppet-enc \
  --node "$1" \
  --server encompass.example.org \
  --srv
EOF
sudo chmod 0755 /etc/puppetlabs/puppet/enc/enc-wrapper.sh

Using puppet-enc.sh (fallback)

sudo install -m 0755 examples/puppet-enc.sh /etc/puppetlabs/puppet/enc/puppet-enc.sh

Required tools on Puppet Server: bash, curl, dig, getopt

sudo tee /etc/puppetlabs/puppet/enc/enc-wrapper.sh >/dev/null <<'EOF'
#!/usr/bin/env bash
exec /etc/puppetlabs/puppet/enc/puppet-enc.sh \
  --node "$1" \
  --server encompass.example.org \
  --srv
EOF
sudo chmod 0755 /etc/puppetlabs/puppet/enc/enc-wrapper.sh

Help:

Usage: puppet-enc --node <node> --server <hostname> [--srv | --rrdns --port <port> | --port <port>] [--user <username> --password <password>]
       puppet-enc -h | --help

  -n, --node      Node to query
  -s, --server    Server hostname/IP to connect
  -u, --user      Username (jointly required with --password)
  -p, --password  Password (jointly required with --user)
      --srv       Resolve endpoint via SRV record _puppet8._tcp.<server>
      --rrdns     Resolve <server> to multiple A/AAAA records and try each with --port
      --port      Static port (required for non-SRV mode)

Configure Puppet Server

In /etc/puppetlabs/puppet/puppet.conf:

[server]
node_terminus = exec
external_nodes = /etc/puppetlabs/puppet/enc/enc-wrapper.sh

Apply and verify:

sudo systemctl restart puppetserver
sudo puppet config print node_terminus external_nodes --section master

The ENC command must return valid YAML for the requested node and exit with code 0.

Migration Notes

If you are migrating from site.pp-based classification to ENC, you can start safely with a minimal ENC setup:

About lookup/precedence:

Operational guidance:

Configuration

Main runtime configuration is in vars (copied from vars.example).

Core settings

MySQL endpoint strategy

MYSQL_NODES is the single configuration knob for the database endpoint:

Single-node mode

MYSQL_NODES=mysql.example.org
# or with explicit port:
MYSQL_NODES=192.168.0.1:3306

Multi-node Galera mode

MYSQL_NODES=10.0.0.10:3306,10.0.0.11:3306,10.0.0.12:3306

Health-check options (multi-node only, mutually exclusive)

HTTP check against a monitoring port (e.g. Galera clustercheck on 9200):

MYSQL_NODES=10.0.0.10:3306,10.0.0.11:3306,10.0.0.12:3306
MYSQL_MONITORING_PORT=9200

MySQL-protocol check (requires a haproxy user on the DB with no password):

MYSQL_NODES=10.0.0.10:3306,10.0.0.11:3306,10.0.0.12:3306
MYSQL_HAPROXY_CHECK_USER=haproxy
-- on each Galera node:
CREATE USER 'haproxy'@'%';

If neither option is set, plain TCP connection checks are used.

CSR challengePassword store

python manage.py rotate_csr_challenge --host node1.example.org --old-token "$OLD_TOKEN" --new-token "$NEW_TOKEN"
python manage.py rotate_csr_challenge --group default --old-token "$OLD_TOKEN" --new-token "$NEW_TOKEN"
python manage.py rotate_csr_challenge --all --old-token "$OLD_TOKEN" --new-token "$NEW_TOKEN"

Logging

Puppet environments

UI behavior:

Feature Branch can be enabled/disabled by the local Admin in the Global Settings.

Host selectors in groups.yaml (hosts list):

Resolution order:

Validation guardrails:

PuppetDB unclassified hosts

The UI page /encompass/unclassified_hosts/ lists nodes considered unclassified.

A node is marked unclassified when its resolved ENC payload (environment, classes, parameters) matches the ENC default group profile.

Configuration:

The nodes endpoint is built internally as:

Authentication

Authentication behavior:

LDAP connection/search settings are managed in Global Settings.

Access control note:

LDAP group mirroring behavior:

Local Django auth mode bootstraps two local users on first start:

Change these initial passwords immediately from the User Settings page.

ENC Viewer Basic Auth

Set ENC_VIEWER_PASSWORD to protect read-only ENC endpoints with basic auth.

Leave empty to disable endpoint basic auth.

SSL

Enable HTTPS listeners by setting:

Git settings

The application accepts the Git SSH private key in either of these forms:

Set only one of the two variables above (they are mutually exclusive).

Branch behavior:

Typical setup:

HA considerations

Once configured on the Vox/Puppet server, ENC becomes essential for its operation and must be highly resilient.

enCompass is stateless and supports autoscaling. It can be set up to run at least two instances for High Availability and inherently load balancing.

The database is only crucial for the UI’s operation but is irrelevant for the ENC endpoint.

enCapsule Agent Runtime

The repository now includes an agent runtime named enCapsule.

Run enCapsule with Docker Compose

A full description of enCapsule and its sync flow with enCompass is available in the enCapsule documentation.

docker compose --profile encapsule up --build encapsule

Default exposed port in compose profile:

Optional Git sync trigger

You can configure a token and trigger a pull/update of ENC data:

Example:

curl -X POST \
  -H "X-Encapsule-Token: ${ENCAPSULE_SYNC_TOKEN}" \
  http://localhost:9081/sync

Fan-out sync to multiple enCapsule agents

Use /usr/local/bin/encapsule-sync.sh from the enCompass runtime after a successful Git push.

When host/group data is changed from enCompass, the application automatically:

  1. commits changed ENC YAML files (if any),
  2. pushes to the configured Git branch,
  3. triggers enCapsule sync fan-out.

Git sync execution mode is configurable:

Reliability and latency controls:

Common variables:

Accepted ENCAPSULE_SYNC_HOST entries:

Example:

ENCAPSULE_SYNC_HOST="encapsule-a.internal,encapsule-b.internal"
ENCAPSULE_SYNC_TOKEN="<shared-token>"

Example:

ENCAPSULE_SYNC_HOST="_encapsule-sync._tcp.enc.example.org"
ENCAPSULE_SYNC_TOKEN="<shared-token>"

Run manually:

/usr/local/bin/encapsule-sync.sh

The script sends requests in parallel and fails if any target fails. If ENCAPSULE_SYNC_HOST is empty, the script exits successfully without sending requests.

In Nomad, SRV entries are typically easiest. In non-SRV environments, use explicit hostnames/IPs.

Data Backup

Host/group YAML data is stored in the configured Git repository.

When using LDAP authentication, the data stored in the database is not critical. It can be rebuilt from scratch and only session cookies will be lost.

When the authentication backend is MySQL, the database stores user information. It’s recommended to back up your MySQL database when Database authentication is used.

Security Checklist

Doubts and open questions

ToDo

License

This project is licensed under the GNU General Public License v3.0 or later (GPL-3.0-or-later). See LICENSE for details.

SPDX-License-Identifier: GPL-3.0-or-later