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