# Time

---

# Keeping the time

_Peios / Using Peios / Time_

> How a Peios machine knows what time it is — timed, authenticated network sources, and why the default is three independent operators rather than one.

The machine's clock is kept by **timed**, and timed is the only process on
the machine permitted to set it.

That is not a figure of speech. Setting the clock needs
`SeSystemtimePrivilege`, which is granted to timed's service identity and
to nothing else, so "what on this machine can change the time" has a
one-line answer that an administrator can check.

## Why this is worth caring about

A wrong clock does not look like a wrong clock. It looks like:

- certificates that are expired, or not yet valid, and TLS that fails for
  reasons that make no sense;
- Kerberos tickets refused across a domain, because Kerberos treats a
  clock skew as evidence of a replay;
- logs from two machines that cannot be put in order, which is exactly
  when you most need them to be.

So time is not a convenience. It is something several other things quietly
depend on, and an attacker who can move it has more than a wrong clock.

## Sources are not trusted individually

timed polls several servers and does **not** believe any of them. Each one
reports not a time but an *interval* it promises the true time lies
within, and timed looks for the largest set of intervals that overlap. A
server outside that overlap is a **falseticker** and is discarded, however
confident it sounded.

The consequence is worth stating, because it is what makes the scheme
work: a server that claims great accuracy and is wrong excludes *itself*.
Its interval is narrow, so it cannot reach the agreement. Overclaiming is
the one lie the arrangement punishes automatically.

With three independent sources this survives one liar. With two that
disagree there is no way to tell which is right, and timed reports itself
unsynchronised rather than guessing — which is why the shipped default is
three operators and why you should configure at least three of your own.

## Every source is authenticated

By default timed will not use a source that cannot prove who it is. That
is **NTS** (RFC 8915): a TLS handshake on port 4460 establishes a pair of
keys, and every NTP packet afterwards carries a tag computed with them.
Forging a reply requires the key.

The certificate is validated against [the machine's trust
store](/peios/using-peios/trust/overview.md), over trustd's socket — so a certificate
authority you distrust is distrusted here too, immediately, without timed
needing to be restarted.

On a network that blocks port 4460 the honest outcome is a machine that
says it is unsynchronised. `AllowUnauthenticated` exists for networks
where that is not acceptable; see [configuring
sources](/peios/using-peios/time/configuring-sources.md) for what you give up.

## The default sources

A machine that has been told nothing uses four names in our own zone:

```
0.time.peios.org    →  time.cloudflare.com
1.time.peios.org    →  nts.netnod.se
2.time.peios.org    →  ptbtime1.ptb.de
3.time.peios.org    →  ptbtime2.ptb.de
```

Three independent operators in three jurisdictions — a commercial network,
a Swedish national infrastructure operator, and Germany's national
metrology institute. Independence is what makes the votes worth counting:
three servers run by one operator would agree with each other about
anything.

The indirection through `time.peios.org` is deliberate. If an operator
withdraws its service, that is a DNS change rather than a new image for
every machine.

## Two circles, and how they are broken

Both are worth knowing about, because both look like bugs when you meet
them.

**NTS needs TLS; TLS needs a clock.** A machine whose battery has died
boots reading 1970, and every certificate it holds is "not yet valid", so
it cannot complete the handshake that would tell it the real time. timed
breaks this by refusing to let the clock sit below **its own build
timestamp** — every certificate in the shipped trust store was valid at
that moment, by construction. The clock is wrong, but it is wrong in a way
that TLS tolerates, and the first poll fixes it. `clock status` reports
the floor so that "my clock is being clamped" is visible rather than
mysterious.

**A fresh handshake needs a clock too.** So the cookies from the last one
are kept under `/var/state/timed`, and a reboot usually needs no handshake
at all.

## Steering, not jumping

Once the time is known, timed **slews** the clock: it changes the rate
slightly so the error is absorbed over the next few minutes. A great deal
of software quietly assumes the clock only goes forwards, and stepping it
backwards breaks timers, file timestamps and anything measuring a
duration.

The clock is stepped in exactly two situations:

- **at startup**, once, however large the correction — this is what lets a
  machine with a dead battery start correctly;
- **after fifteen minutes of consistent disagreement**, when the offset is
  too large to slew away. Fifteen minutes because a congested network or a
  server having a moment looks exactly like a genuine step until time
  passes, and stepping the clock on a transient is worse than being slow
  to correct a real one.

An offset larger than a thousand seconds appearing *after* the machine was
synchronised is refused outright and logged loudly. A machine that far out
has a problem a time client should not paper over.

## Learning the crystal

The clock is a crystal running at the wrong rate — consistently wrong, by
some tens of parts per million. timed learns that rate and writes it to
`/var/state/timed/drift`, so the next boot starts already correcting for
it and is within milliseconds after one poll rather than after twenty.

It is also what holds the clock right when the network goes away: a
machine that knows its crystal is 12 ppm fast stays within a second for
about a day on its own.

## What timed does not do

**It is not a time server.** It listens on no network port and initiates
every conversation it takes part in. Serving time to other machines will
be a separate, unprivileged package — the client holds the dangerous
privilege, and a server is exposed to the whole network and needs no
privilege at all. Keeping them apart is the point.

**It does not touch the hardware clock directly.** Once timed reports the
clock as synchronised, the kernel writes it back to the RTC every eleven
minutes on its own.

---

# Configuring time sources

_Peios / Using Peios / Time_

> Point a machine at your own time servers, decide what it may believe, and understand what turning authentication off actually costs.

Everything about where a machine gets its time is under
`Machine\System\Time`. There is no configuration file, and `clock` writes
nothing — `reg` is how it is changed, so the key's own security descriptor
is the only gate.

## Naming your own servers

```
$ reg set Machine/System/Time Servers multi:'dc01.corp.example' \
                                            'dc02.corp.example' \
                                            'ntp3.corp.example'
$ clock reload
```

Each entry is a host, optionally `host:port`, optionally followed by
option words:

| Option | Meaning |
|---|---|
| `prefer` | Lead with this one when several agree. |
| `unauthenticated` (or `noauth`) | This source speaks plain NTP, not NTS. |

`prefer` breaks ties and nothing more. A preferred source that disagrees
with the majority is still discarded — a preference is a statement about
which server to lean on, not a licence to be wrong.

A misspelled option is an error and the entry is ignored with a warning in
the log, rather than silently doing nothing. `clock sources` will show the
entry missing.

> [!IMPORTANT]
> **Name at least three.** One source is believed because there is nothing
> to check it against. Two that disagree cannot be told apart, and the
> machine will report itself unsynchronised rather than pick one. Three is
> where a wrong server starts being outvoted instead of believed.

### Precedence is first match, not merge

```
Servers  →  the domain  →  DHCP, if enabled  →  the shipped fallback set
```

The first of those that names anything is the *whole* list. Setting
`Servers` does not add to the fallback set, it replaces it — a machine
told exactly which servers to use should not also be quietly talking to
somebody else's.

To go back to the shipped default, delete the value:

```
$ reg del Machine/System/Time Servers
$ clock reload
```

## Turning authentication off

`AllowUnauthenticated` is `0` by default: a source must prove who it is or
it is not used.

```
$ reg set Machine/System/Time AllowUnauthenticated dword:1
$ reg set Machine/System/Time Servers multi:'ntp.lan unauthenticated'
$ clock reload
```

Both are needed. The per-source word says which sources are plain, and the
machine-wide value says whether that is permitted at all — so setting
`AllowUnauthenticated` back to `0` really does turn everything off, rather
than leaving per-source exceptions behind.

What you give up is worth being clear about. Without NTS, the only thing
standing between you and a forged reply is that the server must echo the
exact 64 bits timed put in its request — a random number, so an attacker
who cannot *see* the request cannot guess it. An attacker who can see it
can forge freely and move your clock wherever they like.

The reason to think twice is that a wrong clock is not a self-contained
problem: certificate validity, Kerberos, and the ordering of your logs all
rest on it.

Reasonable uses: an isolated network with its own stratum-1 appliance; a
lab; a machine behind a firewall that blocks port 4460 where you control
the path anyway.

## DHCP time servers

`UseFromDHCP` is `0` by default, as on Windows.

```
$ reg set Machine/System/Time UseFromDHCP dword:1
```

They are used only when `Servers` is absent, and only without
authentication. Off by default because on a network you do not control the
DHCP server's idea of the time is the attacker's idea of the time — and
turning this on is a statement that you trust every network this machine
will ever join. That is a reasonable statement for a machine that only
ever attaches to one you run.

## Poll intervals

```
MinPoll   default 6    64 seconds
MaxPoll   default 10   about 17 minutes
```

Both are powers of two seconds, and both are clamped to the range 4 to 17.
timed lengthens the interval towards `MaxPoll` while the clock is being
held steadily and shortens it when the offset starts moving.

There is rarely a reason to change either. Lowering `MinPoll` makes
recovery from a disturbance faster and costs the servers more traffic; a
long interval measures the crystal's *rate* better than a short one
measures its phase, and it is the rate estimate that holds the clock right
between polls anyway.

The floor of 4 is not negotiable. A configuration error must not be able
to turn a machine into a nuisance at somebody else's public server.

## Doing it from a domain

Every value here is an ordinary registry write, so distributing time
policy to a fleet is an ordinary policy push. There is no separate
mechanism and nothing timed-specific to learn.

## When something is wrong

```
$ clock sources
  source                   state        auth   str reach     offset     delay     last
* 0.time.peios.org         system-peer  nts      3   377   -1.204ms   14.2ms       31s
+ 1.time.peios.org         candidate    nts      2   377   -0.918ms   22.7ms       44s
x 2.time.peios.org         falseticker  nts      2   377   +4.102s    18.1ms       12s
  3.time.peios.org         unreachable  nts     16     0       +0ns       0ns         -
    NTS-KE failed: connecting to 192.0.2.9:4460: Connection refused
```

- `falseticker` — this server disagrees with the others. It is the one to
  go and look at, and the single most useful thing a time client can tell
  you.
- `unreachable` — no reply for eight polls. The note says why.
- `reach` is the last eight polls in octal: `377` is eight for eight, and
  anything else says how recently one was missed.

---

# The clock command

_Peios / Using Peios / Time_

> Every verb of the clock command, what each field of its output means, and what its exit statuses tell a script.

`clock` reads over timed's socket and prints. It **writes nothing** —
time policy is registry configuration, and `reg` is how a registry value is
set, so there is no second permission model to keep in step with the
first. The one verb that acts is `reload`.

It is called `clock` and not `time` because `time` is a shell keyword:
`time status` would run `status` and report how long it took.

## Verbs

| Command | Reads | Acts |
|---|---|---|
| `clock status` | socket | — |
| `clock sources` | socket | — |
| `clock reload` | — | asks timed to re-read `Machine\System\Time` |

`status` is the default, so bare `clock` is `clock status`.

## Status

```
$ clock status
generation   118
state        synchronised
following    1.time.peios.org (stratum 3)
offset       -412.0us
frequency    -12.750 ppm
jitter       +180.3us
accuracy     within 31.4ms
  root delay      28.2ms
  root dispersion 4.10ms
sources      4 configured, 3 contributing
updates      118 (last 22s ago)
stepped      +2.1d in total since start
floor        1788142329 (the build timestamp; the clock is never set below it)
```

**state** is the field to read first:

| State | Meaning |
|---|---|
| `synchronised` | Normal. |
| `settling` | Being steered, but the frequency estimate is still converging. Usual for the first few minutes after boot. |
| `spike` | A large offset has appeared and is being timed to see whether it is real. The clock is deliberately untouched meanwhile. |
| `unsynchronised` | Nothing is believed and the clock is free-running. |

**accuracy** is the honest bound: how wrong this machine's time might be,
with every uncertainty between here and the reference clock added up. It
is the number a Kerberos deployment cares about, and the number to check
before blaming a clock skew on something else.

**frequency** is what the crystal is doing, in parts per million, and it is
persistent — it is written to `/var/state/timed/drift` and read back at the
next boot. Tens of ppm is an ordinary machine. Approaching ±500 means the
hardware is at the edge of what the discipline can correct.

**stepped** is non-zero after a boot on a machine whose clock was wrong,
which is normal. It growing *later* is not, and means something is
repeatedly moving the clock.

**floor** is the build timestamp. If the machine's clock reads exactly
this, nothing has told it the real time yet and it is sitting on the floor
so that TLS can work — see [the overview](/peios/using-peios/time/overview.md).

## Sources

```
$ clock sources
  source                   state        auth   str reach     offset     delay     last
* 1.time.peios.org         system-peer  nts      2   377   -0.918ms   22.7ms       44s
+ 0.time.peios.org         candidate    nts      3   377   -1.204ms   14.2ms       31s
- 3.time.peios.org         outlier      nts      2   377   +8.221ms   61.0ms       58s
x 2.time.peios.org         falseticker  nts      2   377   +4.102s    18.1ms       12s

* system peer  + candidate  - outlier  x falseticker  ? unusable
```

| Mark | State | Meaning |
|---|---|---|
| `*` | `system-peer` | Chosen. The machine takes its stratum and root figures from this one. |
| `+` | `candidate` | Agrees with the majority and contributes to the combined answer. |
| `-` | `outlier` | Agrees, but too noisy to be worth including. |
| `x` | `falseticker` | **Disagrees with the majority.** Go and look at this one. |
| `?` | `unusable` | Answering, but saying it is not synchronised itself. |
| ` ` | `unreachable` | Not answering. |

**auth** is `nts` or `none`. `none` means anyone on the path can forge that
source's replies.

**reach** is the last eight polls as an octal bitmask, newest in the low
bit — the classic NTP display. `377` is eight for eight; `376` means the
poll before last was missed; `0` means nothing for eight polls, at which
point the source's stored measurements are discarded rather than left to
vote with stale numbers.

**last** is how long ago the source answered. It exceeding the poll
interval by much is the first sign of trouble.

A source that is not contributing prints a note underneath saying why.

## Exit statuses

| Status | Meaning |
|---|---|
| `0` | Done. |
| `2` | Asked about something that is not there — no sources are configured. |
| `1` | It went wrong: timed is unreachable, or refused. |
| `64` | The command line was not understood. |

The separation of `2` from `1` is what lets a script tell "this machine has
no time sources" from "I could not find out".

## When reload is refused

```
$ clock reload
clock: not permitted
```

`reload` needs the control right on timed's control object. Reading does
not: what time the machine thinks it is, and how well it knows, is not a
secret, and a program deciding whether the clock is trustworthy enough to
validate a certificate should not need a privilege to find out.

The descriptor is `Machine\System\Time ControlSecurity`; by default SYSTEM
and Administrators may control, and everybody may query.
