View Source

h1. Running a NTP server on SmartOS

h2. Background Information

I recently migrated my gateway to run ontop of SmartOS (OpenBSD in KVM zone), I used to feed it my serial GPS to get a better fix. This won't work with KVM.

So I decided to remove the GPS at first. That also did not work too great, I noticed a lot of drift\! Mostly due to the kvm clock not updating the actual hardware clock\!

My solution was to run ntpd inside a base zone. I had to give it some extra privilages, I also had to disable ntp in the global zone. In the end I even got my GPS to work, although not as wel as on OpenBSD.

h2. Disabling NTP in the global zone

To disable ntp in the global zone I added a custom SMF. You'll see some commented lines to get my GPS to work too.
If you also want to use a GPS in the zone, uncomment those lines.

<?xml version="1.0"?>
<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">

<service_bundle type='manifest' name='acheron:time-helper'>
<service name='acheron/time-helper' type='service' version='1'>
<create_default_instance enabled='true' />
<single_instance />
<dependency name='filesystem' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/system/filesystem/local'/>

<dependency name='system-log' grouping='optional_all' restart_on='none' type='service'>
<service_fmri value='svc:/network/ntp' />

<exec_method type='method' name='start' exec='/opt/custom/bin/time-helper' timeout_seconds='0' />
<exec_method type='method' name='stop' exec=':true' timeout_seconds='0' />

<property_group name='startd' type='framework'>
<propval name='duration' type='astring' value='transient' />

<stability value='Unstable' />

. /lib/svc/share/

## alias my gps device to /dev/gps0 to make ntpd happy
#/usr/bin/ln -sf /dev/cua/0 /dev/gps0
## disable global zone ntp
/usr/sbin/svcadm disable svc:/network/ntp:default


h2. Creating The Zone

"brand": "joyent",
"image_uuid": "c02a2044-c1bd-11e4-bd8c-dfc1db8b0182",
"hostname": "",
"alias": "ntp",
"autoboot": false,
"nowait": false,
"limit_priv": "default,+sys_time,+proc_priocntl,+proc_clock_highres",
"cpu_shares": 100,
"cpu_cap": 100,
"max_physical_memory": 128,
"quota": 2,
"delegate_dataset": true,
"zfs_io_priority": 100,
"zfs_root_compression": "lz4",
"nics": [
"nic_tag": "trunk",
"mtu": 1500,
"vlan_id": 30,
"mac": "00:15:00:xx:xx:xx",
"ip": "172.16.xx.2",
"netmask": "",
"allow_ip_spoofing": true

The *limit_priv* line is important, it allows for: higher resolution timers to be used, ntpd to change it's niceness, ntpd to change the hw clock.

If you want to use a gps device you need to include it in the zone, there is no way to do this through vmadm :(

Run zonecfg -z UUID and add the following:

add device
set match=/dev/cua/0
add device
set match=/dev/gps0

h2. Configuring NTPD

## general
driftfile /var/ntp/ntp.drift
logfile /var/log/ntp.log

## security
# default restrictions
restrict -4 default limited kod notrap nomodify nopeer noquery
restrict -6 default limited kod notrap nomodify nopeer noquery

# allow localhost to manage ntpd
restrict -6 ::1

# allow servers to reply to our queries
restrict source nomodify noquery notrap

## time sources
# local gps direct (mode 0 -> RMC, mode 2 -> GGA)
#server mode 2 minpoll 4 maxpoll 4 prefer
#fudge time2 0.7 refid GPS

# gpsd shared memory
#server minpoll 4 maxpoll 4
#fudge time1 -0.245 refid GPS stratum 15
#server minpoll 4 maxpoll 4 prefer
#fudge refid PPS

# remote time servers
pool burst iburst minpoll 4 maxpoll 4

Comment out 'local gps direct' when using a gps device

The last step is to get ntpd to run inside the zone, I hacked up the global zone's ntp smf manifest.

<?xml version='1.0'?>
<!DOCTYPE service_bundle SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type='manifest' name='export'>
<service name='network/ntp' type='service' version='0'>
<dependency name='network' grouping='require_any' restart_on='error' type='service'>
<service_fmri value='svc:/network/service'/>
<dependency name='routing' grouping='require_all' restart_on='none' type='service'>
<service_fmri value='svc:/network/routing-setup'/>
<exec_method name='start' type='method' exec='/srv/ntpd/smf/svc-ntp %m' timeout_seconds='600'>
<method_credential user='root' group='root' privileges='basic,!file_link_any,!proc_info,!proc_session,net_privaddr,proc_lock_memory,sys_time'/>
<exec_method name='restart' type='method' exec='/srv/ntpd/smf/svc-ntp %m' timeout_seconds='1800'>
<method_credential user='root' group='root' privileges='basic,!file_link_any,!proc_info,!proc_session,net_privaddr,proc_lock_memory,sys_time'/>
<exec_method name='stop' type='method' exec=':kill' timeout_seconds='60'/>
<property_group name='general' type='framework'>
<propval name='action_authorization' type='astring' value='solaris.smf.manage.ntp'/>
<propval name='value_authorization' type='astring' value='solaris.smf.value.ntp'/>
<instance name='default' enabled='true'>
<property_group name='config' type='application'>
<propval name='always_allow_large_step' type='boolean' value='true'/>
<propval name='debuglevel' type='integer' value='0'/>
<propval name='logfile' type='astring' value='/var/ntp/ntp.log'/>
<propval name='mdnsregister' type='boolean' value='false'/>
<propval name='no_auth_required' type='boolean' value='false'/>
<propval name='slew_always' type='boolean' value='false'/>
<propval name='value_authorization' type='astring' value='solaris.smf.value.ntp'/>
<propval name='verbose_logging' type='boolean' value='false'/>
<propval name='wait_for_sync' type='boolean' value='false'/>
<stability value='Unstable'/>
<loctext xml:lang='C'>Network Time Protocol (NTP) Version 4</loctext>
<manpage title='ntpd' section='1M'/>
<manpage title='ntp.conf' section='4'/>
<manpage title='ntpq' section='1M'/>

# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or
# See the License for the specific language governing permissions
# and limitations under the License.
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]

# Copyright 2009 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.

# Copyright (c) 2013 Joyent, Inc. All rights reserved.

# Standard prolog
. /lib/svc/share/


if [ -z $SMF_FMRI ]; then
echo "SMF framework variables are not initialized."

# Is NTP configured?
if [ ! -f /etc/inet/ntp.conf ]; then
echo "Error: Configuration file '/etc/inet/ntp.conf' not found." \
" See ntpd(1M)."

# Disable globbing to prevent privilege escalations by users authorized
# to set property values for the NTP service.
set -f

# Build the command line flags
shift $#
set -- -p /var/run/
# We allow a step large than the panic value of 17 minutes only
# once when ntpd starts up. If always_all_large_step is true,
# then we allow this each time ntpd starts. Otherwise, we allow
# it only the very first time ntpd starts after a boot. We
# check that by making ntpd write its pid to a file in /var/run.

val=`svcprop -c -p config/always_allow_large_step $SMF_FMRI`
if [ "$val" = "true" ]; then

# Auth was off by default in xntpd now the default is on. Better have a way
# to turn it off again. Also check for the obsolete "authenitcation" keyword.
val=`svcprop -c -p config/no_auth_required $SMF_FMRI`
if [ ! "$val" = "true" ]; then
val=`/usr/bin/nawk '/^[ \t]*#/{next}
/^[ \t]*authentication[ \t]+no/ {
printf("true", $2)
next } ' /etc/inet/ntp.conf`
[ "$val" = "true" ] && set -- "$@" --authnoreq

# Set up logging if requested.
logfile=`svcprop -c -p config/logfile $SMF_FMRI`
val=`svcprop -c -p config/verbose_logging $SMF_FMRI`
[ "$val" = "true" ] && [ -n "$logfile" ] && set -- "$@" -l $logfile

# Register with mDNS.
val=`svcprop -c -p config/mdnsregister $SMF_FMRI`
mdns=`svcprop -c -p general/enabled svc:/network/dns/multicast:default`
[ "$val" = "true" ] && [ "$mdns" = "true" ] && set -- "$@" -m

# We used to support the slewalways keyword, but that was a Sun thing
# and not in V4. Look for "slewalways yes" and set the new slew option.
val=`svcprop -c -p config/slew_always $SMF_FMRI`
if [ ! "$val" = "true" ]; then
val=`/usr/bin/nawk '/^[ \t]*#/{next}
/^[ \t]*slewalways[ \t]+yes/ {
printf("true", $2)
next } ' /etc/inet/ntp.conf`
[ "$val" = "true" ] && set -- "$@" --slew

# Set up debugging.
deb=`svcprop -c -p config/debuglevel $SMF_FMRI`

# Start the daemon. If debugging is requested, put it in the background,
# since it won't do it on it's own.
if [ "$deb" -gt 0 ]; then
/usr/sbin/ntpd ${NTPD_OPTIONS} "$@" --set-debug-level=$deb >/var/ntp/ntp.debug &
/usr/sbin/ntpd ${NTPD_OPTIONS} "$@"

# Now, wait for the first sync, if requested.
val=`svcprop -c -p config/wait_for_sync $SMF_FMRI`
[ "$val" = "true" ] && /usr/sbin/ntp-wait