- Go 69.4%
- Jinja 30.6%
|
|
||
|---|---|---|
| .forgejo/workflows | ||
| template | ||
| .gitignore | ||
| go.mod | ||
| go.sum | ||
| README.md | ||
| render_haproxy.go | ||
HAProxy config renderer
Generate HAProxy HTTP/HTTPS routes and backends from a two-column CSV and
template/haproxy.cfg.j2. Every CSV row creates an HTTP backend on port 80 and an HTTPS
backend on port 443, plus the corresponding frontend routing rules.
Build
Install Go 1.25 or newer, then run these commands from this directory:
go mod download
go build -o haproxy-render .
The renderer uses Gonja, a Go implementation of Jinja, to evaluate the loops and expressions in the supplied template. The built binary needs the CSV and template files at runtime, but does not need Go installed.
Forgejo build
.forgejo/workflows/build.yml builds a Linux amd64 binary on pushes, pull
requests, and manual runs. Enable Actions for the repository and provide a
runner with the ubuntu-latest label and a Linux environment supporting Node.js
actions. The Go version is read from go.mod.
Download the haproxy-render-linux-amd64 artifact from the workflow run. Forgejo
downloads it as a ZIP containing haproxy-render-linux-amd64.tar.gz. Unzip the
download first, then extract the tar archive, which includes the executable and
template/haproxy.cfg.j2. Supply your own CSV when running from the extracted
directory:
unzip haproxy-render-linux-amd64.zip
tar -xzf haproxy-render-linux-amd64.tar.gz
./haproxy-render -csv /path/to/records.csv -output haproxy.generated.cfg
Use the actual downloaded filename in the unzip command if your browser saves
it under a different name. Passing the outer ZIP directly to tar -xzf fails
because that command expects gzip data.
The artifact step uses the v3 upload action supported by Forgejo Actions.
CSV format
Create records.example.csv using the following example for the six web routes:
url,internal_url
jellyfin.lab.dangleminh.com,jellyfin.lab.dangleminh.com
auth.lab.dangleminh.com,auth.lab.dangleminh.com
s3.lab.dangleminh.com,s3.lab.dangleminh.com
git.lab.dangleminh.com,192.168.10.200
coder.lab.dangleminh.com,192.168.10.200
dangleminh.com,dangleminh.com
url is the public hostname matched against HTTPS SNI and the HTTP Host header.
A full URL is also accepted; only its hostname is used. Matching is exact and
case insensitive. Duplicate public hostnames are rejected.
Generated backend and ACL names replace hostname dots with underscores, for
example host_jellyfin_lab_dangleminh_com_https. Routing hostnames and backend
addresses retain their dots.
internal_url supplies the backend destination:
| Input example | HTTP target | HTTPS target |
|---|---|---|
192.168.10.200 |
192.168.10.200:80 |
192.168.10.200:443 |
2001:db8::10 |
[2001:db8::10]:80 |
[2001:db8::10]:443 |
origin.example.com |
origin.example.com:80 |
origin.example.com:443 |
https://origin.example.com/path |
origin.example.com:80 |
origin.example.com:443 |
origin.example.com:8443 |
origin.example.com:8443 |
origin.example.com:8443 |
An explicit port overrides both backend ports. Use this only when intentional:
the HTTP backend still sends plain HTTP, and the HTTPS backend still passes
through TLS. For an IPv6 address with a port, use [2001:db8::10]:8443.
URL schemes do not select the backend transport; paths, queries and fragments
do not rewrite requests. URLs containing credentials are rejected.
The header is optional. With a header, use url,internal_url on the first line.
The first two columns are used; extra columns are ignored.
Generate a config
Render to a separate file for review:
./haproxy-render -csv records.example.csv -output haproxy.generated.cfg
Or run directly through Go:
go run . -csv records.example.csv -output haproxy.generated.cfg
To write the final haproxy.cfg:
./haproxy-render -csv records.example.csv -output haproxy.cfg
The output file is overwritten if it already exists. Rendering does not install the config or reload HAProxy.
| Flag | Default | Purpose |
|---|---|---|
-csv |
Required | Input CSV path |
-template |
template/haproxy.cfg.j2 |
Template path |
-output |
haproxy.cfg |
Generated config path |
-ssh-target |
192.168.10.200 |
IPv4 or IPv6 SSH backend address |
Paths are relative to the working directory. Use ./haproxy-render -h for help.
SSH is separate from the CSV: the template forwards port 2222 to the IP supplied
by -ssh-target, also on port 2222.
Routing and DNS
HTTPS uses TCP passthrough. The origin must serve TLS and a certificate for the
public hostname; HAProxy does not terminate TLS or rewrite the client's SNI.
HTTP requests redirect to HTTPS except for /.well-known/acme-challenge/, which
is sent to the matching HTTP backend. The original HTTP Host header is retained.
The example CSV is valid, but its hostname targets must resolve to reachable
origin servers. If a target resolves to this HAProxy listener, forwarding can
loop back into HAProxy. Use an origin IP or a separate origin DNS name when
the public hostname points to the proxy. The git and coder targets require
connectivity from HAProxy to 192.168.10.200 on ports 80 and 443.
The generator does not perform DNS lookups. The template configures HAProxy's
runtime DNS resolvers as 9.9.9.9 and 1.1.1.1; replace these with internal DNS
servers if your backend names are private. Server lines include resolvers mynameservers only for hostname targets. Literal IPv4/IPv6 targets, including
IPs inside URLs and the SSH target, omit it. All targets retain check for health
checks. Hostname targets are not restricted
to AAAA records. HAProxy documents IPv6 as the default runtime DNS family
preference, which is not an IPv6-only guarantee. See the
HAProxy DNS options.
Validate before use
On the intended HAProxy host, run:
haproxy -c -f haproxy.generated.cfg
The template retains the original system paths, haproxy user/group, and error
files under /etc/haproxy/errors/. Those must exist on the target host. A syntax
check does not establish origin connectivity or correct DNS; check these from
the HAProxy host as well before installing the config through your usual process.