Page MenuHomeFreeBSD

Make inside<->outside chroot symlinks for all files on the ntpd command line

Authored by ian on Jun 24 2018, 2:54 AM.



When rc.d/ntpd sets up ntpd to run in a chroot, it currently makes a convenience symlink for /var/db/ntp.drift. Unfortunately, our default driftfile name is actually ntpd.drift. Further complicating matters, users can change the name and location of the file by overriding ntpd_flags in rc.conf.

This loops through all the options in ntpd_flags, and for each one that names a file or directory, it makes the corresponding inside<->outside convenience symlink. It silently ignores any errors making the links, since they're just for convenience and things like readonly filesystems could be involved.

Inspired by

Diff Detail

rS FreeBSD src repository - subversion
Lint Skipped
Unit Tests Skipped
Build Status
Buildable 17608

Event Timeline

Reworked to only try to make symlinks if there isn't already a file/dir/link at the target location.

cy requested changes to this revision.Jun 25 2018, 7:23 PM

What about the corresponding statements in ntp.conf? e.g.

leapfile "/var/db/ntpd.leap-seconds.list"
logfile "/var/log/ntpd"
keys "/var/db/ntp.keys"

This revision now requires changes to proceed.Jun 25 2018, 7:23 PM
In D15987#338950, @cy wrote:

What about the corresponding statements in ntp.conf? e.g.

leapfile "/var/db/ntpd.leap-seconds.list"
logfile "/var/log/ntpd"
keys "/var/db/ntp.keys"

Well, where I started with this was the PR that complained about rc.d/ntpd not linking the same file we set up in the default ntpd_flags. I just wanted a way to fix that problem for new users without breaking any existing setups where people had switched their ntpd_flags and filename to match what's in the script. It occurred to me to just use whatever files the flags are set for and then nothing breaks.

But, all of that was foolishly based on assuming the PR was about a real problem someone had. Now I'm not so sure, because I just tried to set ntpd_chrootdir to start figuring out what-all you really need to do to make chroot'd ntpd work. What I discovered was this:

root@rpi:~ # service ntpd onerestart
ntpd not running? (check /var/run/
pax: Unable to access clockctl <No such file or directory>
pax: WARNING! These file names were not selected:
Starting ntpd.
/usr/sbin/ntpd: The 'user' option has been disabled. -- built without --enable-clockctl or --enable-linuxcaps or --enable-solarisprivs

So now my thinking is that if this chroot'd mode requires build options we don't even use, and a clockctl device we don't even have a driver for, then maybe the right fix is to just nuke all this chrooted stuff in the script and call it done? The alternative would be to port the netbsd clockctl driver to freebsd and start building ntpd with --enable-clockctl.

clockctl would help. I'm in favour of that.

Given the amount of non-$JOB related computer time I have, I need to finish some of the projects I have on my plate before taking on another one. But my arm can be twisted if it's a quick win.

It's definitely worth running ntpd in a chroot. However symlinks, if pointing to an outside file the chroot (or jail) can update, could be used to exploit the host or break out of chroot or jail. I tend to avoid them in case I may have missed something.

The symlinks the script wants to set up link the inside-chroot files to the corresponding outside-chroot locations. I guess so you can do "vi /etc/ntp.conf" and be editing the real inside-chroot config file. It looks like the code we've got now is a straight import from netbsd in 2001 and untouched since then. Their code has been revised since then to do more setup of the chroot.

It looks like porting the clockctl driver should be quick and easy, and in general I'd much rather work on drivers than rc.d scripts, so I'll go mess with that for a while, then come back to this stuff. :)

Well, it turns out the clockctl driver isn't an elegant solution whereby ntpd uses ioctl() calls to manipulate the clock based on filesytem permissions. Instead it's closer to a horrible hack where the clock setting functions in netbsd's libc react to EPERM errors by opening the clockctl device and doing it that way. I want no part of that. There are even netbsd email threads about how fragile the scheme is because of O_CLOEXEC and the order ntpd does things. And what it enables is basically a semi-solution, because once the daemon drops privs it's unable to bind to priveleged sockets, so if an interface goes down/up or you switch to a different wifi network or something, ntpd stops working until you manually restart it.

Agreed, we don't want an inelegant solution.

Let's continue down the symlink path.

  1. Parsing command line arguments and ntp.conf statements to determine which symlinks to create. This should probably go into a function.
  1. Don't replace the destination file with a symlink if it already exists and is a regular file, The user has already set up the chroot.
  1. If the destination file is already a symlink, allow the user to select an option to replace it. Though I'm not entirely enamoured with this. A user may have previously created the symlinks and blowing them away would be a POLA infraction. This should be an option. Defaulted to not replace pre-existing symlinks.
  1. If no pre-existing files or links exist then link them.
  1. Log all actions to syslog. Creating or optionally replacing symlinks should be a auditable.

As with anything ntp, whatever we touch will undoubtedly cause someone to complain. I don't think I missed anything above. What are your thoughts?

I think it's all moot, and we simply don't support dropping priveleges and running in a chroot at all. Part of the reason for that is that ntpd itself inappropriately binds those two concepts together. It will not chroot unless it can also drop root privs afterwards, and it will not drop root privs without linux, solaris, or netbsd-clockctl mechanisms to set the time. IMO, that's bogus, I think ntpd could chroot to limit its access to the filesystem without dropping root privs, but that's not how it's coded now.

We could support running in a chroot as root without changing ntpd code at all, but it would require a much more complete chroot, one that had the necessary shared libs for example. It would maybe need more one-time setup than an rcscript could reasonably do.

Capsicumizing ntpd would solve the part of the problem related to rebinding after network interface changes, and maybe even things like permission to create/write stats and log files if that's an issue. But capsicum seems to support only fd-related permissions, I see no mechanism for process-oriented stuff like clock-setting.

So in terms of rc.d/ntpd, I think we're pretty free to do anything we want since what we've got now is 18 years old and has never worked from day one. We won't be perturbing anyone's existing working chroot setup if we change things. I just don't see a solution for nptd itself yet that would give the rcscript anything to do at all.

Okay, after some experimenting, here's what I've learned today...

We can support running in a chroot as root without changing anything except rc.d/ntpd (change -u ntpd:ntpd to -u 0:0; btw, we nave no 'ntpd' user right now).

We can support dropping root privs and running as ntpd:ntpd (and separately also choose to run in a chroot or not in that condition) by adding a really simply mac(4) module I wrote called mac_ntpd.ko, which enables the clock-changing privs, and binding to a priveleged port as non-root. GENERIC kernels are compiled with MAC support, so this would be available without rebuilding anything.

At this point I'm inclined to abandon this phab diff and open a new one with all the changes needed to run ntpd as non-root.


Thanks for all your work in this area.

I am abandoning this change in favor of D16050 which is a more complete solution.