From c6e0760646c360fc76bb749a512d83e4f85e711f Mon Sep 17 00:00:00 2001 From: Kapil Agrawal <7047165+netops2devops@users.noreply.github.com> Date: Tue, 11 Aug 2026 21:22:32 -0500 Subject: [PATCH 1/2] Adds basic contribution guidelines, developer docs to get started Signed-off-by: Kapil Agrawal <7047165+netops2devops@users.noreply.github.com> --- contrib/contribute.md | 22 +++++++++++++ contrib/develop/externalcas.md | 57 ++++++++++++++++++++++++++++++++++ contrib/develop/maintenance.md | 28 +++++++++++++++++ contrib/develop/test-infra.md | 28 +++++++++++++++++ contrib/scripts/uninstall.sh | 50 +++++++++++++++++++++++++++++ 5 files changed, 185 insertions(+) create mode 100644 contrib/contribute.md create mode 100644 contrib/develop/externalcas.md create mode 100644 contrib/develop/maintenance.md create mode 100644 contrib/develop/test-infra.md create mode 100644 contrib/scripts/uninstall.sh diff --git a/contrib/contribute.md b/contrib/contribute.md new file mode 100644 index 0000000..6341eef --- /dev/null +++ b/contrib/contribute.md @@ -0,0 +1,22 @@ +# Contribution Guidelines + +We welcome all contributions but we ask you to do the following _before_ submitting a pull request: + +- If it’s a **new feature request**, please open an issue with details about your proposed feature, what you want changed and why. What use cases would this feature solve and how will it benefit the community? + +- For any **bug fixes**, please check if there are already open or closed issues about the topic and verify that you are testing with the latest version of acme-proxy. + +- If you are **updating docs, improving tests**, please proceed directly to MR. + +1. Fork the repo +2. In your fork, create a new branch for your work +3. Add code/fix in this branch, write tests, update the docs as necessary +4. Commit & push changes to your forked repo +5. Submit a pull request targeting our main branch + +## Setup Development Environment + +1. Install `go >= 1.25` +2. Install [pre-commit](https://pre-commit.com) +3. Clone the repo using `git clone --recurse-submodules git@github.com:esnet/acme-proxy.git` +4. Run `make dev` diff --git a/contrib/develop/externalcas.md b/contrib/develop/externalcas.md new file mode 100644 index 0000000..6167120 --- /dev/null +++ b/contrib/develop/externalcas.md @@ -0,0 +1,57 @@ +# ACME server as Registration Authority + +See [Breadcrumbs](#Breadcrumbs) for more background on what registration authority, CAS are and how those concepts fits into step-ca architecture. + +## Certificate Authority Service (CAS) + +CAS provides a plugin based architecture that allows Step CA to delegate certificate signing to different backends - whether that's Google Cloud, HashiCorp Vault, or in our case, external certificate authorities like Sectigo or ZeroSSL. ACME proxy can be run as a standalone ACME server in Registraiton Authority mode + +### ExternalCAS + +`Step CA` provides an interface called `CertificateAuthorityService` [CAS](https://github.com/smallstep/certificates/tree/master/cas) to support external certificate authorities as the signing body. Our code in `externalcas` simply implements the `CertificateAuthorityService` interface. + +```go +type CertificateAuthorityService interface { + CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) + RenewCertificate(req *RenewCertificateRequest) (*RenewCertificateResponse, error) + RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error) +} +``` + +The go package also defines a special type called `ExternalCAS` for this exact purpose. Which is why our [ca.json](../../ca.json) file defines an authority of `type: externalcas`. + +```go +const ( + // DefaultCAS is a CertificateAuthorityService using software. + DefaultCAS = "" + // SoftCAS is a CertificateAuthorityService using software. + SoftCAS = "softcas" + // CloudCAS is a CertificateAuthorityService using Google Cloud CAS. + CloudCAS = "cloudcas" + // StepCAS is a CertificateAuthorityService using another step-ca instance. + StepCAS = "stepcas" + // VaultCAS is a CertificateAuthorityService using Hasicorp Vault PKI. + VaultCAS = "vaultcas" + // ExternalCAS is a CertificateAuthorityService using an external injected CA implementation + ExternalCAS = "externalcas" +) +``` + +## Breadcrumbs + +**step-ca github repo** + + +**Step CA Registration Authority (RA) mode** + + +**RA related github discussions** + +- +- + +**Step CA full configuration options** + + +**Certificate issuance policy configuration** + diff --git a/contrib/develop/maintenance.md b/contrib/develop/maintenance.md new file mode 100644 index 0000000..55d21ad --- /dev/null +++ b/contrib/develop/maintenance.md @@ -0,0 +1,28 @@ +# Guide to patching upstream related bugs + +acme-proxy uses smallstep/certificates as an upstream dependency but just like any other software `smallstep/certifiates` can have bugs too. We have identified a couple of issues which imapct us and have even submitted merge requests but are waiting action from upstream maintainers. + +Until then we will have to maintain some patches/fixes ourselves until they get merged upstream. Our patched version of step-ca is currently maintained in a forked repo [esnet/certificates](https://github.com/esnet/certificates). + +- The branch naming scheme for our patches follow a pattern `patch/upstream-version`. For example: patches made against smallstep/certificates `v0.30.2` are in a branch called `patch/v0.30.2`. + +- Once the patches have been applied and tested, we tag the commit using a naming scheme `[upstream version]-patch.count`. So if the patches have been applied against upstream `v0.30.2` our go.mod in acme-proxy should contain the following directive + +``` +replace github.com/smallstep/certificates => github.com/esnet/certificates v0.30.2-patch.2 +``` + +Where the trailing patch.2 indicates _total count of patches applied_ so far i.e two. Should we encounter another another bug in upstream v0.30.2 & have to maintain a third patch then we add our fix to github.com/esnet/certificates under branch patch/v0.30.2, test it & create a commit tag with v0.30.2-patch.3. + +``` +git tag -a v0.30.2-patch.3 -m "detailed commit message" +git push --tags v0.30.2-patch.3 +``` + +- Update the go.mod in acme-proxy repo to point to the new tag + +``` +replace github.com/smallstep/certificates => github.com/esnet/certificates v0.30.2-patch.3 +``` + +As a best practice, use [atomic commits for each patch](https://github.com/smallstep/certificates/compare/master...esnet:certificates:patch/v0.30.2) with detailed commit message. diff --git a/contrib/develop/test-infra.md b/contrib/develop/test-infra.md new file mode 100644 index 0000000..347e16f --- /dev/null +++ b/contrib/develop/test-infra.md @@ -0,0 +1,28 @@ +# Test Infrstructure using Docker containers + +1. Build the container image from project root and start the server + +```sh +git clone --recurse-submodules git@github.com:esnet/acme-proxy.git +docker build -t acme-proxy:latest . +``` + +2. If you are running Docker on a Linux host then run the following commands. If on MacOS, jump to 3. + +```sh +mkdir -p /opt/acme-proxy/db +touch /opt/acme-proxy/ca.json +chown -R 65532:65532 /opt/acme-proxy +``` + +3. Start the container with appropriate mounts + +```sh +docker run -d \ + --name acme-proxy-test \ + -p 8443:443 \ + -v "$(pwd)"/ca.json:/opt/acme-proxy/ca.json:ro \ + -v "$(pwd)"/db:/opt/acme-proxy/db \ + --restart unless-stopped \ + acme-proxy:latest +``` diff --git a/contrib/scripts/uninstall.sh b/contrib/scripts/uninstall.sh new file mode 100644 index 0000000..1e957bd --- /dev/null +++ b/contrib/scripts/uninstall.sh @@ -0,0 +1,50 @@ +#!/bin/sh +# +# Uninstalls acme-proxy and related configuration files on a linux host for a clean start +# +set -e + +INSTALL_DIR="${INSTALL_DIR:-/opt/acme-proxy}" +SERVICE_USER="${SERVICE_USER:-acme-proxy}" +SERVICE_GROUP="${SERVICE_GROUP:-acme-proxy}" +SERVICE_FILE="/etc/systemd/system/acme-proxy.service" + +echo "Stopping acme-proxy service..." +if systemctl is-active --quiet acme-proxy 2>/dev/null; then + systemctl stop acme-proxy +fi + +echo "Disabling acme-proxy service..." +if systemctl is-enabled --quiet acme-proxy 2>/dev/null; then + systemctl disable acme-proxy +fi + +echo "Removing systemd service file..." +if [ -f "$SERVICE_FILE" ]; then + rm -f "$SERVICE_FILE" + systemctl daemon-reload +fi + +echo "Clearing any failed state..." +systemctl reset-failed acme-proxy 2>/dev/null || true + +echo "Removing installation directory ${INSTALL_DIR}..." +if [ -d "$INSTALL_DIR" ]; then + rm -rf "$INSTALL_DIR" +fi + +echo "Removing service user ${SERVICE_USER}..." +if id "${SERVICE_USER}" >/dev/null 2>&1; then + userdel "${SERVICE_USER}" +fi + +if [ "${SERVICE_USER}" != "${SERVICE_GROUP}" ]; then + echo "Removing service group ${SERVICE_GROUP}..." + if getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then + groupdel "${SERVICE_GROUP}" + fi +fi + +echo "" +echo "Uninstallation complete." +echo "" From def7aced598718a08d3abaa7dcbbe506f69bd5a7 Mon Sep 17 00:00:00 2001 From: Kapil Agrawal <7047165+netops2devops@users.noreply.github.com> Date: Tue, 11 Aug 2026 21:25:15 -0500 Subject: [PATCH 2/2] Bump golang version from 1.26.2 -> 1.26.5 for Docker container builds. Adds a new make target useful when using dlv as debugger. Signed-off-by: Kapil Agrawal <7047165+netops2devops@users.noreply.github.com> --- Dockerfile | 2 +- Makefile | 6 ++++ develop/contribute.md | 13 -------- develop/externalcas.md | 57 ------------------------------------ develop/maintenance.md | 26 ---------------- develop/scripts/uninstall.sh | 47 ----------------------------- 6 files changed, 7 insertions(+), 144 deletions(-) delete mode 100644 develop/contribute.md delete mode 100644 develop/externalcas.md delete mode 100644 develop/maintenance.md delete mode 100644 develop/scripts/uninstall.sh diff --git a/Dockerfile b/Dockerfile index 7ce9405..e7213a0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # Stage 1: Build step-ca with acme-proxy plugin -FROM golang:1.26.2-trixie AS build +FROM golang:1.26.5-trixie AS build WORKDIR /build diff --git a/Makefile b/Makefile index 4720e94..ff3a55a 100644 --- a/Makefile +++ b/Makefile @@ -55,3 +55,9 @@ build: clean check-deps mkdir db go build -ldflags="-s -w -X main.Version=$(VERSION) -X 'main.BuildTime=$(BUILD_TIME)'" -v -o $(APP_NAME) . @echo "✔ OK" + +debug: clean check-deps + mkdir db + go build \ + -ldflags="-X main.Version=$(VERSION) -X 'main.BuildTime=$(BUILD_TIME)'" \ + -gcflags="all=-N -l" -o $(APP_NAME) . diff --git a/develop/contribute.md b/develop/contribute.md deleted file mode 100644 index 74bd35f..0000000 --- a/develop/contribute.md +++ /dev/null @@ -1,13 +0,0 @@ -# Setup development environment - -1. Install `go >= 1.25` -2. Install `pre-commit` -3. Clone the repo -4. Run `make dev` - -## Contribute - -1. Fork the repo -2. In your fork, create a new branch for your work -3. Add changes in your branch, Commit & push changes to your forked repo -4. Submit a pull request diff --git a/develop/externalcas.md b/develop/externalcas.md deleted file mode 100644 index abc9f12..0000000 --- a/develop/externalcas.md +++ /dev/null @@ -1,57 +0,0 @@ -# ACME server as Registration Authority - -See [upstream docs](#upstream-docs) section for more background on what registration authority, CAS are and how those concepts fits into step-ca architecture. - -## Certificate Authority Service (CAS) - -CAS provides a plugin based architecture that allows Step CA to delegate certificate signing to different backends - whether that's Google Cloud, HashiCorp Vault, or in our case, external certificate authorities like Sectigo or ZeroSSL. ACME proxy can be run as a standalone ACME server in Registraiton Authority mode - -### ExternalCAS - -`Step CA` provides an interface called `CertificateAuthorityService` [CAS](https://github.com/smallstep/certificates/tree/master/cas) to support external certificate authorities as the signing body. Our code in `externalcas` simply implements the `CertificateAuthorityService` interface. - -```go -type CertificateAuthorityService interface { - CreateCertificate(req *CreateCertificateRequest) (*CreateCertificateResponse, error) - RenewCertificate(req *RenewCertificateRequest) (*RenewCertificateResponse, error) - RevokeCertificate(req *RevokeCertificateRequest) (*RevokeCertificateResponse, error) -} -``` - -The go package also defines a special type called `ExternalCAS` for this exact purpose. Which is why our [ca.json](../ca.json) file defines an authority of `type: externalcas`. - -```go -const ( - // DefaultCAS is a CertificateAuthorityService using software. - DefaultCAS = "" - // SoftCAS is a CertificateAuthorityService using software. - SoftCAS = "softcas" - // CloudCAS is a CertificateAuthorityService using Google Cloud CAS. - CloudCAS = "cloudcas" - // StepCAS is a CertificateAuthorityService using another step-ca instance. - StepCAS = "stepcas" - // VaultCAS is a CertificateAuthorityService using Hasicorp Vault PKI. - VaultCAS = "vaultcas" - // ExternalCAS is a CertificateAuthorityService using an external injected CA implementation - ExternalCAS = "externalcas" -) -``` - -## Upstream docs - -**Step CA github repo** - - -**Step CA Registration Authority (RA)** - - -**RA related github discussions** - -- -- - -**Step CA full configuration options** - - -**Certificate issuance policy configuration** - diff --git a/develop/maintenance.md b/develop/maintenance.md deleted file mode 100644 index 47f9440..0000000 --- a/develop/maintenance.md +++ /dev/null @@ -1,26 +0,0 @@ -# Guide to patching upstream related changes - -- While smallstep/certifiates is meant to serve as the upstream Go module for acme-proxy, we have to maintain some patches/fixes ourselves until they get merged upstream. Our patched version of step-ca is currently maintained in a forked repo [esnet/certificates](https://github.com/esnet/certificates). - -- The branch naming scheme for our patches follow a pattern `patch/upstream-version`. For example: patches made against smallstep/certificates `v0.30.2` are in a branch called `patch/v0.30.2`. - -- Once the patches have been applied and tested, we tag the commit using a naming scheme `[upstream version]-patch.count`. So if the patches have been applied against upstream `v0.30.2` our go.mod in acme-proxy should contain - -``` -replace github.com/smallstep/certificates => github.com/esnet/certificates v0.30.2-patch.2 -``` - -Where the trailing patch.2 indicates _total count of patches applied_ so far i.e two. Should we encounter another another bug in upstream v0.30.2 & have to maintain a third patch then we add our fix to github.com/esnet/certificates under branch patch/v0.30.2, test it & create a commit tag with v0.30.2-patch.3. - -``` -git tag -a v0.30.2-patch.3 -m "detailed commit message" -git push --tags v0.30.2-patch.3 -``` - -- Update the go.mod in acme-proxy repo to point to the new tag - -``` -replace github.com/smallstep/certificates => github.com/esnet/certificates v0.30.2-patch.3 -``` - -As a best practice, use [atomic commits for each patch](https://github.com/smallstep/certificates/compare/master...esnet:certificates:patch/v0.30.2) with detailed commit message. diff --git a/develop/scripts/uninstall.sh b/develop/scripts/uninstall.sh deleted file mode 100644 index f63c1c2..0000000 --- a/develop/scripts/uninstall.sh +++ /dev/null @@ -1,47 +0,0 @@ -#!/bin/sh -set -e - -INSTALL_DIR="${INSTALL_DIR:-/opt/acme-proxy}" -SERVICE_USER="${SERVICE_USER:-acme-proxy}" -SERVICE_GROUP="${SERVICE_GROUP:-acme-proxy}" -SERVICE_FILE="/etc/systemd/system/acme-proxy.service" - -echo "Stopping acme-proxy service..." -if systemctl is-active --quiet acme-proxy 2>/dev/null; then - systemctl stop acme-proxy -fi - -echo "Disabling acme-proxy service..." -if systemctl is-enabled --quiet acme-proxy 2>/dev/null; then - systemctl disable acme-proxy -fi - -echo "Removing systemd service file..." -if [ -f "$SERVICE_FILE" ]; then - rm -f "$SERVICE_FILE" - systemctl daemon-reload -fi - -echo "Clearing any failed state..." -systemctl reset-failed acme-proxy 2>/dev/null || true - -echo "Removing installation directory ${INSTALL_DIR}..." -if [ -d "$INSTALL_DIR" ]; then - rm -rf "$INSTALL_DIR" -fi - -echo "Removing service user ${SERVICE_USER}..." -if id "${SERVICE_USER}" >/dev/null 2>&1; then - userdel "${SERVICE_USER}" -fi - -if [ "${SERVICE_USER}" != "${SERVICE_GROUP}" ]; then - echo "Removing service group ${SERVICE_GROUP}..." - if getent group "${SERVICE_GROUP}" >/dev/null 2>&1; then - groupdel "${SERVICE_GROUP}" - fi -fi - -echo "" -echo "Uninstallation complete." -echo ""