Security News

Cybersecurity news aggregator

đź“°
INFO News Reddit r/netsec

Using Nix or Docker for reproducible Development Environments

  • What: Article discusses using Nix or Docker for reproducible development environments
  • Impact: Helps developers maintain consistent workflows
Read Full Article →

Using Nix or Docker for reproducible Development Environments Introduction Having an efficient and robust CI/CD pipeline is one of the keys to developing high-quality services. However, setting up such a system is not always straightforward, and maintaining it over time is even more challenging. One of the main reasons is the difficulty in achieving a consistent execution environment between the CI system and developers’ local machines. Unfortunately, such systems are sometimes underdeveloped because they are not always perceived as worthwile. Yet, without them, it is impossible to deliver a service under good conditions and at a sustained pace. In my view, the rise of GitHub Actions is a symptom of these issues. When you think about it, the promise of GitHub Actions is to spend as little time as possible on your CI by maximizing the use of actions from the marketplace . The promise is enticing, and it’s true that the catalog is massive. But as soon as you dig a little deeper, you realize that this paradigm can quickly lead to several problems. Notably: CI/CD pipelines often require secrets to function. All it takes is one compromised action to have those secrets stolen. In the best case, you can rotate the secrets in time. But the time saved by not writing the action yourself is likely lost managing the consequences of such an attack. This is not a hypothetical scenario: it’s what recently happened with the Trivy security scanner hack , as well as the tj-actions/changed-files action compromise in 2025. As soon as you introduce an action other than checkout or the various setup-* actions to install a program, you lose much of your ability to test changes. Yet, the CI/CD pipeline itself needs to be tested, as its failure can waste time for all development teams. In the worst case, it can even delay the deployment of critical hotfixes. In the rest of this article, we will try to implement best practices to limit these problems while still enjoying the benefits of GitHub Actions. The Scenario Let’s set a goal starting from a simple application: a web server in Go. To avoid limiting ourselves to a simple “hello-world”, we will add a third-party library: templ for HTML rendering. If you are not familiar with templ , don’t worry, it’s not the core of the subject. Just remember that this tool requires running a command to generate code before building our application. From there, we want to be able to: Lint our code Test our code Generate code (required when using templ ) Build our application But we will impose several constraints to keep our CI/CD maintainable: The steps of our CI/CD must be easily runnable locally, so we can test them effectively. The system must work on Linux and macOS, because even though the CI will run on Linux, some developers use macOS. Our system must be as secure as possible, especially resistant to supply-chain attacks. Let’s get started! Code Linting Let’s start with this aspect, which will be the simplest. If you are somewhat familiar with GitHub Actions, you probably know that it can be done very simply. We will use the golangci-lint tool, an excellent tool that checks whether our code complies with a number of standards, such as not ignoring errors. We just need to make sure our code is generated via templ generate beforehand. on : push : branches : - main jobs : codegen : name : "Ensure code is generated" runs-on : ubuntu-latest steps : - uses : actions/checkout@v6 - uses : actions/setup-go@v6 with : go-version-file : "go.mod" - name : Setup templ run : go install github.com/a-h/templ/cmd/templ@latest - name : "Ensure code is generated" run : scripts/codegen.sh lint : name : "Lint code" runs-on : ubuntu-latest steps : - uses : actions/checkout@v6 - uses : actions/setup-go@v6 with : go-version-file : "go.mod" - name : golangci-lint uses : golangci/golangci-lint-action@v9 The honeymoon with GitHub Actions is at its peak: the code will be linted on every commit with just 15 lines of YAML. Everything seems perfect… Except it’s not. Several issues remain: Every time this workflow runs, the system will read golangci/golangci-lint-action@v9 and download the corresponding action from GitHub. But what happens if this project is attacked and a hacker points the v9 tag to a malicious version of the action? In this case, you will have to rotate your secrets, but even if you do so in time, the attacker will already have had plenty of time to download your source code. The more different actions you use in your workflow, the higher this risk. Additionally, we do not control the version of golangci-lint that is installed, which can lead to differences in results between local use and CI. We want to avoid such subtleties, as they can all cause us to lose time if an error were to appear. Finally, we have a code generation job and a linting job. If we want to keep these jobs separate, we have to reinstall all Go utilities in each job, which creates unnecessary network overhead. Let’s Improve Things...

Share this article