Skip to content

Commit 4168185

Browse files
feat(dockerfile): migrate to scratch-based image (#143)
* feat: migrate to scratch-based Docker image - Replace alpine base with scratch for minimal 16MB image - Remove docker_start.sh (no shell in scratch) - Use ENTRYPOINT + CMD for direct binary execution - Add CA certificates for HTTPS connections to LAPI - Add docker/README.md with comprehensive usage documentation * fix: create runtime directories for scratch image - Add /run/crowdsec-spoa/ for Unix socket - Add /var/log/crowdsec-spoa/ for log files - Use .keep files to ensure empty directories are copied to scratch * fix: correct Docker image name to crowdsecurity/spoa-bouncer * feat: add Docker-optimized config with environment variables - Add config/crowdsec-spoa-bouncer.docker.yaml with stdout logging - Support env vars: CROWDSEC_KEY, CROWDSEC_URL, LOG_LEVEL, etc. - Enable Prometheus by default on port 6060 - Update Dockerfile to use Docker config and expose both ports - Update README with comprehensive environment variables table * fix: use ENV defaults instead of shell syntax for env vars - StrictExpand doesn't support ${VAR:-default} syntax - Set default values via ENV in Dockerfile - Simplify docker.yaml to use plain ${VAR} substitution - Update README to clarify only CROWDSEC_KEY is required * docs: pin image versions in Docker examples Address Copilot review: use pinned versions instead of :latest - haproxy:3.1 - curlimages/curl:8.11.1 * fix: restore VOLUME declarations for Lua and HTML customization Address Copilot review: keep volumes for user customization even though documentation examples were simplified * docs: remove GOMEMLIMIT from README examples * docs: reorder config sections - custom config before env vars Custom configuration file is the primary method, environment variables are a convenience layer for simple deployments.
1 parent 383d8f6 commit 4168185

File tree

4 files changed

+274
-37
lines changed

4 files changed

+274
-37
lines changed

Dockerfile

Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -4,43 +4,53 @@ FROM golang:${GOVERSION}-alpine AS build
44

55
WORKDIR /go/src/cs-spoa-bouncer
66

7-
RUN apk add --update --no-cache make git
7+
RUN apk add --update --no-cache make git ca-certificates
88
COPY . .
99

1010
RUN make build DOCKER_BUILD=1
1111

12-
FROM alpine:latest
13-
COPY --from=build /go/src/cs-spoa-bouncer/crowdsec-spoa-bouncer /usr/local/bin/crowdsec-spoa-bouncer
14-
COPY --from=build /go/src/cs-spoa-bouncer/config/crowdsec-spoa-bouncer.yaml /etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml
15-
COPY --from=build /go/src/cs-spoa-bouncer/docker/docker_start.sh /docker_start.sh
12+
# Create directory structure for scratch image (with .keep files so COPY works)
13+
RUN mkdir -p /run/crowdsec-spoa /var/log/crowdsec-spoa && \
14+
touch /run/crowdsec-spoa/.keep /var/log/crowdsec-spoa/.keep
1615

17-
# Set permissions for config file and binary
18-
RUN chmod 644 /etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml && \
19-
chmod 755 /usr/local/bin/crowdsec-spoa-bouncer
16+
# Final minimal image
17+
FROM scratch
2018

21-
## Add the same haproxy user as the official haproxy image
22-
RUN addgroup -g 99 -S haproxy && adduser -S -D -H -u 99 -h /var/lib/haproxy -s /sbin/nologin -G haproxy -g haproxy haproxy
23-
## Add worker user
24-
RUN addgroup -S crowdsec-spoa && adduser -S -D -H -s /sbin/nologin -g crowdsec-spoa crowdsec-spoa
19+
# Default environment variables (can be overridden at runtime)
20+
ENV LOG_MODE=stdout \
21+
LOG_LEVEL=info \
22+
CROWDSEC_URL=http://crowdsec:8080/ \
23+
UPDATE_FREQUENCY=10s \
24+
INSECURE_SKIP_VERIFY=false \
25+
LISTEN_TCP=0.0.0.0:9000 \
26+
PROMETHEUS_ENABLED=true \
27+
PROMETHEUS_ADDR=0.0.0.0 \
28+
PROMETHEUS_PORT=6060
2529

26-
## Create a socket for the spoa to inherit crowdsec-spoa:haproxy user from official haproxy image
27-
RUN mkdir -p /run/crowdsec-spoa/ && chown crowdsec-spoa:haproxy /run/crowdsec-spoa/ && chmod 770 /run/crowdsec-spoa/
30+
# Copy CA certificates for HTTPS connections to LAPI
31+
COPY --from=build /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
2832

29-
## Copy Lua files (matching Debian/RPM paths)
30-
RUN mkdir -p /usr/lib/crowdsec-haproxy-spoa-bouncer/lua
31-
COPY --from=build /go/src/cs-spoa-bouncer/lua/* /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/
33+
# Copy the static binary
34+
COPY --from=build /go/src/cs-spoa-bouncer/crowdsec-spoa-bouncer /crowdsec-spoa-bouncer
3235

33-
## Copy templates (matching Debian/RPM paths)
34-
RUN mkdir -p /var/lib/crowdsec-haproxy-spoa-bouncer/html
35-
COPY --from=build /go/src/cs-spoa-bouncer/templates/* /var/lib/crowdsec-haproxy-spoa-bouncer/html/
36+
# Copy Docker-optimized config file
37+
COPY --from=build /go/src/cs-spoa-bouncer/config/crowdsec-spoa-bouncer.docker.yaml /etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml
3638

37-
RUN chown -R root:haproxy /usr/lib/crowdsec-haproxy-spoa-bouncer/lua /var/lib/crowdsec-haproxy-spoa-bouncer/html
39+
# Copy Lua files for HAProxy integration
40+
COPY --from=build /go/src/cs-spoa-bouncer/lua/ /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/
3841

39-
VOLUME [ "/usr/lib/crowdsec-haproxy-spoa-bouncer/lua/", "/var/lib/crowdsec-haproxy-spoa-bouncer/html/" ]
42+
# Copy HTML templates for ban/captcha pages
43+
COPY --from=build /go/src/cs-spoa-bouncer/templates/ /var/lib/crowdsec-haproxy-spoa-bouncer/html/
4044

41-
RUN chmod +x /docker_start.sh
45+
# Copy runtime directories (required for Unix socket and logs)
46+
COPY --from=build /run/crowdsec-spoa/ /run/crowdsec-spoa/
47+
COPY --from=build /var/log/crowdsec-spoa/ /var/log/crowdsec-spoa/
4248

43-
# Run as user
44-
USER crowdsec-spoa
49+
# Declare volumes for customizable content
50+
VOLUME /usr/lib/crowdsec-haproxy-spoa-bouncer/lua/
51+
VOLUME /var/lib/crowdsec-haproxy-spoa-bouncer/html/
4552

46-
ENTRYPOINT ["/docker_start.sh"]
53+
EXPOSE 9000 6060
54+
55+
ENTRYPOINT ["/crowdsec-spoa-bouncer"]
56+
CMD ["-c", "/etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml"]
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
## Docker-optimized configuration for CrowdSec SPOA Bouncer
2+
## Environment variables can override settings: ${VAR_NAME}
3+
## If a variable is not set, the default value is used.
4+
5+
## Log configuration
6+
## stdout is recommended for Docker (use `docker logs` to view)
7+
log_mode: ${LOG_MODE}
8+
log_level: ${LOG_LEVEL}
9+
10+
## LAPI configuration
11+
api_url: ${CROWDSEC_URL}
12+
api_key: ${CROWDSEC_KEY}
13+
update_frequency: ${UPDATE_FREQUENCY}
14+
insecure_skip_verify: ${INSECURE_SKIP_VERIFY}
15+
16+
## SPOA listener configuration
17+
listen_tcp: ${LISTEN_TCP}
18+
#listen_unix: ${LISTEN_UNIX}
19+
20+
## GeoIP databases (optional, mount as volumes)
21+
#asn_database_path: /var/lib/crowdsec/data/GeoLite2-ASN.mmdb
22+
#city_database_path: /var/lib/crowdsec/data/GeoLite2-City.mmdb
23+
24+
## Global AppSec configuration (optional)
25+
#appsec_url: ${APPSEC_URL}
26+
#appsec_timeout: ${APPSEC_TIMEOUT}
27+
28+
## Prometheus metrics endpoint
29+
prometheus:
30+
enabled: ${PROMETHEUS_ENABLED}
31+
listen_addr: ${PROMETHEUS_ADDR}
32+
listen_port: ${PROMETHEUS_PORT}
33+
34+
## pprof debug endpoint (disabled by default)
35+
## WARNING: Only enable for debugging, exposes internal runtime data
36+
#pprof:
37+
# enabled: false
38+
# listen_addr: 0.0.0.0
39+
# listen_port: 6070

docker/README.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# CrowdSec HAProxy SPOA Bouncer Docker Image
2+
3+
This is a minimal scratch-based Docker image containing only the statically-linked bouncer binary and essential files.
4+
5+
## Image Contents
6+
7+
```
8+
/crowdsec-spoa-bouncer # The bouncer binary
9+
/etc/ssl/certs/ca-certificates.crt # CA certs for HTTPS to LAPI
10+
/etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml # Default config
11+
/usr/lib/crowdsec-haproxy-spoa-bouncer/lua/ # Lua files for HAProxy
12+
/var/lib/crowdsec-haproxy-spoa-bouncer/html/ # Ban/captcha templates
13+
```
14+
15+
## Quick Start
16+
17+
```bash
18+
docker run -d \
19+
--name crowdsec-spoa-bouncer \
20+
-e CROWDSEC_KEY=your-api-key \
21+
-e CROWDSEC_URL=http://crowdsec:8080/ \
22+
-p 9000:9000 \
23+
-p 6060:6060 \
24+
crowdsecurity/spoa-bouncer
25+
```
26+
27+
## Configuration
28+
29+
### Custom Configuration File
30+
31+
Mount your own configuration file for full control:
32+
33+
```bash
34+
docker run -d \
35+
--name crowdsec-spoa-bouncer \
36+
-v /path/to/your/config.yaml:/etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml:ro \
37+
-p 9000:9000 \
38+
crowdsecurity/spoa-bouncer
39+
```
40+
41+
Or specify a different config path:
42+
43+
```bash
44+
docker run -d \
45+
--name crowdsec-spoa-bouncer \
46+
-v /path/to/config.yaml:/config.yaml:ro \
47+
-p 9000:9000 \
48+
crowdsecurity/spoa-bouncer -c /config.yaml
49+
```
50+
51+
### Environment Variables
52+
53+
For simple deployments, the default configuration supports environment variables:
54+
55+
| Variable | Default | Description |
56+
|----------|---------|-------------|
57+
| `CROWDSEC_KEY` | **required** | API key for CrowdSec LAPI |
58+
| `CROWDSEC_URL` | `http://crowdsec:8080/` | CrowdSec LAPI URL |
59+
| `LOG_MODE` | `stdout` | Log output: `stdout` or `file` |
60+
| `LOG_LEVEL` | `info` | Log level: `trace`, `debug`, `info`, `warn`, `error` |
61+
| `UPDATE_FREQUENCY` | `10s` | How often to poll LAPI for decisions |
62+
| `INSECURE_SKIP_VERIFY` | `false` | Skip TLS verification for LAPI |
63+
| `LISTEN_TCP` | `0.0.0.0:9000` | TCP listener address |
64+
| `PROMETHEUS_ENABLED` | `true` | Enable Prometheus metrics |
65+
| `PROMETHEUS_ADDR` | `0.0.0.0` | Prometheus listen address |
66+
| `PROMETHEUS_PORT` | `6060` | Prometheus listen port |
67+
68+
**Note:** Default values are set in the Docker image. Only `CROWDSEC_KEY` must be provided.
69+
70+
### Unix Socket (Recommended for Same-Host HAProxy)
71+
72+
```bash
73+
docker run -d \
74+
--name crowdsec-spoa-bouncer \
75+
-v /path/to/config.yaml:/etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml:ro \
76+
-v /run/crowdsec-spoa:/run/crowdsec-spoa \
77+
crowdsecurity/spoa-bouncer
78+
```
79+
80+
Ensure the socket directory exists and has appropriate permissions for HAProxy to connect.
81+
82+
## Docker Compose Example
83+
84+
```yaml
85+
services:
86+
crowdsec-spoa-bouncer:
87+
image: crowdsecurity/spoa-bouncer
88+
restart: unless-stopped
89+
environment:
90+
- CROWDSEC_KEY=${CROWDSEC_API_KEY}
91+
- CROWDSEC_URL=http://crowdsec:8080/
92+
- LOG_LEVEL=info
93+
ports:
94+
- "6060:6060" # Prometheus metrics
95+
networks:
96+
- crowdsec
97+
deploy:
98+
resources:
99+
limits:
100+
memory: 256M
101+
102+
haproxy:
103+
image: haproxy:3.1
104+
volumes:
105+
- ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
106+
ports:
107+
- "80:80"
108+
- "443:443"
109+
networks:
110+
- crowdsec
111+
depends_on:
112+
- crowdsec-spoa-bouncer
113+
114+
networks:
115+
crowdsec:
116+
```
117+
118+
## Running as Non-Root
119+
120+
The scratch image runs as root by default. To run as a specific user:
121+
122+
```bash
123+
docker run -d \
124+
--user 1000:1000 \
125+
--name crowdsec-spoa-bouncer \
126+
-p 9000:9000 \
127+
crowdsecurity/spoa-bouncer
128+
```
129+
130+
Note: Ensure mounted volumes have appropriate permissions for the specified user.
131+
132+
## Health Checks
133+
134+
Prometheus metrics are enabled by default on port 6060. Since this is a scratch image with no shell, use external health checks:
135+
136+
```yaml
137+
# Docker Compose with healthcheck via curl sidecar
138+
services:
139+
crowdsec-spoa-bouncer:
140+
image: crowdsecurity/spoa-bouncer
141+
environment:
142+
- CROWDSEC_KEY=${API_KEY}
143+
# Use depends_on with service_healthy for dependent services
144+
145+
healthcheck:
146+
image: curlimages/curl:8.11.1
147+
command: ["sh", "-c", "while true; do curl -sf http://crowdsec-spoa-bouncer:6060/metrics > /dev/null && echo healthy || echo unhealthy; sleep 30; done"]
148+
depends_on:
149+
- crowdsec-spoa-bouncer
150+
```
151+
152+
Or check from the host:
153+
154+
```bash
155+
curl -sf http://localhost:6060/metrics > /dev/null && echo "healthy" || echo "unhealthy"
156+
```
157+
158+
## Ports
159+
160+
| Port | Default | Description |
161+
|------|---------|-------------|
162+
| 9000 | Yes | SPOA TCP listener |
163+
| 6060 | Yes | Prometheus metrics (enabled by default) |
164+
| 6070 | No | pprof debug endpoint (disabled by default) |
165+
166+
## Troubleshooting
167+
168+
### View Logs
169+
170+
```bash
171+
docker logs -f crowdsec-spoa-bouncer
172+
```
173+
174+
### Debug Mode
175+
176+
Set the `LOG_LEVEL` environment variable:
177+
178+
```bash
179+
docker run -e LOG_LEVEL=debug -e CROWDSEC_KEY=... crowdsecurity/spoa-bouncer
180+
```
181+
182+
### Connection Issues
183+
184+
1. Verify LAPI is reachable from the container
185+
2. Check API key is correct
186+
3. Ensure HAProxy can reach the SPOA listener (TCP port or Unix socket)
187+
188+
## Building the Image
189+
190+
```bash
191+
docker build -t crowdsecurity/spoa-bouncer .
192+
```
193+
194+
### Build Arguments
195+
196+
| Argument | Default | Description |
197+
|----------|---------|-------------|
198+
| GOVERSION | 1.25 | Go version for build stage |
199+

docker/docker_start.sh

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)