Skip to content

Commit e1ccdbb

Browse files
refactor: replace worker subprocesses with goroutines (#88)
* refactor: replace worker subprocesses with goroutines Major architectural refactor to improve performance and simplify codebase: Performance improvements: - Remove IPC overhead between SPOA and workers (~1200 lines deleted) - Direct shared memory access via pointers instead of socket communication - Eliminate JSON encoding/decoding per request - Reduce latency and CPU usage Architecture changes: - New goroutine-based worker manager (internal/worker/manager.go) - SPOA workers now share dataset, host manager, and geo database via pointers - Removed worker API client/server and all IPC infrastructure - Metrics now tracked directly in SPOA handlers (upstream proxy compatible) Security improvements: - Systemd service runs as crowdsec-spoa user/group (privilege separation) - Admin socket now optional and disabled by default - SO_PEERCRED verification for root-only admin access - Security hardening in systemd unit files Bug fixes: - Fix ServeUnix using wrong listener (ListenAddr vs ListenSocket) - Fix worker shutdown timeout (5ns -> 5s) - Fix potential metrics double-counting for upstream proxy mode Testing: - Comprehensive test suite for new worker manager - All existing tests passing - Build verified successful Packaging: - Added optional admin socket systemd unit - Updated Debian and RPM packaging - Docker container runs as root (standard practice) Breaking changes: None (configuration compatible) * fix: address golangci-lint issues - Fix containedctx: Add nolint comment for errgroup context (legitimate use case) - Fix deferInLoop: Wrap defer cancel() in anonymous function to properly scope - Fix hugeParam: Pass SpoaConfig by pointer instead of value (80 bytes) - Fix testifylint: Use assert.Empty instead of assert.Len for empty check - Fix testifylint: Use require.NoError for error assertions All tests passing, build verified. * fix: add admin socket installation to install/uninstall scripts - Add ADMIN_SOCKET and SYSTEMD_ADMIN_SOCKET_FILE variables to _bouncer.sh - Install optional admin socket unit in install.sh (disabled by default) - Add installation instructions for optional admin socket - Update uninstall.sh to stop, disable, and remove admin socket unit - Fix documentation URL to include trailing slash - Add systemctl daemon-reload to uninstall.sh The admin socket is installed but remains disabled by default, users must manually enable it with: systemctl enable --now crowdsec-spoa-bouncer-admin.socket * fix: use dedicated logging directory with proper permissions - Change log_dir from /var/log/ to /var/log/crowdsec-spoa/ - Add LogsDirectory=crowdsec-spoa to systemd service (auto-creates with correct ownership) - Set LogsDirectoryMode=0750 for security - Update uninstall script to clean up both legacy and new log locations This fixes 'read-only file system' errors when using ProtectSystem=strict. Systemd automatically creates /var/log/crowdsec-spoa/ owned by crowdsec-spoa:crowdsec-spoa. For existing installations, users need to update log_dir in their config to the new path. Migration instructions will be provided in the release changelog. * fix: config file permissions for unprivileged service user - Change config file permissions from 0600 to 0640 (owner rw, group r) - Set config file group to crowdsec-spoa for read access - Ensure crowdsec-spoa group is created in all install paths: * Manual install script (groupadd/addgroup fallback) * RPM %post (groupadd --system) * Debian postinst (already creates group) - Update packaging scripts to set group ownership: * install.sh: -g crowdsec-spoa flag * _bouncer.sh: set_config_var_value() with -g crowdsec-spoa * RPM spec: chgrp in %post * Debian postinst: chgrp after user creation * debian/rules: chmod 0640 * RPM spec: install -m 640 This fixes permission denied errors when the systemd service (running as crowdsec-spoa user) tries to read the config file owned by root. * fix: add Service directive to admin socket unit - Add Service=crowdsec-spoa-bouncer.service to admin socket unit - This tells systemd to activate the main bouncer service when admin socket receives connection Without this directive, systemd looks for crowdsec-spoa-bouncer-admin.service which doesn't exist, causing 'Socket service not loaded' error when enabling the admin socket. * fix: admin socket systemd unit configuration - Remove Service= directive (causes conflict when service already running) - Add PartOf/Before to make socket part of the service lifecycle - Socket fd is passed to service on startup via systemd activation - Update instructions: enable socket, then restart service (not --now) The admin socket uses systemd socket activation but the service must be restarted to receive the socket file descriptor. This prevents 'already active, refusing' errors. Correct activation flow: 1. Uncomment admin_socket in config 2. systemctl enable crowdsec-spoa-bouncer-admin.socket 3. systemctl restart crowdsec-spoa-bouncer.service * fix: ensure systemd socket activation works for admin socket - Add After=crowdsec-spoa-bouncer-admin.socket to service unit - Service now waits for admin socket if it's enabled - If socket unit is enabled, systemd creates it as root:root - If socket unit is disabled, service creates it as crowdsec-spoa:crowdsec-spoa This ensures proper socket ownership (root:root) when using systemd socket activation, while maintaining fallback for non-systemd environments. To verify: systemctl stop crowdsec-spoa-bouncer systemctl enable crowdsec-spoa-bouncer-admin.socket systemctl start crowdsec-spoa-bouncer-admin.socket systemctl start crowdsec-spoa-bouncer ls -la /run/crowdsec-spoa/admin.sock # Should show root:root * fix: properly configure optional systemd socket activation Socket unit configuration: - Service=crowdsec-spoa-bouncer.service (passes fd to correct service) - FileDescriptorName=admin (identifies the fd) - PartOf/Before for lifecycle management Service unit configuration: - After=crowdsec-spoa-bouncer-admin.socket (waits if socket enabled) - NO Wants= directive (keeps socket truly optional) The socket is only activated if explicitly enabled by user: systemctl enable crowdsec-spoa-bouncer-admin.socket systemctl restart crowdsec-spoa-bouncer If not enabled, service creates socket itself as fallback (crowdsec-spoa:crowdsec-spoa) * refactor: consolidate admin API and fix logger inheritance - Merge pkg/server + internal/api -> internal/admin (single package) - Remove GOB encoding, use simple string-based protocol - Remove unused GetHostUnsetCookie command (worker API only) - Fix logger inheritance: cmd/root.go -> manager -> workers - Simplify types: keep only used error codes and response types - Net reduction: -602 lines of code This completes the worker-to-goroutine refactor by cleaning up the admin interface and ensuring proper logger propagation. * refactor: simplify admin server to use single listener The admin server only ever uses one listener (either from systemd socket activation or manually created), so there's no need for a slice. This simplifies the code and makes the logic clearer. * fix: make admin socket truly optional with proper systemd integration Changes to socket unit: - Remove Service= directive (service runs independently, not socket-activated) - Remove PartOf= directive (socket is independent/optional) - Remove FileDescriptorName= (not needed for basic activation) - Keep Before= to ensure socket is ready if enabled - Add usage instructions in comments Changes to service unit: - Add Sockets= directive to explicitly request admin socket FD if available - Note: Sockets= is a hint, not a hard dependency - service starts fine without it - Remove admin socket from After= (it's optional, shouldn't block startup) How it works: 1. By default, socket is disabled - service runs without admin interface 2. To enable: systemctl enable --now crowdsec-spoa-bouncer-admin.socket 3. When service starts/restarts, systemd passes the socket FD via LISTEN_FDS 4. Service detects FD and uses it for root-only admin access * fix: add Service directive back to socket unit Without Service=, systemd looks for crowdsec-spoa-bouncer-admin.service which doesn't exist. Need to explicitly specify which service the socket is for: crowdsec-spoa-bouncer.service The socket is still optional - Service= just tells systemd which service to pass the FD to when the socket is enabled. * fix: remove Sockets= to make admin socket truly optional The Sockets= directive was causing the socket to auto-start when the service started, even when the socket unit was disabled. This made it impossible to truly disable the admin socket. Removed Sockets= from service unit. Now the socket is only active if explicitly enabled by the administrator. How it works now: - Socket disabled (default): No admin interface - Socket enabled: systemd passes FD to service via LISTEN_FDS when service starts - To enable: systemctl enable --now crowdsec-spoa-bouncer-admin.socket - To disable: systemctl stop + disable the socket unit * docs: clarify admin socket configuration for Docker vs systemd Updated the config file comments to make it clear: - Docker users: Uncomment admin_socket line in config - Systemd users: Do NOT uncomment config, use the socket unit instead This prevents confusion where systemd users might uncomment the config option, which would create the socket as crowdsec-spoa user instead of root, defeating the security model. * fix: preserve runtime directory across service restarts Add RuntimeDirectoryPreserve=yes to systemd service unit. Without this, /run/crowdsec-spoa/ is removed when the service stops and recreated with default ownership (crowdsec-spoa:crowdsec-spoa) when it starts. With this setting, the directory persists across restarts, allowing administrators to set custom group ownership that survives restarts: chgrp haproxy /run/crowdsec-spoa/ systemctl restart crowdsec-spoa-bouncer The directory group is preserved, and Unix sockets created within it will inherit the group ownership (with 0660 permissions). * fix: use setgid directory for socket group inheritance Non-root process can't chown() to change socket group. Use setgid directory instead so sockets automatically inherit directory group. Changes: - Remove chown() call (was failing with 'operation not permitted') - Set RuntimeDirectoryMode=2750 (setgid bit + 0750 permissions) - Sockets inherit directory group, get 0660 permissions from umask Usage: chgrp haproxy /run/crowdsec-spoa && systemctl restart Result: sockets are crowdsec-spoa:haproxy with 0660 permissions * fix: admin server logging to inherit root logger and log level - Remove Logger field from admin.Config (not needed) - Use log.WithField() directly to inherit from standard logger - Admin server now respects log level configuration like other components * test: use random ports in worker tests to avoid conflicts - Add getFreePort helper function for dynamic port allocation - Update all TCP worker tests to use random available ports - Makes tests more robust and prevents port conflicts * fix: SPOA logging to use inherited logger instead of global logger - Replace all log. calls in SPOA with s.logger. calls - Fixed handleHTTPRequest and handleIPRequest functions - Ensures worker-specific logging with proper context - All log messages now include worker name for better debugging * fix: filter out net.ErrClosed errors during graceful server shutdown Prevent spurious 'use of closed network connection' errors from being logged when SPOA TCP and Unix servers shut down gracefully. Use errors.Is() to check for net.ErrClosed and treat it as a normal shutdown rather than an error. * refactor: unify TCP and Unix server handling into single Serve method Replace separate ServeTCP and ServeUnix methods with a single Serve method that handles both TCP and Unix socket serving using a shared error channel and helper function, following the same pattern as CrowdSec's APIServer. - Remove duplicate logging by using Spoa logger with worker context - Simplify worker manager to launch single goroutine per worker - Maintain same error filtering for net.ErrClosed during shutdown * chore: remove accidentally committed binary (crowdsec-spoa) * chore(lint): fix revive issues on workers-to-goroutines - manager_test: check TCPAddr type assertion - spoa: remove useless break in Serve switch - spoa: add temporary nolint for handleHTTPRequest length (to be split with AppSec) * test(worker): make TCPAddr assertion safe in tests * chore: remove accidentally committed binary (crowdsec-spoa) from workers-to-goroutines * refactor: single spoa listener (#98) * refactor: simplify SPOA shutdown logic - Remove unnecessary goroutine for context cancellation - Always call Shutdown() after errgroup completes - Ensure graceful shutdown even on unexpected errors * refactor: remove worker manager files * refactor(spoa): extract captcha flow, add HTTPRequestData for AppSec - Move captcha remediation logic to handleCaptchaRemediation - Return parsed URL/Method/Headers/Body to avoid reparsing later - Add parseHTTPData helper and wire through allow/ban paths - Cleanup logging and use matchedHost.Host instead of hoststring - Keep behavior for metrics and remediation unchanged * refactor: remove worker-only settings and remnants - Drop SpoaConfig.LogLevel and level override in spoa - Remove internal/worker package and tests - Ensure build/lint/tests pass under single SPOA architecture * fix: captcha logic and reduce calls to session when not needed * Refactor IP remediation to use netip * Enforce listener requirement in config * Run go mod tidy after merge * Remove admin socket functionality (#102) - Remove internal/admin package and all admin socket code - Remove AdminSocket configuration option - Remove admin socket systemd unit file - Remove admin socket references from scripts and Dockerfile - Clean up configuration comments * Run go mod tidy * Run Docker container as crowdsec-spoa user * Remove socat from Dockerfile * Fix origin variable initialization in metrics counting - Initialize origin when checking IP remediation for metrics - Only count metrics when IP extraction succeeds - Remove unused checkIPRemediation function * Update debian/postinst Co-authored-by: Copilot <[email protected]> * Remove unnecessary directory creation from Docker startup script Directory is already created in Dockerfile with proper permissions before user switch * Update comment to clarify setgid bit handling systemd RuntimeDirectoryMode=2750 already sets setgid bit, manual chmod only needed for non-systemd deployments * Set config file permissions to 0640 and group ownership - Change config file permissions from 0600 to 0640 (allow group read) - Set group ownership to crowdsec-spoa in RPM post-install script - Match Debian and RPM packaging for consistent permissions --------- Co-authored-by: Copilot <[email protected]>
1 parent 742df38 commit e1ccdbb

File tree

25 files changed

+653
-2981
lines changed

25 files changed

+653
-2981
lines changed

Dockerfile

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ARG GOVERSION=1.24
1+
ARG GOVERSION=1.25
22

33
FROM golang:${GOVERSION}-alpine AS build
44

@@ -14,8 +14,10 @@ COPY --from=build /go/src/cs-spoa-bouncer/crowdsec-spoa-bouncer /usr/local/bin/c
1414
COPY --from=build /go/src/cs-spoa-bouncer/config/crowdsec-spoa-bouncer.yaml /etc/crowdsec/bouncers/crowdsec-spoa-bouncer.yaml
1515
COPY --from=build /go/src/cs-spoa-bouncer/docker/docker_start.sh /docker_start.sh
1616

17-
## Add socat
18-
RUN apk add --no-cache socat
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
20+
1921
## Add the same haproxy user as the official haproxy image
2022
RUN addgroup -g 99 -S haproxy && adduser -S -D -H -u 99 -h /var/lib/haproxy -s /sbin/nologin -G haproxy -g haproxy haproxy
2123
## Add worker user
@@ -37,4 +39,7 @@ VOLUME [ "/usr/local/crowdsec/lua/haproxy/", "/var/lib/crowdsec/lua/haproxy/temp
3739

3840
RUN chmod +x /docker_start.sh
3941

42+
# Run as user
43+
USER crowdsec-spoa
44+
4045
ENTRYPOINT ["/docker_start.sh"]

cmd/root.go

Lines changed: 36 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package cmd
22

33
import (
44
"context"
5-
"encoding/json"
65
"errors"
76
"fmt"
87
"net"
@@ -19,13 +18,10 @@ import (
1918
"github.com/spf13/pflag"
2019
"golang.org/x/sync/errgroup"
2120

22-
"github.com/crowdsecurity/crowdsec-spoa/internal/api"
23-
"github.com/crowdsecurity/crowdsec-spoa/internal/worker"
2421
"github.com/crowdsecurity/crowdsec-spoa/pkg/cfg"
2522
"github.com/crowdsecurity/crowdsec-spoa/pkg/dataset"
2623
"github.com/crowdsecurity/crowdsec-spoa/pkg/host"
2724
"github.com/crowdsecurity/crowdsec-spoa/pkg/metrics"
28-
"github.com/crowdsecurity/crowdsec-spoa/pkg/server"
2925
"github.com/crowdsecurity/crowdsec-spoa/pkg/spoa"
3026
csbouncer "github.com/crowdsecurity/go-cs-bouncer"
3127
"github.com/crowdsecurity/go-cs-lib/csdaemon"
@@ -63,9 +59,6 @@ func Execute() error {
6359
testConfig := pflag.BoolP("test", "t", false, "test config and exit")
6460
showConfig := pflag.BoolP("show-config", "T", false, "show full config (.yaml + .yaml.local) and exit")
6561

66-
// Worker pflags
67-
workerConfigJSON := pflag.String("worker-config", "", "worker configuration as JSON string")
68-
6962
pflag.Parse()
7063

7164
// Handle version flags
@@ -74,19 +67,6 @@ func Execute() error {
7467
return nil
7568
}
7669

77-
if *workerConfigJSON != "" {
78-
var w *worker.Worker
79-
if err := json.Unmarshal([]byte(*workerConfigJSON), &w); err != nil {
80-
return fmt.Errorf("failed to parse worker config JSON: %w", err)
81-
}
82-
83-
if w.TcpAddr == "" && w.UnixAddr == "" {
84-
return fmt.Errorf("worker must have one listener address")
85-
}
86-
return WorkerExecute(w)
87-
88-
}
89-
9070
if configPath == nil || *configPath == "" {
9171
return errors.New("configuration file is required")
9272
}
@@ -158,7 +138,7 @@ func Execute() error {
158138
return metricsProvider.Run(ctx)
159139
})
160140

161-
prometheus.MustRegister(csbouncer.TotalLAPICalls, csbouncer.TotalLAPIError, metrics.TotalActiveDecisions, metrics.TotalBlockedRequests, metrics.TotalProcessedRequests, metrics.TotalWorkerAPIConnectionErrors, metrics.TotalWorkerAPIConnectionEvents)
141+
prometheus.MustRegister(csbouncer.TotalLAPICalls, csbouncer.TotalLAPIError, metrics.TotalActiveDecisions, metrics.TotalBlockedRequests, metrics.TotalProcessedRequests)
162142

163143
if config.PrometheusConfig.Enabled {
164144
go func() {
@@ -220,121 +200,61 @@ func Execute() error {
220200
}
221201
}
222202

223-
socketConnChan := make(chan server.SocketConn)
224-
225-
workerServer, err := server.NewWorkerSocket(ctx, socketConnChan, config.WorkerSocketDir)
226-
227-
if err != nil {
228-
return fmt.Errorf("failed to create worker server: %w", err)
229-
}
230-
231-
var adminServer *server.Server
232-
if config.AdminSocket != "" {
233-
var err error
234-
adminServer, err = server.NewAdminSocket(ctx, socketConnChan)
235-
236-
if err != nil {
237-
return fmt.Errorf("failed to create admin server: %w", err)
238-
}
239-
240-
err = adminServer.NewAdminListener(config.AdminSocket)
241-
if err != nil {
242-
return fmt.Errorf("failed to create admin listener: %w", err)
243-
}
203+
// Create single SPOA listener - ultra-simplified architecture
204+
spoaLogger := log.WithField("component", "spoa")
205+
206+
// Create single SPOA directly with minimal configuration
207+
spoaConfig := &spoa.SpoaConfig{
208+
TcpAddr: config.ListenTCP,
209+
UnixAddr: config.ListenUnix,
210+
Dataset: dataSet,
211+
HostManager: HostManager,
212+
GeoDatabase: &config.Geo,
213+
Logger: spoaLogger,
244214
}
245215

246-
workerManager := worker.NewManager(workerServer, config.WorkerUid, config.WorkerGid)
247-
248-
g.Go(func() error {
249-
return workerManager.Run(ctx)
250-
})
251-
252-
apiServer := api.NewAPI(api.APIConfig{
253-
WorkerManager: workerManager,
254-
HostManager: HostManager,
255-
Dataset: dataSet,
256-
GeoDatabase: &config.Geo,
257-
SocketChan: socketConnChan,
258-
})
259-
260-
for _, worker := range config.Workers {
261-
workerManager.CreateChan <- worker
216+
singleSpoa, err := spoa.New(spoaConfig)
217+
if err != nil {
218+
return fmt.Errorf("failed to create SPOA listener: %w", err)
262219
}
263220

221+
// Launch single SPOA server directly
264222
g.Go(func() error {
265-
return apiServer.Run(ctx)
266-
})
267-
268-
// Add worker server to errgroup
269-
g.Go(func() error {
270-
return workerServer.Wait()
223+
if err := singleSpoa.Serve(ctx); err != nil {
224+
return fmt.Errorf("SPOA server failed: %w", err)
225+
}
226+
return nil
271227
})
272228

273-
// Add admin server to errgroup if configured
274-
if adminServer != nil {
275-
g.Go(func() error {
276-
return adminServer.Wait()
277-
})
278-
}
279-
280229
_ = csdaemon.Notify(csdaemon.Ready, log.StandardLogger())
281230

282-
if err := g.Wait(); err != nil {
231+
err = g.Wait()
232+
233+
// Determine if this was an expected shutdown signal
234+
isExpectedShutdown := false
235+
if err != nil {
283236
switch err.Error() {
284237
case "received SIGTERM":
285238
log.Info("Received SIGTERM, shutting down")
239+
isExpectedShutdown = true
286240
case "received interrupt":
287241
log.Info("Received interrupt, shutting down")
288-
default:
289-
return err
242+
isExpectedShutdown = true
290243
}
291244
}
292245

293-
return nil
294-
}
295-
296-
func WorkerExecute(w *worker.Worker) error {
297-
298-
g, ctx := errgroup.WithContext(context.Background())
299-
300-
g.Go(func() error {
301-
return HandleSignals(ctx)
302-
})
303-
304-
spoad, err := spoa.New(w.TcpAddr, w.UnixAddr)
305-
306-
if err != nil {
307-
return fmt.Errorf("failed to create SPOA: %w", err)
308-
}
309-
310-
g.Go(func() error {
311-
if err := spoad.ServeTCP(ctx); err != nil {
312-
return fmt.Errorf("failed to serve TCP: %w", err)
313-
}
314-
return nil
315-
})
316-
317-
g.Go(func() error {
318-
if err := spoad.ServeUnix(ctx); err != nil {
319-
return fmt.Errorf("failed to serve Unix: %w", err)
320-
}
321-
return nil
322-
})
246+
// Shutdown SPOA server gracefully after all goroutines finish
247+
log.Info("Shutting down SPOA listener")
248+
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
249+
defer cancel()
323250

324-
if err := g.Wait(); err != nil {
325-
switch err.Error() {
326-
case "received SIGTERM":
327-
case "received interrupt":
328-
default:
329-
return err
330-
}
251+
if shutdownErr := singleSpoa.Shutdown(shutdownCtx); shutdownErr != nil {
252+
log.Errorf("Failed to shutdown SPOA: %v", shutdownErr)
331253
}
332254

333-
cancelCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
334-
defer cancel()
335-
336-
if err := spoad.Shutdown(cancelCtx); err != nil {
337-
return fmt.Errorf("failed to shutdown server: %w", err)
255+
// Return error only if it was unexpected
256+
if err != nil && !isExpectedShutdown {
257+
return err
338258
}
339259

340260
return nil
Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,29 @@
11
[Unit]
22
Description=The haproxy spoa bouncer for CrowdSec
3+
Documentation=https://docs.crowdsec.net/u/bouncers/haproxy_spoa/
34
After=syslog.target network.target remote-fs.target nss-lookup.target crowdsec.service
45

56
[Service]
67
Type=notify
8+
User=crowdsec-spoa
9+
Group=crowdsec-spoa
10+
# Allow binding to lower ports if needed
11+
# AmbientCapabilities=CAP_NET_BIND_SERVICE
712
ExecStart=${BIN} -c ${CFG}/crowdsec-spoa-bouncer.yaml
813
ExecStartPre=${BIN} -c ${CFG}/crowdsec-spoa-bouncer.yaml -t
9-
ExecStartPre=/bin/mkdir -p /run/crowdsec-spoa
10-
ExecStartPre=/bin/chown crowdsec-spoa:crowdsec-spoa /run/crowdsec-spoa
1114
ExecStartPost=/bin/sleep 0.1
1215
Restart=always
1316
RestartSec=10
17+
# Security hardening
18+
NoNewPrivileges=true
19+
PrivateTmp=true
20+
ProtectSystem=strict
21+
ProtectHome=true
22+
RuntimeDirectory=crowdsec-spoa
23+
RuntimeDirectoryMode=2750
24+
RuntimeDirectoryPreserve=yes
25+
LogsDirectory=crowdsec-spoa
26+
LogsDirectoryMode=0750
1427

1528
[Install]
1629
WantedBy=multi-user.target

config/crowdsec-spoa-bouncer.yaml

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
## Log configuration
22
log_mode: file
3-
log_dir: /var/log/
3+
log_dir: /var/log/crowdsec-spoa/
44
log_level: info
55
log_compression: true
66
log_max_size: 100
@@ -13,20 +13,13 @@ api_url: http://127.0.0.1:8080/
1313
api_key: ${API_KEY}
1414
insecure_skip_verify: false
1515

16-
## SPOA configuration
17-
workers:
18-
- name: spoa1
19-
listen_addr: 0.0.0.0:9000
20-
listen_socket: /run/crowdsec-spoa/spoa-1.sock
21-
worker_user: crowdsec-spoa
22-
worker_group: crowdsec-spoa
16+
## SPOA listener configuration
17+
# Configure TCP and/or Unix socket listeners
18+
listen_tcp: 0.0.0.0:9000
19+
listen_unix: /run/crowdsec-spoa/spoa.sock
2320
#asn_database_path: /var/lib/crowdsec/data/GeoLite2-ASN.mmdb
2421
#city_database_path: /var/lib/crowdsec/data/GeoLite2-City.mmdb
2522

26-
#admin socket
27-
admin_socket: /run/crowdsec-spoa-admin.sock
28-
29-
3023
prometheus:
3124
enabled: false
3225
listen_addr: 127.0.0.1

debian/postinst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ if ! getent passwd crowdsec-spoa >/dev/null; then
2222
adduser crowdsec-spoa --system --group --comment "crowdsec haproxy spoa bouncer"
2323
fi
2424

25+
# Set config file group ownership
26+
if [ -f "$CONFIG" ]; then
27+
chgrp crowdsec-spoa "$CONFIG" 2>/dev/null || true
28+
fi
29+
2530
if [ -d "/etc/haproxy" ]; then
2631
cp /usr/share/doc/crowdsec-haproxy-spoa-bouncer/examples/crowdsec.cfg /etc/haproxy/crowdsec.cfg
2732
fi

debian/rules

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,5 @@ execute_after_dh_fixperms:
3737
PKG="crowdsec-haproxy-spoa-bouncer"; \
3838
chmod 0755 "debian/$$PKG/usr/bin/$$BOUNCER"; \
3939
chmod 0600 "debian/$$PKG/usr/lib/$$PKG/_bouncer.sh"; \
40-
chmod 0600 "debian/$$PKG/etc/crowdsec/bouncers/$$BOUNCER.yaml"; \
40+
chmod 0640 "debian/$$PKG/etc/crowdsec/bouncers/$$BOUNCER.yaml"; \
4141
chmod 0644 "debian/$$PKG/lib/systemd/system/$$BOUNCER.service"

0 commit comments

Comments
 (0)