sdwdate Development Notes

From Kicksecure
< Dev
Jump to navigation Jump to search

chrony as a replacement for sclockadj[edit]


Idea: use sdwdate to fetch the time. But set the time using chrony (new method). Instead of using sclockadj (current method).

Problem with sclockadj is that it is using clock_settime which is causing issues. chrony is using adjtimex which will probably work better.

For that, chrony is configured in "offline mode". I.e. not contacting any remote servers for fetching the time. Chrony is supposed to be used for setting the time only which sdwdate should feed into chrony through its unix domain socket driver protocol.


1. Platform specific.

  • Debian: No special steps required.
  • Qubes: Required in Qubes only.

sudo touch /var/run/qubes-service/clocksync

2. Remove all other time synchronization daemons.

This is to avoid other time synchronization daemons competing for any clock changes.

sudo apt purge ntp* sdwdate systemd-timesyncd

3. Install chrony.

Install chrony. To accomplish that, the following steps A. to D. need to be done.

A. Update the package lists.

sudo apt update

B. Upgrade the system.

sudo apt full-upgrade

C. Install the chrony package.

Using apt command line parameter --no-install-recommends is in most cases optional.

sudo apt install --no-install-recommends chrony

D. Done.

The procedure of installing chrony is complete.

4. Open file /etc/chrony/conf.d/30_sdwdate.conf in an editor with root rights.

(Kicksecure ™ inside Qubes: In Template)

This box uses sudoedit for better security. This is an example and other tools could also achieve the same goal. If this example does not work for you or if you are not using Kicksecure ™, please refer to this link.

sudoedit /etc/chrony/conf.d/30_sdwdate.conf

5. Paste.

## Enable offline mode. ## Disable Debian default pool. ## Which is configured in /etc/chrony/chrony.conf ## Does not work. #pool offline ## Open the refclock unix domain socket. ## Tested. This is functional. When running "chronyc sources" ## the socket is shown. Versus without the following line, the ## socket is not shown. refclock SOCK /var/run/chrony.ttyS0.sock

6. Save and exit.

7. Open file /etc/chrony/chrony.conf in an editor with root rights.

(Kicksecure ™ inside Qubes: In Template)

This box uses sudoedit for better security. This is an example and other tools could also achieve the same goal. If this example does not work for you or if you are not using Kicksecure ™, please refer to this link.

sudoedit /etc/chrony/chrony.conf

Out-comment or delete the following line.

pool iburst

Non-ideal. Would be nicer if configuration of offline mode could be done with a drop-in snippet in folder /etc/chrony/conf.d but this might not be possible.

8. Save and exit.

9. Restart chrony.

sudo systemctl restart chrony

10. No need to restart chronyd. [1]

11. Check if unix domain socket file /var/run/chrony.ttyS0.sock exists.

ls /var/run/chrony.ttyS0.sock

12. Watch chrony journal log.

sudo journalctl -f -u chrony

13. Verify that the unix domain socket driver is the only time source.

chronyc sources

Expected printout:

MS Name/IP address         Stratum Poll Reach LastRx Last sample               
#? SOC0                          0   4     0     -     +0ns[   +0ns] +/-    0ns

14. chronyc

Might be helpful during development.


chronyc help

chronyc tracking

15. See man pages.

16. GNU date usage examples:

Might be useful during development.

To show the current time in unixtime format:

date --utc "+%s"

Expected printout:


To convert a specific time/date to unixtime, use:

date --utc "+%s" --date "Sun 05 Jun 2022 09:22:28 AM UTC"

Expected printout:


17. TODO: task

Write a python3.9 or bash script that expects unixtime and writes into unix domain socket file /var/run/chrony.ttyS0.sock.

interface draft, TODO:

./unixtime-to-chrony-socket 1654420510

Or if the protocol also supports nanoseconds:

./unixtime-to-chrony-socket 1654420510.864804506

How can we write messages to the unix domain socket file?


does not work


Testing if chrony is really in "offline mode" is not trivial. pool offline did not work.

chrony needed 7 minutes between its start and showing that it is still communicating with remote time servers.

Jun 05 04:46:44 disp5211 systemd[1]: Starting chrony, an NTP client/server...
Jun 05 04:46:44 disp5211 chronyd[6133]: chronyd version 4.0 starting (+CMDMON +NTP +REFCLOCK +RTC +PRIVDROP +SCFILTER +SIGND +ASYNCDNS +NTS +SECHASH +IPV6 -DEBUG)
Jun 05 04:46:44 disp5211 chronyd[6133]: Frequency -0.343 +/- 9.106 ppm read from /var/lib/chrony/chrony.drift
Jun 05 04:46:44 disp5211 chronyd[6133]: Using right/UTC timezone to obtain leap second data
Jun 05 04:46:44 disp5211 chronyd[6133]: Loaded seccomp filter
Jun 05 04:46:44 disp5211 systemd[1]: Started chrony, an NTP client/server.
Jun 05 04:53:52 disp5211 chronyd[6133]: Selected source (
Jun 05 04:53:52 disp5211 chronyd[6133]: System clock TAI offset set to 37 seconds

Fingerprinting versus Security[edit]

An optimization problem. There are conflicting goals.

  • goal: Saving remote server resources → implementation: use HEAD (implemented)
  • goal: Fingerprinting resistance at remote server level. → implementation: Hide outdated user agent version numbers (not implemented)
    • similar to --user-agent curl or even a fake user agent.
    • Disadvantage being that this user agent makes use really unique.
    • Hiding version number might not help with security either. If a user was running a vulnerable version, an adversary could just try out every exploit independent of version number in user agent. The only solution are timely software upgrades by the user.
    • Tails-dev - Faking htpdate user agent worth it?
  • goal: Maximization of security of sdwdate (implemented) → implementation: use most remote code execution resistant backend (such as curl vs full web browser vs python3 requests) for url_to_unixtime
    • Use python3 requests since (implemented in memory safe python3 code) instead of curl (implemented in unsafe memory language C).
  • goal: Maximization of fingerprinting resistance from viewpoint of remote server → implementation: emulate being a full browser by using selenium or similar. (not implemented)
    • Convincingly emulating being a browser is impossible. Changing the user agent is insufficient. The only way of convincingly emulating being a browser is to actually run a browser.
    • Running a full browser inside sdwdate however would mess up security. Also very difficult to implement. Running a full browser GUI with an invisible X server and then somehow only exacting HTTP DATE header from the GUI. Also might actually worsen the web fingerprint of sdwdate since sdwdate would fetch and run any tracking (google analytics) script that the remote server has. Then also which browser? Tor Browser + selenium? Keep that browser updated. Seems way too complex to implement.

A compromise had to be made. Priority goals had to be chosen. Maximization of security of sdwdate and saving remote server resources was chosen.

sdwdate linux user account[edit]

  • sdwdate is a daemon designed to be executed by systemd (or other init systems if someone would contribute a port to such init systems).
  • Is designed to run under linux user account sdwdate.
  • Running as root is neither desired nor required.
  • Thanks to systemd, sdwdate can benefit from systemd sandboxing which would be unavailable if starting it as a user.
    • Using Linux capabilities. Has AmbientCapabilities=CAP_SYS_TIME and CapabilityBoundingSet=CAP_SYS_TIME. Therefore linux user account sdwdate can set the time without any requirement for root.


  1. Since it is just an alias in /lib/systemd/systemchrony.service.

Unfinished: This wiki is a work in progress. Please do not report broken links until this notice is removed, use Search Engines First and contribute improving this wiki.