Please follow the instructions in the following step for setting up the development environment. (Note: This process was tested on a virtual machine with Ubuntu 22.04):
- Install git
- Install Go 1.20+
- Install Rust 1.64+
- Install Mage.
If you are installing
Mage
using GOPATH, make sure that your$GOPATH
variable is set correctly. - Install Docker.
After the installation is completed, add the user to the
docker
group by runningsudo usermod -aG docker $USER
on the terminal. Restart desktop session by relogging in order to the make changes effective. - Optionally setup environment for testing application across various distros.
- Install Vagrant.
- Install Vagrant-Libvirt. How to use vagrant (from the project root):
List of available distros:vagrant up fedora36 vagrant ssh fedora36 vagrant destroy fedora36 # when the work is done
- debian10
- debian11
- fedora35
- fedora36
- ubuntu18
- ubuntu20
- ubuntu22
Virtual machine mounts
dist/app
directory from the host at/vagrant
.
- (optional) Install protoc to be able to compile protobuf files.
- Might need to rename
protoc-gen-go-grpc
binary toprotoc-gen-go_grpc
to work.
- Might need to rename
- (optional) Install Act to run Github jobs locally https://github.com/nektos/act
- Run
mage
to discover and execute build targets.- To use non-Docker targets please refer to
ci/docker/*/Dockerfile
Dockerfiles for necessary dependencies to be installed.
- To use non-Docker targets please refer to
Convenient way for building the application is available using the mage and optionally having a docker daemon running.
mage build:binaries
mage build:binariesDocker
Below steps can be used to build the app without mage and build scripts.
- Go 1.22+
- libxml2
- iptables
- iproute2
Building requires injecting the following variables via linker:
# Used to derive keys when encrypting/decrypting configuration files.
# Changing the Salt means changing the keys, which means losing access
# to previously encrypted files. While this is fine during development,
# production builds should never change the Salt between versions.
main.Salt
# Used by `nordvpn --version` command.
# Usually taken from the git commit tag.
main.Version
# Used to implement feature toggles.
# It's either dev or prod.
main.Environment
# Used by `nordvpn --version` command.
# Equal to the git commit hash the build was made from.
main.Hash
LDFLAGS="-X main.Version=${} \
-X main.Environment=${} \
-X main.Hash=${} \
-X main.Salt=${} \
go build -ldflags "${LDFLAGS}" \
-o "bin/nordvpn" "cmd/cli"
Since Go 1.17 building position independent executables requires using C linker. C linker also allows adding additional protection known as hardening:
CGO_CFLAGS="-g -O2 -D_FORTIFY_SOURCE=2" \
CGO_LDFLAGS="-Wl,-z,relro,-z,now" \
LDFLAGS="-X main.Version=${} \
-X main.Environment=${} \
-X main.Hash=${} \
-X main.Salt=${} \
go build -buildmode=pie -ldflags "-s -w -linkmode=external ${LDFLAGS}" \
-o "bin/nordvpn" "cmd/cli"
Building requires injecting the following variables via linker:
# Used to derive keys when encrypting/decrypting configuration files.
# Changing the Salt means changing the keys, which means losing access
# to previously encrypted files. While this is fine during development,
# production builds should never change the Salt between versions.
main.Salt
# Used by `nordvpn --version` command.
# Usually taken from the git commit tag.
main.Version
# Used to implement feature toggles.
# It's either dev or prod.
main.Environment
# Used by deb/rpm repository checker to find out if there is a new version available.
main.Arch
# Used by deb/rpm repository checker to find out if there is a new version available.
main.PackageType
Application features implemented in FFI libraries are hidden behind build tags. For development builds, it's acceptable to omit build tags, which means that the application is compiled without CGo dependencies. For production builds, the following tags are used:
- drop (filesharing feature)
- moose (telemetry feature)
- telio (meshnet feature)
LDFLAGS="-X main.Version=${} \
-X main.Environment=${} \
-X main.Arch=${} \
-X main.Salt=${} \
-X main.PackageType=${} \
go build -ldflags "${LDFLAGS}" \
-o "bin/nordvpnd" "cmd/daemon"
Since Go 1.17 building position independent executables requires using C linker.
Production builds also use CGo, so the library path has to be given to the C linker.
Due to Rust's unstable ABI, multiple Rust libraries are compiled into a single library
called libfoss.a
and linked into the application.
C linker also allows adding additional protection known as hardening:
CGO_CFLAGS="-g -O2 -D_FORTIFY_SOURCE=2" \
LDFLAGS="-X main.Version=${} \
-X main.Environment=${} \
-X main.Arch=${} \
-X main.Salt=${} \
-X main.PackageType=${} \
go build -buildmode=pie -tags=drop,moose,telio \
-ldflags "-s -w -linkmode=external ${LDFLAGS}" \
-o "bin/nordvpnd" "cmd/daemon"
Binaries found in cmd/<binary>/main.go
are not shipped to the users and are built with:
go build -o "bin/<binary>" "cmd/<binary>"
- checkelf (ensures that nordvpn/nordvpnd executables don't exceed glibc version)
- downloader (downloads files from CDN used when building deb/rpm packages)
- pulp (removes outdated packages from deb/rpm package repository)
main
- main branch which always contains latest changes. Direct commits are strictly forbidden.
Released packages of Linux App can be found in https://repo.nordvpn.com/deb and https://repo.nordvpn.com/yum
The following images can be built:
- builder
- generator
- notifier
- packager
- qa-peer
- ruster
- scanner
- snaper
- tester
- uploader
Images are stored in ghcr.io/nordsecurity/nordvpn-linux
registry.
The building and tagging can be done in a single command like this:
docker build -t <registry>/<image>[:tag] ci/docker/<image>
The pushing can be done with:
docker push <registry>/<image>[:tag]
By default, mage targets will always pull docker images from the registry. If below entry is present in .env
file, images will be pulled only if they are not present on the host system:
IDEMPOTENT_DOCKER=1
We run golangci-lint for our changes. You can find our linter setting in .golangci-lint.yml.