From 45c42cf41d9d753a325f7d277c9539074a41ef58 Mon Sep 17 00:00:00 2001 From: abtreece Date: Sun, 25 Jan 2026 22:05:12 -0600 Subject: [PATCH 1/3] feat(packaging): add RPM and DEB package support via nFPM Add Linux package generation to the release process using goreleaser's nFPM integration. Packages include: - Systemd service with security hardening (CapabilityBoundingSet, ProtectSystem, NoNewPrivileges, etc.) - Environment file configuration (/etc/default/confd for Debian, /etc/sysconfig/confd for RHEL) - Default confd.toml with documented options - Post-install script with setup instructions - Pre-remove script for clean service shutdown Package architectures: amd64, arm64, armv7 Package formats: .deb (Debian/Ubuntu), .rpm (RHEL/Fedora/CentOS) Packages will be automatically attached to GitHub Releases. --- .goreleaser.yml | 76 ++++++++++++++++++++++++++++++++ packaging/confd.default | 48 ++++++++++++++++++++ packaging/confd.service | 50 +++++++++++++++++++++ packaging/confd.toml.default | 68 ++++++++++++++++++++++++++++ packaging/scripts/postinstall.sh | 32 ++++++++++++++ packaging/scripts/preremove.sh | 14 ++++++ 6 files changed, 288 insertions(+) create mode 100644 packaging/confd.default create mode 100644 packaging/confd.service create mode 100644 packaging/confd.toml.default create mode 100644 packaging/scripts/postinstall.sh create mode 100644 packaging/scripts/preremove.sh diff --git a/.goreleaser.yml b/.goreleaser.yml index 142936fb9..22af2c9ab 100644 --- a/.goreleaser.yml +++ b/.goreleaser.yml @@ -31,6 +31,82 @@ archives: formats: [zip] files: - none* + +nfpms: + - id: confd + package_name: confd + vendor: abtreece + homepage: https://github.com/abtreece/confd + maintainer: Andrew Treece + description: Lightweight configuration management tool + license: MIT + formats: + - deb + - rpm + bindir: /usr/bin + + # Package dependencies + dependencies: + - systemd + + contents: + # Systemd service file + - src: ./packaging/confd.service + dst: /usr/lib/systemd/system/confd.service + file_info: + mode: 0644 + + # Default config file (won't overwrite on upgrade) + - src: ./packaging/confd.toml.default + dst: /etc/confd/confd.toml + type: config|noreplace + file_info: + mode: 0644 + + # Environment file for Debian/Ubuntu + - src: ./packaging/confd.default + dst: /etc/default/confd + type: config|noreplace + file_info: + mode: 0644 + packager: deb + + # Environment file for RHEL/Fedora + - src: ./packaging/confd.default + dst: /etc/sysconfig/confd + type: config|noreplace + file_info: + mode: 0644 + packager: rpm + + # Create config directories + - dst: /etc/confd/conf.d + type: dir + file_info: + mode: 0755 + + - dst: /etc/confd/templates + type: dir + file_info: + mode: 0755 + + - dst: /var/lib/confd + type: dir + file_info: + mode: 0755 + + scripts: + postinstall: ./packaging/scripts/postinstall.sh + preremove: ./packaging/scripts/preremove.sh + + rpm: + group: System Environment/Daemons + compression: gzip + + deb: + lintian_overrides: + - statically-linked-binary + checksum: name_template: 'checksums.txt' algorithm: sha256 diff --git a/packaging/confd.default b/packaging/confd.default new file mode 100644 index 000000000..22dd21f83 --- /dev/null +++ b/packaging/confd.default @@ -0,0 +1,48 @@ +# confd environment configuration +# This file is sourced by the confd systemd service. +# +# See https://github.com/abtreece/confd for documentation. + +# Backend to use (required) +# Options: etcd, consul, vault, redis, zookeeper, dynamodb, ssm, acm, +# secretsmanager, env, file, imds +CONFD_BACKEND="etcd" + +# Command-line options passed to confd +# Common options: +# --watch Enable watch mode (real-time updates) +# --interval N Polling interval in seconds (default: 600) +# --onetime Run once and exit +# --node URL Backend node address (can specify multiple times) +# --prefix PATH Key prefix +# --log-level LEVEL Log level: debug, info, warn, error +# --config-file PATH Path to confd.toml config file +# --confdir PATH Path to conf.d directory +# --systemd-notify Enable systemd sd_notify support +# --watchdog-interval D Systemd watchdog ping interval (e.g., 30s) +# +# Backend-specific options vary. Run 'confd --help' for details. + +CONFD_OPTS="--watch --systemd-notify --watchdog-interval 30s --log-level info" + +# Examples: +# +# etcd with watch mode: +# CONFD_BACKEND="etcd" +# CONFD_OPTS="--watch --node http://127.0.0.1:2379 --systemd-notify" +# +# Consul with watch mode: +# CONFD_BACKEND="consul" +# CONFD_OPTS="--watch --node 127.0.0.1:8500 --systemd-notify" +# +# Vault with polling: +# CONFD_BACKEND="vault" +# CONFD_OPTS="--interval 60 --node https://vault.example.com:8200 --auth-type approle" +# +# File backend with watch mode: +# CONFD_BACKEND="file" +# CONFD_OPTS="--watch --file /etc/myapp/config.yaml --systemd-notify" +# +# AWS SSM Parameter Store: +# CONFD_BACKEND="ssm" +# CONFD_OPTS="--interval 300 --prefix /myapp/production" diff --git a/packaging/confd.service b/packaging/confd.service new file mode 100644 index 000000000..75b089bcd --- /dev/null +++ b/packaging/confd.service @@ -0,0 +1,50 @@ +[Unit] +Description=confd configuration management +Documentation=https://github.com/abtreece/confd +After=network-online.target +Wants=network-online.target + +[Service] +Type=notify +EnvironmentFile=-/etc/default/confd +EnvironmentFile=-/etc/sysconfig/confd +ExecStart=/usr/bin/confd $CONFD_BACKEND $CONFD_OPTS +ExecReload=/bin/kill -HUP $MAINPID + +# Restart behavior +Restart=on-failure +RestartSec=5s +WatchdogSec=60s + +# Graceful shutdown +KillMode=mixed +KillSignal=SIGTERM +TimeoutStopSec=30 + +# Security hardening - run as root but with restrictions +CapabilityBoundingSet=CAP_DAC_OVERRIDE CAP_KILL CAP_CHOWN CAP_FOWNER CAP_DAC_READ_SEARCH +NoNewPrivileges=true +ProtectSystem=strict +ProtectHome=true +PrivateTmp=true +PrivateDevices=true +ProtectKernelTunables=true +ProtectKernelModules=true +ProtectControlGroups=true +RestrictSUIDSGID=true +RestrictRealtime=true +LockPersonality=true +MemoryDenyWriteExecute=true + +# Allow writes to config directories and common application paths +ReadWritePaths=/etc /var/lib/confd /var/run + +# Restrict system calls +SystemCallFilter=@system-service +SystemCallArchitectures=native + +# Resource limits +LimitNOFILE=65536 + +[Install] +WantedBy=multi-user.target diff --git a/packaging/confd.toml.default b/packaging/confd.toml.default new file mode 100644 index 000000000..fca15dfd1 --- /dev/null +++ b/packaging/confd.toml.default @@ -0,0 +1,68 @@ +# confd configuration file +# See https://github.com/abtreece/confd/blob/main/docs/configuration-guide.md +# +# This file provides default values. Command-line flags and environment +# variables (CONFD_*) override these settings. +# +# Note: The backend and connection settings are typically specified via +# command-line in /etc/default/confd (or /etc/sysconfig/confd on RHEL). + +# Configuration directories +# confdir = "/etc/confd" + +# Global key prefix applied to all template resources +# prefix = "" + +# Polling interval in seconds (used when watch mode is disabled) +# interval = 600 + +# Error handling mode: +# "best-effort" - Continue processing remaining templates when one fails +# "fail-fast" - Stop all processing on first template error +# failure_mode = "best-effort" + +# Logging +# log-level = "info" +# log-format = "text" + +# Backend connection (typically set via CLI in /etc/default/confd) +# nodes = ["http://127.0.0.1:2379"] +# scheme = "http" + +# Authentication (if required by backend) +# basic_auth = false +# username = "" +# password = "" +# auth_token = "" + +# TLS configuration +# client_cert = "" +# client_key = "" +# client_cakeys = "" +# client_insecure = false + +# Timeouts +# dial_timeout = "5s" +# read_timeout = "1s" +# write_timeout = "1s" +# backend_timeout = "30s" +# check_cmd_timeout = "30s" +# reload_cmd_timeout = "60s" + +# Retry configuration +# retry_max_attempts = 3 +# retry_base_delay = "100ms" +# retry_max_delay = "5s" + +# Watch mode settings +# watch_error_backoff = "2s" +# debounce = "" +# batch_interval = "" + +# Performance +# template_cache = true +# stat_cache_ttl = "1s" + +# Metrics endpoint (disabled if empty) +# Exposes /metrics, /health, /ready, /ready/detailed +# metrics_addr = ":9100" diff --git a/packaging/scripts/postinstall.sh b/packaging/scripts/postinstall.sh new file mode 100644 index 000000000..78aa2a052 --- /dev/null +++ b/packaging/scripts/postinstall.sh @@ -0,0 +1,32 @@ +#!/bin/bash +set -e + +# Create state directory +if [ ! -d /var/lib/confd ]; then + mkdir -p /var/lib/confd + chmod 755 /var/lib/confd +fi + +# Reload systemd to pick up new service file +if command -v systemctl >/dev/null 2>&1; then + systemctl daemon-reload +fi + +echo "" +echo "confd has been installed." +echo "" +echo "To configure confd:" +echo " 1. Edit /etc/default/confd (or /etc/sysconfig/confd on RHEL)" +echo " - Set CONFD_BACKEND to your backend (etcd, consul, vault, etc.)" +echo " - Set CONFD_OPTS with connection and runtime options" +echo "" +echo " 2. Create template resources in /etc/confd/conf.d/" +echo " See: https://github.com/abtreece/confd/blob/main/docs/template-resources.md" +echo "" +echo " 3. Create templates in /etc/confd/templates/" +echo " See: https://github.com/abtreece/confd/blob/main/docs/templates.md" +echo "" +echo " 4. Start the service:" +echo " systemctl enable confd" +echo " systemctl start confd" +echo "" diff --git a/packaging/scripts/preremove.sh b/packaging/scripts/preremove.sh new file mode 100644 index 000000000..33b98acd5 --- /dev/null +++ b/packaging/scripts/preremove.sh @@ -0,0 +1,14 @@ +#!/bin/bash +set -e + +# Stop and disable the service before removal +if command -v systemctl >/dev/null 2>&1; then + if systemctl is-active --quiet confd 2>/dev/null; then + echo "Stopping confd service..." + systemctl stop confd + fi + if systemctl is-enabled --quiet confd 2>/dev/null; then + echo "Disabling confd service..." + systemctl disable confd + fi +fi From c284c7d56145807440d0b2220a161082f7769ea2 Mon Sep 17 00:00:00 2001 From: abtreece Date: Sun, 25 Jan 2026 22:09:34 -0600 Subject: [PATCH 2/3] docs: add package installation instructions and address review feedback - Add Linux package installation section to installation.md - Update service-deployment.md with package-based setup instructions - Add comment in confd.service explaining why /etc write access is needed - Improve documentation links in packaging files - Simplify postinstall.sh output with single docs link --- docs/installation.md | 73 +++++++++++++++++++++++++++++++- docs/service-deployment.md | 30 +++++++++++++ packaging/confd.default | 3 +- packaging/confd.service | 4 ++ packaging/confd.toml.default | 3 +- packaging/scripts/postinstall.sh | 4 +- 6 files changed, 112 insertions(+), 5 deletions(-) diff --git a/docs/installation.md b/docs/installation.md index 9b8306f28..de09ba857 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -1,6 +1,77 @@ # Installation -### Binary Download +## Linux Packages (Recommended) + +confd provides native packages for Debian/Ubuntu (.deb) and RHEL/Fedora/CentOS (.rpm). These packages include systemd integration with security hardening. + +### Debian / Ubuntu + +```bash +# Download the latest release (replace VERSION and ARCH as needed) +VERSION=0.34.0 +ARCH=amd64 # or arm64 + +curl -LO "https://github.com/abtreece/confd/releases/download/v${VERSION}/confd_${VERSION}_linux_${ARCH}.deb" +sudo dpkg -i "confd_${VERSION}_linux_${ARCH}.deb" +``` + +### RHEL / Fedora / CentOS + +```bash +# Download the latest release (replace VERSION and ARCH as needed) +VERSION=0.34.0 +ARCH=x86_64 # or aarch64 + +curl -LO "https://github.com/abtreece/confd/releases/download/v${VERSION}/confd-${VERSION}-1.${ARCH}.rpm" +sudo rpm -i "confd-${VERSION}-1.${ARCH}.rpm" +``` + +### Package Contents + +The packages install: + +| Path | Description | +|------|-------------| +| `/usr/bin/confd` | Binary | +| `/usr/lib/systemd/system/confd.service` | Systemd service with security hardening | +| `/etc/confd/confd.toml` | Default configuration file | +| `/etc/confd/conf.d/` | Template resource directory | +| `/etc/confd/templates/` | Template directory | +| `/etc/default/confd` | Environment file (Debian) | +| `/etc/sysconfig/confd` | Environment file (RHEL) | +| `/var/lib/confd/` | State directory | + +### Post-Installation Setup + +1. Configure the backend and options in the environment file: + + ```bash + # Debian/Ubuntu + sudo vi /etc/default/confd + + # RHEL/Fedora + sudo vi /etc/sysconfig/confd + ``` + + Example configuration: + ```bash + CONFD_BACKEND="etcd" + CONFD_OPTS="--watch --systemd-notify --watchdog-interval 30s --log-level info" + ``` + +2. Create template resources and templates in `/etc/confd/conf.d/` and `/etc/confd/templates/` + +3. Enable and start the service: + ```bash + sudo systemctl enable confd + sudo systemctl start confd + ``` + +See [Service Deployment Guide](service-deployment.md) for advanced systemd configuration. + +--- + +## Binary Download confd ships binaries for OS X, Linux, and Windows for both amd64 and arm64 architectures. You can download the latest release from [GitHub](https://github.com/abtreece/confd/releases). diff --git a/docs/service-deployment.md b/docs/service-deployment.md index 0313bec9e..d011b8f6c 100644 --- a/docs/service-deployment.md +++ b/docs/service-deployment.md @@ -15,6 +15,36 @@ This guide covers deploying confd as a production service with systemd, Docker, confd supports systemd's `sd_notify` protocol for improved service management. +### Package-Based Installation (Recommended) + +If you installed confd via RPM or DEB packages, systemd integration is pre-configured. Configure confd using the environment file: + +```bash +# Debian/Ubuntu +sudo vi /etc/default/confd + +# RHEL/Fedora/CentOS +sudo vi /etc/sysconfig/confd +``` + +Example configuration: +```bash +CONFD_BACKEND="etcd" +CONFD_OPTS="--watch --systemd-notify --watchdog-interval 30s --node http://etcd.example.com:2379" +``` + +Then enable and start: +```bash +sudo systemctl enable confd +sudo systemctl start confd +``` + +The packaged service includes security hardening (see [Security Hardening](#security-hardening) below). + +### Manual Installation + +If you installed confd via binary download, create a systemd service file manually. + ### Type=notify (Recommended) The `Type=notify` service provides better reliability and monitoring: diff --git a/packaging/confd.default b/packaging/confd.default index 22dd21f83..a249e4000 100644 --- a/packaging/confd.default +++ b/packaging/confd.default @@ -1,7 +1,8 @@ # confd environment configuration # This file is sourced by the confd systemd service. # -# See https://github.com/abtreece/confd for documentation. +# Documentation: https://github.com/abtreece/confd/blob/main/docs/ +# Quick start: https://github.com/abtreece/confd/blob/main/docs/quick-start-guide.md # Backend to use (required) # Options: etcd, consul, vault, redis, zookeeper, dynamodb, ssm, acm, diff --git a/packaging/confd.service b/packaging/confd.service index 75b089bcd..7d3b30f79 100644 --- a/packaging/confd.service +++ b/packaging/confd.service @@ -37,6 +37,10 @@ LockPersonality=true MemoryDenyWriteExecute=true # Allow writes to config directories and common application paths +# Note: /etc is required because confd writes to arbitrary config locations +# (e.g., /etc/nginx/nginx.conf, /etc/myapp/config.yaml). To restrict further, +# replace /etc with specific paths your templates write to: +# ReadWritePaths=/etc/nginx /etc/myapp /etc/confd /var/lib/confd /var/run ReadWritePaths=/etc /var/lib/confd /var/run # Restrict system calls diff --git a/packaging/confd.toml.default b/packaging/confd.toml.default index fca15dfd1..bd11132a2 100644 --- a/packaging/confd.toml.default +++ b/packaging/confd.toml.default @@ -1,5 +1,6 @@ # confd configuration file -# See https://github.com/abtreece/confd/blob/main/docs/configuration-guide.md +# Documentation: https://github.com/abtreece/confd/blob/main/docs/configuration-guide.md +# CLI reference: https://github.com/abtreece/confd/blob/main/docs/command-line-flags.md # # This file provides default values. Command-line flags and environment # variables (CONFD_*) override these settings. diff --git a/packaging/scripts/postinstall.sh b/packaging/scripts/postinstall.sh index 78aa2a052..1da03602f 100644 --- a/packaging/scripts/postinstall.sh +++ b/packaging/scripts/postinstall.sh @@ -21,10 +21,10 @@ echo " - Set CONFD_BACKEND to your backend (etcd, consul, vault, etc.)" echo " - Set CONFD_OPTS with connection and runtime options" echo "" echo " 2. Create template resources in /etc/confd/conf.d/" -echo " See: https://github.com/abtreece/confd/blob/main/docs/template-resources.md" echo "" echo " 3. Create templates in /etc/confd/templates/" -echo " See: https://github.com/abtreece/confd/blob/main/docs/templates.md" +echo "" +echo " Documentation: https://github.com/abtreece/confd#documentation" echo "" echo " 4. Start the service:" echo " systemctl enable confd" From f9897ccad2ca332d3c974e646b48e214a85ea197 Mon Sep 17 00:00:00 2001 From: abtreece Date: Sun, 25 Jan 2026 22:15:45 -0600 Subject: [PATCH 3/3] fix(packaging): handle package upgrades correctly in lifecycle scripts - preremove.sh: Only disable service on true removal, not upgrades - DEB receives "remove" or "upgrade" as $1 - RPM receives 0 (uninstall) or 1 (upgrade) as $1 - postinstall.sh: Restart service on upgrade if it was enabled - Only show setup instructions on fresh install - Automatically restart service after upgrade completes This prevents the service from being left disabled after package upgrades. --- packaging/scripts/postinstall.sh | 74 ++++++++++++++++++++++++-------- packaging/scripts/preremove.sh | 29 +++++++++++-- 2 files changed, 80 insertions(+), 23 deletions(-) diff --git a/packaging/scripts/postinstall.sh b/packaging/scripts/postinstall.sh index 1da03602f..92ddbaedd 100644 --- a/packaging/scripts/postinstall.sh +++ b/packaging/scripts/postinstall.sh @@ -1,32 +1,68 @@ #!/bin/bash set -e +# This script runs after package installation or upgrade. +# Arguments: +# DEB: $1 = "configure" (fresh install or upgrade) +# RPM: $1 = 1 (fresh install) or 2+ (upgrade) + # Create state directory if [ ! -d /var/lib/confd ]; then mkdir -p /var/lib/confd chmod 755 /var/lib/confd fi -# Reload systemd to pick up new service file +# Reload systemd to pick up new/updated service file if command -v systemctl >/dev/null 2>&1; then systemctl daemon-reload + + # On upgrade, restart the service if it was enabled + # (preremove stopped it, but didn't disable it on upgrade) + if systemctl is-enabled --quiet confd 2>/dev/null; then + echo "Restarting confd service..." + systemctl restart confd + fi fi -echo "" -echo "confd has been installed." -echo "" -echo "To configure confd:" -echo " 1. Edit /etc/default/confd (or /etc/sysconfig/confd on RHEL)" -echo " - Set CONFD_BACKEND to your backend (etcd, consul, vault, etc.)" -echo " - Set CONFD_OPTS with connection and runtime options" -echo "" -echo " 2. Create template resources in /etc/confd/conf.d/" -echo "" -echo " 3. Create templates in /etc/confd/templates/" -echo "" -echo " Documentation: https://github.com/abtreece/confd#documentation" -echo "" -echo " 4. Start the service:" -echo " systemctl enable confd" -echo " systemctl start confd" -echo "" +# Only show setup instructions on fresh install +# DEB: $1 is "configure" for both install and upgrade, but we can check if service exists +# RPM: $1 = 1 for fresh install, 2+ for upgrade +is_fresh_install() { + case "$1" in + 1) + # RPM fresh install + return 0 + ;; + configure) + # DEB - check if service was already enabled (indicates upgrade) + if systemctl is-enabled --quiet confd 2>/dev/null; then + return 1 # upgrade + fi + return 0 # fresh install + ;; + *) + return 1 + ;; + esac +} + +if is_fresh_install "$1"; then + echo "" + echo "confd has been installed." + echo "" + echo "To configure confd:" + echo " 1. Edit /etc/default/confd (or /etc/sysconfig/confd on RHEL)" + echo " - Set CONFD_BACKEND to your backend (etcd, consul, vault, etc.)" + echo " - Set CONFD_OPTS with connection and runtime options" + echo "" + echo " 2. Create template resources in /etc/confd/conf.d/" + echo "" + echo " 3. Create templates in /etc/confd/templates/" + echo "" + echo " Documentation: https://github.com/abtreece/confd#documentation" + echo "" + echo " 4. Start the service:" + echo " systemctl enable confd" + echo " systemctl start confd" + echo "" +fi diff --git a/packaging/scripts/preremove.sh b/packaging/scripts/preremove.sh index 33b98acd5..5ccaedfbf 100644 --- a/packaging/scripts/preremove.sh +++ b/packaging/scripts/preremove.sh @@ -1,14 +1,35 @@ #!/bin/bash set -e -# Stop and disable the service before removal +# This script runs before package removal or upgrade. +# Arguments: +# DEB: $1 = "remove" (uninstall) or "upgrade" +# RPM: $1 = 0 (uninstall) or 1 (upgrade) + +# Determine if this is a removal or upgrade +is_removal() { + case "$1" in + 0|remove|purge) + return 0 + ;; + *) + return 1 + ;; + esac +} + if command -v systemctl >/dev/null 2>&1; then + # Always stop the service (will be restarted by postinstall on upgrade) if systemctl is-active --quiet confd 2>/dev/null; then echo "Stopping confd service..." systemctl stop confd fi - if systemctl is-enabled --quiet confd 2>/dev/null; then - echo "Disabling confd service..." - systemctl disable confd + + # Only disable on true removal, not on upgrade + if is_removal "$1"; then + if systemctl is-enabled --quiet confd 2>/dev/null; then + echo "Disabling confd service..." + systemctl disable confd + fi fi fi