{
  "id": "CB-UBU-SVC-015",
  "standard": "ubuntu",
  "version": "0.1.0",
  "url": "https://baselines.cirsec.org/b/ubuntu/v0.1.0/CB-UBU-SVC-015",
  "json": "https://baselines.cirsec.org/b/ubuntu/v0.1.0/CB-UBU-SVC-015.json",
  "yaml": "https://baselines.cirsec.org/b/ubuntu/v0.1.0/CB-UBU-SVC-015.yml",
  "profiles_effective": [
    "core",
    "server",
    "server-hardened",
    "desktop",
    "desktop-hardened"
  ],
  "license": "Text CC BY 4.0, code Apache-2.0",
  "attribution": "CIRSEC Baselines, https://baselines.cirsec.org",
  "control": {
    "id": "CB-UBU-SVC-015",
    "title": "Configure systemd-timesyncd with approved time servers and keep it running",
    "finding": {
      "title": "systemd-timesyncd uses built-in fallback servers or is not running",
      "description": "The clock follows whatever servers were compiled in, or is not synchronized at all."
    },
    "status": "active",
    "profiles": [
      "core"
    ],
    "risk": {
      "severity": "low"
    },
    "assessment": "automated",
    "tags": [
      "time",
      "ntp",
      "timesyncd"
    ],
    "applicability": {
      "facts": [
        "systemd-timesyncd"
      ]
    },
    "description": "`systemd-timesyncd` is a simple SNTP client. Its servers are set with `NTP=` in\n`/etc/systemd/timesyncd.conf` or in a drop-in under `/etc/systemd/timesyncd.conf.d/`. Without\n`NTP=`, it only uses the `FallbackNTP=` list compiled into the package. The service must be\nenabled and running.\n",
    "rationale": "Explicit servers make the time source a documented choice: internal servers for isolated\nnetworks, or a public pool the organization trusts. A stopped client lets the clock drift,\nwhich breaks log correlation and time-based authentication.\n",
    "impact": "The system contacts the configured servers on UDP port 123; the firewall must allow it. When\nthe default list is kept, the server is `ntp.ubuntu.com`, run by Canonical.\n",
    "notes": "The check reads the merged configuration with `systemd-analyze cat-config` and keeps the last\n`NTP=` line, which is the one systemd applies. Ubuntu ships no `NTP=` line (only the commented\n`FallbackNTP=ntp.ubuntu.com`), so the control fails on a fresh install of 20.04 to 24.04. On\n26.04, chrony replaces systemd-timesyncd and the control is not applicable. The fix writes `/etc/systemd/timesyncd.conf.d/60-cirsec.conf`.\n\n**Check and swap the default servers.** `time_servers` defaults to `ntp.ubuntu.com`, the\nserver Ubuntu already falls back to, so that the fix works everywhere. It is not an approved source: replace it in the tailoring file\nwith your organization's own time servers (internal NTP, domain controllers, or a provider you\ntrust) before applying the fix in production.\n",
    "setting": {
      "location": "/etc/systemd/timesyncd.conf.d/60-cirsec.conf",
      "default_value": "No NTP= line (compiled-in fallback servers)",
      "recommended_value": "NTP={{ time_servers }}"
    },
    "parameters": [
      {
        "name": "time_servers",
        "type": "string",
        "description": "Space-separated list of approved NTP servers, written by the fix.",
        "default": "ntp.ubuntu.com"
      }
    ],
    "audit": {
      "manual": "1. Run `systemd-analyze cat-config systemd/timesyncd.conf`. The last `NTP=` line must list at\n   least one approved server.\n2. `systemctl is-enabled systemd-timesyncd` must print `enabled` and\n   `systemctl is-active systemd-timesyncd` must print `active`.\n",
      "logic": "all",
      "checks": [
        {
          "id": "servers",
          "engine": "shell",
          "description": "Time servers set with NTP= in the merged configuration.",
          "spec": {
            "script": "o=$(systemd-analyze cat-config systemd/timesyncd.conf 2>/dev/null | grep -E '^[[:space:]]*NTP=' | tail -n 1)\nv=$(printf '%s' \"$o\" | sed 's/^[[:space:]]*NTP=[[:space:]]*//')\necho \"NTP=${v:-<not set>}\"\n[ -n \"$v\" ]\n"
          }
        },
        {
          "id": "enabled",
          "engine": "systemd-unit",
          "description": "systemd-timesyncd is enabled.",
          "spec": {
            "unit": "systemd-timesyncd.service",
            "state": "enabled"
          }
        },
        {
          "id": "active",
          "engine": "shell",
          "description": "systemd-timesyncd is running.",
          "spec": {
            "script": "systemctl is-active systemd-timesyncd.service || exit 1"
          }
        }
      ]
    },
    "remediation": {
      "manual": "Create `/etc/systemd/timesyncd.conf.d/60-cirsec.conf` containing:\n```\n[Time]\nNTP={{ time_servers }}\n```\nthen run `systemctl enable systemd-timesyncd` and `systemctl restart systemd-timesyncd`.\nCheck with `timedatectl timesync-status`.\n",
      "effects": {
        "restart": "service",
        "services": [
          "systemd-timesyncd"
        ],
        "disruption": "none",
        "lockout_risk": false
      },
      "rollback": "Delete `/etc/systemd/timesyncd.conf.d/60-cirsec.conf` and restart systemd-timesyncd.\n",
      "scripts": [
        {
          "id": "enabled",
          "from_check": "enabled"
        },
        {
          "id": "servers",
          "engine": "shell",
          "content": "f=/etc/systemd/timesyncd.conf.d/60-cirsec.conf\nwant=$(printf '# Written by CIRSEC Baselines CB-UBU-SVC-015\\n[Time]\\nNTP=%s' \"$CB_TIME_SERVERS\")\nif [ \"$(cat \"$f\" 2>/dev/null)\" != \"$want\" ]; then\n  cb_backup \"$f\"\n  mkdir -p /etc/systemd/timesyncd.conf.d\n  printf '%s\\n' \"$want\" >\"$f\"\nfi\nsystemctl is-active --quiet systemd-timesyncd.service || systemctl start systemd-timesyncd.service\n"
        }
      ]
    },
    "references": [
      {
        "title": "timesyncd.conf(5), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man5/timesyncd.conf.5.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "systemd-timesyncd.service(8), Ubuntu manual pages",
        "url": "https://manpages.ubuntu.com/manpages/noble/man8/systemd-timesyncd.service.8.html",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      },
      {
        "title": "Ubuntu Server documentation - Synchronize time using timedatectl and timesyncd",
        "url": "https://documentation.ubuntu.com/server/how-to/networking/timedatectl-and-timesyncd/",
        "publisher": "Canonical",
        "kind": "vendor-doc",
        "accessed": "2026-10-19"
      }
    ],
    "mappings": [
      {
        "framework": "nist-800-53r5",
        "framework_version": "5.1.1",
        "id": "AU-8(1)",
        "relation": "partial"
      }
    ],
    "threats": [
      {
        "framework": "mitre-attack",
        "id": "T1070.006"
      }
    ],
    "related_controls": [
      {
        "id": "CB-UBU-SVC-014",
        "type": "related"
      }
    ],
    "authors": [
      {
        "name": "Alice",
        "github": "Sn0wAlice"
      }
    ],
    "verified": [
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "20.04"
        ],
        "environment": "Proxmox VM, Ubuntu 20.04 cloud image, kernel 5.4.0-216-generic",
        "notes": "Stock image fails (no NTP=); fix writes timesyncd.conf.d/60-cirsec.conf with ntp.ubuntu.com, clock synchronized after reboot; not applicable once chrony replaced timesyncd."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "22.04"
        ],
        "environment": "Proxmox VM, Ubuntu 22.04 cloud image, kernel 5.15.0-198-generic",
        "notes": "Stock image fails (no NTP=); fix writes timesyncd.conf.d/60-cirsec.conf with ntp.ubuntu.com, clock synchronized after reboot; not applicable once chrony replaced timesyncd."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "24.04"
        ],
        "environment": "Proxmox VM, Ubuntu 24.04 cloud image, kernel 6.8.0-147-generic",
        "notes": "Stock image fails (no NTP=); fix writes timesyncd.conf.d/60-cirsec.conf with ntp.ubuntu.com, clock synchronized after reboot; not applicable once chrony replaced timesyncd."
      },
      {
        "date": "2026-10-19",
        "by": "Sn0wAlice",
        "versions": [
          "26.04"
        ],
        "environment": "Proxmox VM, Ubuntu 26.04 cloud image, kernel 7.0.0-38-generic",
        "notes": "Not applicable: chrony replaces systemd-timesyncd on 26.04."
      }
    ],
    "introduced_in": "0.1.0",
    "created": "2026-10-19",
    "updated": "2026-10-19"
  }
}