Skip to main content

Build a NTP Timeserver Client in Go

Shenzhen, China

I used Docker to build a Go-based NTS Timeserver Server by @hacklunch and deployed the service using Hashicorp Nomad. Now I need to compile the binary for an ARM environment so that it can be used on an IoT device.

Related

Test Build the Binary

build.sh

#!/usr/bin/bash
archs=(amd64 arm64 arm)

for arch in ${archs[@]}
do
env GOOS=linux GOARCH=${arch} go build -ldflags "-s -w" -o build/ntsclient_${arch}
upx --ultra-brute -obuild/ntsclient_upx_${arch} build/ntsclient_${arch}
done
./build.sh

Ultimate Packer for eXecutables
Copyright (C) 1996 - 2020
UPX git-d7ba31+ Markus Oberhumer, Laszlo Molnar & John Reiser Jan 23rd 2020

File size Ratio Format Name
-------------------- ------ ----------- -----------
5554176 -> 1734704 31.23% linux/amd64 ntsclient_upx_amd64

Packed 1 file.

File size Ratio Format Name
-------------------- ------ ----------- -----------
5242880 -> 1494584 28.51% linux/arm64 ntsclient_upx_arm64

Packed 1 file.

File size Ratio Format Name
-------------------- ------ ----------- -----------
5373952 -> 1443108 26.85% linux/arm ntsclient_upx_arm

Run the Binary

ntsclient does not output anything when querying and setting the time, unless something goes wrong (or debug output is turned on). An example configuration file looks like:

ntsclient.toml

# NTS Server
server="time.cloudflare.com"
# Interval in seconds between queries
interval=1000

# Alternative NTS Server

# https://netnod.se
#server="nts.ntp.se"

# https://nts.time.nl/
#server="nts.time.nl"

# Test servers listed on https://docs.ntpsec.org/latest/NTS-QuickStart.html
#server="ntp1.glypnod.com"
#server="ntp2.glypnod.com"
./ntsclient --config ./ntsclient.toml
Could not set system time: operation not permitted

sudo ./ntsclient --config ./ntsclient.toml

To switch on the debug mode:

sudo ./ntsclient --config ./ntsclient.toml --debug

Use Gitlab CI to Build the Binary

The following CI file generates the ARM version of the ntsclient and stores it as an artifact to be used in a firmware build pipeline:

.gitlab-ci.yml

image: golang:latest

variables:
REPO: my.gitlab.com
GROUP: firmware
PROJECT: ntsclient
ARCH: arm

stages:
- build

before_script:
# Something strange to make Gitlab like Go
- mkdir -p $GOPATH/src/$REPO/$GROUP $GOPATH/src/_/builds
- cp -r $CI_PROJECT_DIR $GOPATH/src/$REPO/$GROUP/$PROJECT
- ln -s $GOPATH/src/$REPO/$GROUP $GOPATH/src/_/builds/$GROUP
# Use UPX to compress the binary after build
# I downloaded/un-tar'ed the latest release into the repo root dir
- cp ./upx-4.0.0-amd64_linux/upx /usr/bin/npx
- chmod +x /usr/bin/npx
# Make sure all Go build dependencies are available
- go get -v -d ./...

build:
stage: build
script:
- env GOOS=linux GOARCH=$ARCH go build -ldflags "-s -w" -o ntsclient_src
- /usr/bin/npx --ultra-brute -ontsclient ntsclient_src
only:
- main
artifacts:
paths:
# Push the binary into your repo's build artifacts
- /builds/firmware/ntsclient/ntsclient
expire_in: 1 day