Architecture Log: Hardening Self-Hosted CI/CD Pipelines
Tags: DevSecOps, Infrastructure, Docker, Nginx, GitLab
Self-hosting a CI/CD pipeline (like GitLab) is a great way to maintain control over your code, but it makes your infrastructure a prime target for automated scanners and credential stuffing. I recently audited my own edge architecture and implemented a few strict defensive layers I want to document here.
If you are exposing a Git instance or Docker registry to the open internet, a standard Nginx reverse proxy isn't enough. You need aggressive, application-layer filtering.
Defensive Strategy 1: Edge Rate Limiting
Most people rate-limit globally, which punishes legitimate users. I prefer targeting specific, high-value authentication endpoints. By applying a strict limit_req_zone in Nginx strictly to the /users/sign_in paths, I neutralized automated brute-forcing without impacting general repository cloning.
Defensive Strategy 2: Rootless Containers & Read-Only State At the system level, privilege escalation is the worst-case scenario.
Rootless Docker: The daemon should never run as root. If a runner executes malicious CI code that escapes the container, the attacker lands in an unprivileged user space.
Immutability: I've started mounting my container configuration volumes as
ro(read-only). If an attacker finds a vulnerability in the web interface, they cannot overwrite the coregitlab.rbconfigurations to gain persistence.
The Result: A defense-in-depth model where the edge drops scanners immediately, and the host OS is shielded from container-level compromises.
References:
Docker Security Best Practices:
Docker Docs Nginx Rate Limiting Module:
NGINX Docs