During setup, it’s common practice to use some kind of “runners” that is, applications that take care of translating our infrastructure as a code definition to a real deployment. The runner can be used for many purposes, to deploy infrastructure, to build an application from a source code and push it to an artifactory, or to just execute commands against any targets of our choosing.
Possible attacks against the runners are:
- Leverage leaked runner registration token to steal source code and sensitive CI/CD variables.
- Malicious code can misuse runner to access internal company network.
- Compromise host system by leveraging Docker runner in
- Cloning the runner instance that can steal the jobs and extract sensitive data.
In this guide, we are mainly touching Gitlab runners, but similar problems exist in all publicly known software with similar functionality, for example Jenkins.
Security control proposal
When you setup runners
Don’t use self-signed certificates when configuring communication between the runner and the Gitlab server. Instead, use either public or private certification authority and properly signed x509 SSL/TLS certificates for the communication.
Don’t use Shell runner executor as there is no security. Instead, use Docker-in-a-Docker executor or Kubernetes executor when running jobs. Use unprivileged Docker containers. Apply appropriate hardening to Docker and Kubernetes executors.
For more information on how to properly setup runner, please read Gitlab reference documentation.
Using shared runners
When using shared runners:
- Do NOT use production credentials nor deploy to production.
- Do NOT allow full access to your internal network (or Internet).
- Limit number of jobs.
- Use specific tags.
Use shared runners only when you do not expect privacy, i.e. no sensitive information is pulled to the runner. Also make sure to not expose sensitive network to a runner as jobs which run there can access network resources. This can be exploited by a fraudulent application during the supply chain attack.
Using dedicated runners
When to use dedicated runners:
- If you handle sensitive data.
- Group or project specific runner is way better than shared runner. On shared runner, sensitive information can be exposed to other users by runner misconfiguration or by exploiting runner vulnerability.
Project specific or private runners are the best choice from the security perspective, but they are using a lot of extra resources, which can be a problem in large organizations with lots of projects.
Good practice is to use group runners for a selected set of projects which logically belong together.
When using dedicated runners:
- Do NOT allow public repositories to have access to runners which have broader access to the internal network.
- Place runners in a confined network.
- Use specific runner tags to prevent accidents.