Disclaimer: This post includes Amazon affiliate links. If you click on one of them and you make a purchase I’ll earn a commission. Please notice your final price is not affected at all by using those links.
Welcome to another post part of the series covering Quality Attributes / Non-Functional Requirements, this time I’m talking about Security
specifically when dealing with Dependencies.
Security is important
Security
is very important and also a huge and complex topic, I’m stating the obvious for sure but I want to make it clear that this post is focused on Securing Dependencies. What this is means in practice is having a way to determine if the packages we use are not exposed to known vulnerabilities, this includes the standard library packages as well as third party packages.
I will share with you a few different tools to determine if the packages we use are secure and up to date. To make all of this to work we need to have something in place already: Continuous Integration. If you don’t have this I recommend you to invest some resources to have it, because by using Continuous Integration a lot of the things I’m going to be sharing would be automated and easier to implement.
Securing Dependencies
Whether you use Github Actions, CircleCI, JenkinsCI or GitlabCi; the steps I’m going to show you are going to be similar, the key component is to have a way to automatically determine if the packages we use are:
- Up to date, and
- Vulnerability free
The tools I recommend adding to your Continuous Integration to determine if those requirements are satisfied are the following:
Dependabot
,gosec
linter,Snyk
service,CodeQL
, andversions
.
Dependabot
Dependabot is a service part of Github that allows us to keep our dependencies up to date, it supports multiple programming languages, it’s Open Source and in some cases there are alternatives compatible to concrete CI-services, like GitlabCI.
The way Dependabot works is by defining a configuration file associated to your repository that indicates the Package Ecosystem, like Go Modules, NPM, Maven or Docker (to mention a few); as well as a concrete interval to use for checking if there are updates available to the packages being used.
Because Dependabot is part of Github, it integrates nicely with Github Actions, a simple configuration to enable checking for Go modules for my To-Do Microservice Example looks like:
1version: 2
2updates:
3 - package-ecosystem: "gomod"
4 directory: "/"
5 schedule:
6 interval: "daily"
7 - package-ecosystem: "gomod"
8 directory: "/internal/tools/"
9 schedule:
10 interval: "daily"
This would create Pull Requests when new versions are available, for example:
Please refer to the official Dependabot documentation because there are more things included in dependabot like ignoring certain versions for example.
gosec
linter
gosec
is a Go linter that comes bundled as part of golangci-lint, but it also works stand alone if you prefer it that way. This linter includes multiple rules to detect, among other things:
- Harcoded credentials,
- SQL query construction using strings, that could lead to SQL injection, and
- Detect usage of some
crypto
packages in the standard library.
When using golangci-lint
make sure you have this linter enabled, to do that update your .golangci.yml
and make sure it’s part of the list of enabled linters, for example:
linters:
enable:
- gosec
Snyk
Snyk
is a paid service that detects vulnerabilities found in our projects, it not only detects issues in the packages used to build our artifacts but also other dependencies like Dockerfiles. I linked some of my Open Source projects on Github and to date they look like this:
If we look at nit
, you can notice that it’s also doing some Code Analysis and, like I mentioned before, it’s also checking Dockerfiles.
Snyk also includes a list vulnerabilities Database, for example for Go, it looks (to date) like this:
Finally depending on our configuration we may want to also enable creating Pull Requests automatically when new issues are detected, for example Snyk created this one for upgrading Alpine in the Dockerfile.
CodeQL
CodeQL is a service to discover vulnerabilities across a codebase using their semantic code analysis engine. Similar to Dependabot, enabling it on Github is simple, we only need to add a new workflow:
1name: "CodeQL"
2
3on:
4 push:
5 branches: [main]
6 pull_request:
7 branches: [main]
8 schedule:
9 - cron: '19 4 * * 1'
10
11jobs:
12 analyze:
13 name: Analyze
14 runs-on: ubuntu-latest
15 permissions:
16 actions: read
17 contents: read
18 security-events: write
19 strategy:
20 fail-fast: false
21 matrix:
22 language: [ 'go' ]
23 steps:
24 - name: Checkout repository
25 uses: actions/checkout@v2
26 - name: Initialize CodeQL
27 uses: github/codeql-action/init@v1
28 with:
29 languages: ${{ matrix.language }}
30 - name: Autobuild
31 uses: github/codeql-action/autobuild@v1
32 - name: Perform CodeQL Analysis
33 uses: github/codeql-action/analyze@v1
Which to be fair, was practically autogenerated after enabling it on the project, to do so:
- Go to your project Settings,
- Click Security & analisys, and
- Click Set up for Code Scanning
From there you can add the CodeQL Analysis workflow that will let you to push the configuration file directly to your repository.
versions
tool
versions
is a tool I wrote to determine the package versions used by multiple projects, at the time I didn’t know about Dependabot so doing something like opening Pull Requests automatically to upgrade versions was something planned but in the end I decided to focus more on building some-sort of reports other tools could import.
To date versions
is used to create a list of the packages each project uses, their versions as well as the Go version they use, however it only works for projects using Go modules. One thing I do like about versions
is the fact that Go versions are listed as well, this is important because of Go’s Release policy, where only the last two major versions are supported, having a tool like this let us know when it’s time to upgrade to a newer major version.
Conclusion
Security
is usually an after-thought when building services but it shouldn’t be that way, we already have tools and services that allows us to detect issues automatically, however those require in most cases to have Continuous Integration in place, so this is another great reason to add that to your development pipeline.
This is not an extensive list for sure, I will continue covering more Security
related topics for Go in future posts, stay tuned.
Recommended reading
If you’re looking to sink your teeth into more Software Architecture I recommend the following links:
- Post: Quality Attributes / Non-Functional Requirements
- Post: Software Architecture in Go: Throttling Cloud Design Pattern for Scalability and Security
- Post: Microservices in Go: Caching using memcached
- Book: Hands-On Software Architecture with Golang
- Book: Building Evolutionary Architectures: Support Constant Change - Post
- Book: Practical Cloud Security: A Guide for Secure Design and Deployment