Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
134 changes: 134 additions & 0 deletions .github/workflows/library-chronyd4.8.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
name: library/chronyd:4.8

on:
schedule:
- cron: '0 3 * * *'

push:
branches: [main]
paths:
- 'library/chronyd/4.8/**'
- '.github/workflows/library-chronyd4.8.yaml'
- '!library/chronyd/4.8/README.md'

pull_request:
types: [opened, synchronize, reopened]
branches: [main]
paths:
- 'library/chronyd/4.8/**'
- '.github/workflows/library-chronyd4.8.yaml'
- '!library/chronyd/4.8/README.md'

# Automatically cancel in-progress actions on the same branch
concurrency:
group: ${{ github.workflow }}-${{ github.event_name == 'pull_request_target' && github.head_ref || github.ref }}
cancel-in-progress: true

jobs:
build:
strategy:
fail-fast: false
matrix:
include:
- plat: qemu
arch: x86_64
- plat: fc
arch: x86_64

runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Build chronyd4.8
uses: unikraft/kraftkit@staging
with:
loglevel: debug
workdir: library/chronyd/4.8
runtimedir: /github/workspace/.kraftkit
plat: ${{ matrix.plat }}
arch: ${{ matrix.arch }}
push: false
output: oci://index.unikraft.io/unikraft.org/chronyd:4.8

- name: Archive OCI digests
uses: actions/upload-artifact@v4
with:
name: oci-digests-${{ matrix.arch }}-${{ matrix.plat }}
path: ${{ github.workspace }}/.kraftkit/oci/digests
if-no-files-found: error

push:
if: ${{ github.event_name == 'push' || github.event_name == 'schedule' }}
needs: [ build ]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Login to OCI registry
uses: docker/login-action@v3
with:
registry: index.unikraft.io
username: ${{ secrets.REG_USERNAME }}
password: ${{ secrets.REG_TOKEN }}

- name: Retrieve, merge and push OCI digests
uses: ./.github/actions/merge-oci-digests
with:
name: index.unikraft.io/unikraft.org/chronyd:4.8
push: true

test:
needs: [ build ]
strategy:
fail-fast: false
matrix:
include:
- plat: qemu
arch: x86_64
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Install dependencies
run: |
sudo apt-get update
sudo apt-get install -y qemu-system-x86 qemu-utils

- name: Install KraftKit
run: curl -sSfL https://get.kraftkit.sh | sudo sh -s -- -y

- name: Build and Run chronyd4.8
env:
KRAFTKIT_NO_CHECK_UPDATES: true
UK_CFLAGS: "-std=gnu17"
run: |
cd library/chronyd/4.8
kraft build --plat ${{ matrix.plat }} --arch ${{ matrix.arch }}
kraft run --rm -W -M 16M -p 1123:123/udp --plat ${{ matrix.plat }} --arch ${{ matrix.arch }} . > /tmp/kraft-chronyd.log 2>&1 &
echo $! > /tmp/kraft_pid

- name: Wait for chronyd to serve synchronized time
run: |
cd library/chronyd/4.8
for i in $(seq 1 180); do
if python3 test.py >/tmp/chronyd-test.log 2>&1; then
echo "chronyd on ${{ matrix.plat }} served synchronized time."
cat /tmp/chronyd-test.log
exit 0
fi
sleep 1
done
echo "chronyd did not serve synchronized time after 180 seconds."
cat /tmp/chronyd-test.log || true
cat /tmp/kraft-chronyd.log || true
exit 1

- name: Stop chronyd instance
if: ${{ always() }}
run: |
if [ -f /tmp/kraft_pid ]; then
kill "$(cat /tmp/kraft_pid)" || true
fi
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,3 +73,4 @@ kraft pkg ls --apps --update
| [![](https://github.com/unikraft/catalog/actions/workflows/library-ruby3.2.yaml/badge.svg)](https://github.com/unikraft/catalog/actions/workflows/library-ruby3.2.yaml) | [`unikraft.org/ruby:3.2`](library/ruby/3.2) |
| [![](https://github.com/unikraft/catalog/actions/workflows/library-skipper0.18.yaml/badge.svg)](https://github.com/unikraft/catalog/actions/workflows/library-skipper0.18.yaml) | [`unikraft.org/skipper:0.18`](library/skipper/0.18) |
| [![](https://github.com/unikraft/catalog/actions/workflows/library-surreal1.1.yaml/badge.svg)](https://github.com/unikraft/catalog/actions/workflows/library-surreal1.1.yaml) | [`unikraft.org/surreal:1.1`](library/surreal/1.1) |
| [![](https://github.com/unikraft/catalog/actions/workflows/library-chronyd4.8.yaml/badge.svg)](https://github.com/unikraft/catalog/actions/workflows/library-chronyd4.8.yaml) | [`unikraft.org/chronyd:4.8`](library/chronyd/4.8) |
2 changes: 2 additions & 0 deletions library/chronyd/4.8/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.unikraft
.config*
40 changes: 40 additions & 0 deletions library/chronyd/4.8/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# chrony built from source with HAVE_RECVMMSG removed
FROM alpine:3.24 AS build

ARG CHRONY_VERSION=4.8

RUN apk add --no-cache gcc make musl-dev nettle-dev
ADD https://chrony-project.org/releases/chrony-${CHRONY_VERSION}.tar.gz /chrony.tar.gz
RUN tar xzf /chrony.tar.gz && \
Comment on lines +7 to +8

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

valid

cd /chrony-${CHRONY_VERSION} && \
./configure --prefix=/usr --sysconfdir=/etc/chrony \
--disable-cmdmon --disable-refclock --disable-ipv6 \
--without-libcap --without-seccomp \
--without-nss --without-tomcrypt \
--without-gnutls && \
sed -i '/HAVE_RECVMMSG/d' config.h && \
! grep -q HAVE_RECVMMSG config.h && \
make -j"$(nproc)" chronyd && \
strip chronyd && \
cp chronyd /usr/sbin/chronyd

# runtime dirs, staged so scratch COPY can pick them up
RUN mkdir -p /out/etc/chrony /out/run/chrony /out/var/lib/chrony

FROM scratch

COPY --from=build /usr/sbin/chronyd /usr/sbin/chronyd
COPY --from=build /etc/passwd /etc/passwd
COPY --from=build /etc/group /etc/group
COPY --from=build /out/etc/chrony /etc/chrony
COPY --from=build /out/run/chrony /run/chrony
COPY --from=build /out/var/lib/chrony /var/lib/chrony

# musl loader (+ libc alias) and nettle, for SECHASH symmetric-key auth
COPY --from=build /lib/ld-musl-x86_64.so.1 /lib/ld-musl-x86_64.so.1
COPY --from=build /lib/ld-musl-x86_64.so.1 /lib/libc.musl-x86_64.so.1
COPY --from=build /usr/lib/libnettle.so.8 /usr/lib/libnettle.so.8

# Static configuration
COPY ./conf/chrony.conf /etc/chrony/chrony.conf
COPY ./conf/resolv.conf /etc/resolv.conf
142 changes: 142 additions & 0 deletions library/chronyd/4.8/Kraftfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
spec: v0.6

name: chronyd

rootfs: ./Dockerfile

# -d foreground, -x to never touch the system clock (its syscalls are stubs anyway)
# -u root skips the privilege drop (single-user unikernel)
# -L 0 informational logging to stdout
cmd: ["/usr/sbin/chronyd", "-d", "-x", "-u", "root", "-L", "0"]

template:
source: https://github.com/unikraft/app-elfloader.git
version: staging

unikraft:
source: https://github.com/unikraft/unikraft.git
version: staging
kconfig:
# Configurations options for app-elfloader
# (they can't be part of the template atm)
CONFIG_LIBPOSIX_PROCESS_ARCH_PRCTL: 'y'
CONFIG_APPELFLOADER_BRK: 'y'
CONFIG_APPELFLOADER_CUSTOMAPPNAME: 'y'
CONFIG_APPELFLOADER_STACK_NBPAGES: 128
CONFIG_APPELFLOADER_VFSEXEC_EXECBIT: 'n'
CONFIG_APPELFLOADER_VFSEXEC: 'y'
CONFIG_APPELFLOADER_HFS: 'y'
CONFIG_APPELFLOADER_HFS_ETCRESOLVCONF: 'n'
CONFIG_APPELFLOADER_HFS_ETCHOSTS: 'y'
CONFIG_APPELFLOADER_HFS_ETCHOSTNAME: 'y'
CONFIG_APPELFLOADER_HFS_REPLACEEXIST: 'y'
# Unikraft options
CONFIG_HAVE_PAGING_DIRECTMAP: 'y'
CONFIG_HAVE_PAGING: 'y'
CONFIG_I8042: 'y'
CONFIG_LIBPOSIX_ENVIRON_ENVP0: "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
CONFIG_LIBPOSIX_ENVIRON_ENVP1: "LD_LIBRARY_PATH=/usr/local/lib:/usr/lib:/lib"
CONFIG_LIBPOSIX_ENVIRON_ENVP2: "HOME=/"
CONFIG_LIBPOSIX_ENVIRON: 'y'
CONFIG_LIBPOSIX_ENVIRON_LIBPARAM: 'y'
CONFIG_LIBPOSIX_ENVIRON_LIBPARAM_MAXCOUNT: '64'
CONFIG_LIBPOSIX_EVENTFD: 'y'
CONFIG_LIBPOSIX_FDIO: 'y'
CONFIG_LIBPOSIX_FDTAB: 'y'
CONFIG_LIBPOSIX_FUTEX: 'y'
CONFIG_LIBPOSIX_MMAP: 'y'
CONFIG_LIBPOSIX_NETLINK: 'y'
CONFIG_LIBPOSIX_PIPE: 'y'
CONFIG_LIBPOSIX_POLL: 'y'
CONFIG_LIBPOSIX_PROCESS: 'y'
CONFIG_LIBPOSIX_PROCESS_MULTITHREADING: 'y'
CONFIG_LIBPOSIX_SOCKET: 'y'
CONFIG_LIBPOSIX_SYSINFO: 'y'
CONFIG_LIBPOSIX_TIME: 'y'
CONFIG_LIBPOSIX_TIMERFD: 'y'
CONFIG_LIBPOSIX_UNIXSOCKET: 'y'
CONFIG_LIBPOSIX_USER_GID: 0
CONFIG_LIBPOSIX_USER_GROUPNAME: "root"
CONFIG_LIBPOSIX_USER_UID: 0
CONFIG_LIBPOSIX_USER_USERNAME: "root"
CONFIG_LIBPOSIX_USER: 'y'
CONFIG_LIBSYSCALL_SHIM_HANDLER_ULTLS: 'y'
CONFIG_LIBSYSCALL_SHIM_HANDLER: 'y'
CONFIG_LIBSYSCALL_SHIM_LEGACY_VERBOSE: 'y'
CONFIG_LIBSYSCALL_SHIM: 'y'
CONFIG_LIBUKALLOCPOOL: 'y'
CONFIG_LIBUKBLKDEV_MAXNBQUEUES: '1'
CONFIG_LIBUKBLKDEV_DISPATCHERTHREADS: 'y'
CONFIG_LIBUKBLKDEV_SYNC_IO_BLOCKED_WAITING: 'y'
CONFIG_LIBUKBLKDEV: 'y'
CONFIG_LIBUKBOOT_BANNER_MINIMAL: 'y'
CONFIG_LIBUKBOOT_MAINTHREAD: 'y'
CONFIG_LIBUKBOOT_SHUTDOWNREQ_HANDLER: 'y'
CONFIG_LIBUKCPIO: 'y'
CONFIG_LIBUKDEBUG_CRASH_SCREEN: 'y'
CONFIG_LIBUKDEBUG_ENABLE_ASSERT: 'y'
CONFIG_LIBUKDEBUG_PRINT_SRCNAME: 'n'
CONFIG_LIBUKDEBUG_PRINT_TIME: 'y'
CONFIG_LIBUKDEBUG_PRINTK_ERR: 'y'
CONFIG_LIBUKDEBUG_PRINTK: 'y'
CONFIG_LIBUKDEBUG: 'y'
CONFIG_LIBUKFALLOC: 'y'
CONFIG_LIBUKMPI: 'n'
CONFIG_LIBUKSIGNAL: 'y'
CONFIG_LIBUKRANDOM_DEVFS: 'y'
CONFIG_LIBUKRANDOM: 'y'
CONFIG_LIBUKRANDOM_GETRANDOM: 'y'
CONFIG_LIBUKVMEM_DEMAND_PAGE_IN_SIZE: 12
CONFIG_LIBUKVMEM_PAGEFAULT_HANDLER_PRIO: 4
CONFIG_LIBUKVMEM: 'y'
CONFIG_LIBVFSCORE: 'n'
CONFIG_LIBPOSIX_VFS_FSTAB: 'y'
CONFIG_LIBPOSIX_VFS_FSTAB_BUILTIN: 'y'
CONFIG_LIBPOSIX_VFS_FSTAB_BUILTIN_INITRD: 'y'
CONFIG_LIBPOSIX_VFS_FSTAB_MOUNT_DEV: 'y'
CONFIG_LIBUK9P: 'y'
CONFIG_OPTIMIZE_DEADELIM: 'y'
CONFIG_OPTIMIZE_LTO: 'y'
CONFIG_PAGING: 'y'
CONFIG_STACK_SIZE_PAGE_ORDER: 4 # 128 * 4K = 512K
CONFIG_UKPLAT_MEMREGION_MAX_COUNT: 64
CONFIG_LIBUKNETDEV_EINFO_LIBPARAM: 'y'

# Debug options
# CONFIG_LIBUKDEBUG_PRINTD: 'y'
# CONFIG_LIBUKDEBUG_PRINTK_INFO: 'y'
# CONFIG_LIBSYSCALL_SHIM_STRACE: 'y'
# CONFIG_LIBSYSCALL_SHIM_DEBUG: 'y'

libraries:
lwip:
source: https://github.com/unikraft/lib-lwip.git
version: staging
kconfig:
CONFIG_LWIP_LOOPIF: 'y'
CONFIG_LWIP_UKNETDEV: 'y'
CONFIG_LWIP_LOOPBACK: 'y'
CONFIG_LWIP_TCP: 'y'
CONFIG_LWIP_UDP: 'y'
CONFIG_LWIP_RAW: 'y'
CONFIG_LWIP_WND_SCALE: 'y'
CONFIG_LWIP_TCP_KEEPALIVE: 'y'
CONFIG_LWIP_THREADS: 'y'
CONFIG_LWIP_HEAP: 'y'
CONFIG_LWIP_SOCKET: 'y'
CONFIG_LWIP_AUTOIFACE: 'y'
CONFIG_LWIP_IPV4: 'y'
# IPv6 off on purpose: with AF_INET6 available chrony prefers the (unreachable) v6 addresses of upstream servers and never syncs;
CONFIG_LWIP_IPV6: 'n'
CONFIG_LWIP_DHCP: 'y'
CONFIG_LWIP_DNS: 'y'
CONFIG_LWIP_NUM_TCPCON: 64
CONFIG_LWIP_NUM_TCPLISTENERS: 64
CONFIG_LWIP_ICMP: 'y'
libelf:
source: https://github.com/unikraft/lib-libelf.git
version: staging

targets:
- fc/x86_64
- qemu/x86_64
64 changes: 64 additions & 0 deletions library/chronyd/4.8/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# chronyd 4.8

This directory contains the definition for the `unikraft.org/chronyd:4.8` image,
an NTP server running [chrony](https://chrony-project.org/) in binary
compatibility mode.

To run this image, [install Unikraft's companion command-line toolchain `kraft`](https://unikraft.org/docs/cli)
and then you can run:

```console
kraft run -M 16M -p 1123:123/udp --plat qemu --arch x86_64 unikraft.org/chronyd:4.8
```

If the `--plat` argument is left out, it defaults to `qemu`.
If the `--arch` argument is left out, it defaults to your system's CPU architecture.

The `/udp` suffix is required: `kraft` forwards TCP when no protocol is given,
and NTP queries then get no reply from an otherwise healthy server. Host port
123 needs root to bind, so the example uses a high port; serve real clients with
`-p 123:123/udp` as root.

Wait ~20 s after boot for the upstream sync (iburst), then query the server:

```console
python3 test.py
```

It reports the stratum, the reference ID of the selected upstream and the served
time, exiting non-zero until chronyd has synchronized.

## Building from source

To build the image locally, clone this repository and `cd` into this directory.
Then run:

```console
kraft build --plat qemu --arch x86_64 .
```

To run the locally built image, use `.` (_dot_, the current directory) as the
final argument:

```console
kraft run -M 16M -p 1123:123/udp --plat qemu .
```

## Notes

- chrony is compiled from source with `HAVE_RECVMMSG` removed from `config.h`.
Unikraft has no `recvmmsg` syscall and the stock Alpine binary has no runtime
fallback, so it busy-loops on a socket it can never drain.
- The configuration is baked in at build time from
[conf/chrony.conf](conf/chrony.conf); a unikernel has no shell to generate one
at startup. Edit it and rebuild to change servers.
- chronyd runs with `-x`, so it never adjusts the guest clock, whose set/adjust
syscalls are stubs on Unikraft. It still serves corrected time to clients.
- [conf/chrony.conf](conf/chrony.conf) sets `allow all`, so the instance answers
any client that can reach the port. Narrow it (`allow 192.168.0.0/16`) before
exposing it to an untrusted network.

## See also

- [How to run unikernels locally in Unikraft's Documentation](https://unikraft.org/docs/cli/running).
- [chrony's documentation](https://chrony-project.org/documentation.html)
Loading
Loading