Точное время в цифровом мире: Разбираемся с NTP

Содержание

  • Введение

  • Что такое NTP?

  • Как работает NTP?

  • Процесс синхронизации

  • Время практики

Введение

Сегодня точное время необходимо для корректной работы компьютерных систем, обеспечения согласованности данных, безопасности транзакций и координации действий в распределенных системах. Предлагаю вам сегодня разобраться в механизме синхронизации времени. Рассмотрим, что такое NTP, как он работает, как проходит синхронизация времени, а также попрактикуемся на конкретной задаче.

Что такое NTP?

В 1985 году профессором Делавэрского университета Дэвидом Л. Милсом был создан протокол NTP (Network Time Protocol), который является одним из наиболее распространенных и эффективных методов синхронизации времени. Протокол работает по алгоритму согласования данных Марзулло, согласно которому оценка точного времени выполняется на основании ряда источников времени с разной степенью точности, осредненных по времени и поступающих с разных NTP серверов. Протокол имеет несколько версий, последним является NTPV4. Для небольших сетей или устройств, где небольшие изменения в синхронизации времени не вызывают существенных сбоев, была разработан протокол SNTP — несколько упрощенная версия NTP.

Протокол NTP (а именно его последняя версия NTPv4) описан в RFC5905. В нем описана базовая архитектура, протокол, конечные автоматы, структуры данных и алгоритмы.

Как работает NTP?

Протокол NTP работает по принципу клиент-сервер, где NTP-клиенты отправляют запрос на получение времени у NTP-серверов. Серверы, в свою очередь, получают точное время от высокоточных источников, таких как атомные часы или GPS, и отправляют ответ клиенту, на котором устанавливается точное время. Передача от одного устройства к другому происходит с использованием сетевого протокола UDP.

Все NTP-сервера образуют иерархическую структуру, состоящую из уровней, называемых «stratum» (см. рисунок 1). Серверы уровня 1 напрямую подключены к источникам точного времени и служат основными источниками для серверов уровня 2, серверы уровня 2 — для уровня 3 и т.д. Клиенты могут обращаться к нескольким серверам для получения времени, что повышает надежность синхронизации. Протокол также учитывает сетевые задержки и может постепенно корректировать время, чтобы избежать резких изменений. Кстати, недавно яндекс.станции уронили NTP-сервера рунета.

Рисунок 1. Иерархическая структура NTP-серверов

Рисунок 1. Иерархическая структура NTP-серверов

Процесс синхронизации

Процесс синхронизации включает в себя обмен небольшими пакетами данных, содержащими информацию о времени и представляющими собой запросы и соответствующие ответы (см. рисунок 2) между клиентом и сервером (или двумя серверами).

Рисунок 2. Процесс синхронизации клиента и сервера

Рисунок 2. Процесс синхронизации клиента и сервера

Чтобы определить смещение времени между системами и задержки, прошедшей с момента отправки запроса до получения ответа, в передаваемых пакетах используются три поля:

  • t1: Локальное время клиента в момент отправки запроса.

  • t2: Локальное время сервера при получении запроса.

  • t3: Локальное время сервера в момент отправки ответа.

Перед отправкой клиент фиксирует своё текущее время (метка времени t1) и сохраняет это значение в переменной.

Когда сервер получает пакет от клиента, он формирует ответный пакет. В этот пакет копируется значение времени отправки из полученного запроса, и записывается текущее время сервера (метка времени t2, момент получения запроса). После обработки запроса сервер добавляет в пакет своё текущее время (метка времени t3, момент отправки ответа) и отправляет его обратно клиенту.

Когда клиент получает ответ, он записывает время его получения (метка времени t4). Теперь у клиента есть все необходимые данные для вычисления смещения и задержки, связанной с передачей пакетов по сети.

Смещение времени (разница между временем сервера и клиента) вычисляется по формуле: dt = 0.5 * ((t2−t1) + (t3−t4)), а общее время передачи данных: t = (t4−t1) − (t3−t2). Используя эти данные, клиент выставляет у себя время.

Время практики

Попробуем настроить свой NTP-сервер в локальной сети и добиться синхронизации с ним клиента, который будет находится в этой же локальной сети. Для этого построим простую сеть, состоящую из двух виртуальных машин. Одна из них будет в роли NTP-сервера, другая — в роли NTP-клиента.

Будем использовать VMware Workstation Pro для запуска наших виртуальных машин, на каждой из которых будет установлена обычная ubuntu-22.04.4-desktop-amd64.

Настройка виртуальных машин сети

Ещё до запуска клиента и сервера изменим настройки сети для каждой из виртуальных машин (VM → Settings → Network Adapter). Создадим LAN Segment, который будет представлять собой виртуальную сеть (см. рисунок 3). Этот сегмент будет изолирован от других сетей и будет доступен только для виртуальных машин, которые подключены к нему. Виртуальные машины, подключенные к одному и тому же LAN Segment, могут обмениваться данными друг с другом так же, как если бы они находились в одной физической сети (см. рисунок 4). Они могут использовать IP-адреса для связи, и все стандартные сетевые протоколы (например, TCP/IP) будут работать.

Рисунок 3. Создание LAN Segment

Рисунок 3. Создание LAN Segment

Рисунок 4. Настройка сети виртуальных машин

Рисунок 4. Настройка сети виртуальных машин

Виртуальные машины должны быть настроены с уникальными IP-адресами в пределах одного сегмента, чтобы они могли корректно обмениваться данными. Для этого запустим каждую из виртуальных машин, откроем терминал и проделаем ряд команд для статической настройки IP-адресов нашей сети. Будем использовать утилиту nmcli (инструмент управления сетевыми подключениями NetworkManager в командной строке). Для понятности и однозначности восприятия будем использовать конкретный сетевой адрес, IP-адреса и прочие настройки далее (при большом желании их можно поменять на свои). Установка IP-адресов:

Для сервера:

$ nmcli connection modify "Wired connection 1" ipv4.address 192.168.10.1/24

Для клиента:

$ nmcli connection modify "Wired connection 1" ipv4.address 192.168.10.2/24

Далее метод установки IP-адреса и для клиента, и для сервера изменим на значение «вручную» (manual), чтобы изменения вступили в силу, деактивируем и затем активируем соединение:

$ nmcli connection modify "Wired connection 1" ipv4.method manual
$ nmcli connection down "Wired connection 1"
Connection 'Wired connection 1' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/10)
$ nmcli connection up "Wired conn
ection 1"
Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/11)

Проверим, что сеть настроена правильно с помощью утилиты ping.

С клиента на сервер:

$ ping 192.168.10.1 -c 4
PING 192.168.10.1 (192.168.10.1) 56(84) bytes of data.
64 bytes from 192.168.10.1: icmp_seq=1 ttl=64 time=0.507 ms
64 bytes from 192.168.10.1: icmp_seq=2 ttl=64 time=0.505 ms
64 bytes from 192.168.10.1: icmp_seq=3 ttl=64 time=0.423 ms
64 bytes from 192.168.10.1: icmp_seq=4 ttl=64 time=0.581 ms

--- 192.168.10.1 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3051ms
rtt min/avg/max/mdev = 0.423/0.504/0.581/0.055 ms

С сервера на клиент:

$ ping 192.168.10.2 -c 4
PING 192.168.10.2 (192.168.10.2) 56(84) bytes of data.
64 bytes from 192.168.10.2: icmp_seq=1 ttl=64 time=0.373 ms
64 bytes from 192.168.10.2: icmp_seq=2 ttl=64 time=0.445 ms
64 bytes from 192.168.10.2: icmp_seq=3 ttl=64 time=0.471 ms
64 bytes from 192.168.10.2: icmp_seq=4 ttl=64 time=0.453 ms

--- 192.168.10.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3079ms
rtt min/avg/max/mdev = 0.373/0.435/0.471/0.037 ms

Установка и настройка NTP-сервера

В Linux NTP-сервер функционирует на основе пакета ntp, который необходимо установить. Этот пакет доступен в репозиториях различных дистрибутивов Linux. В зависимости от конфигурации, демон NTP (ntpd) может работать как сервер, предоставляющий точное время другим хостам, или как клиент.

Настройка NTP сервера включает в себя несколько этапов:

  1. Установка пакета ntp.

  2. Конфигурация файла ntp.conf.

  3. Проверка настроек и работоспособности сервера.

Для установки пакета ntp в Linux воспользуемся менеджером пакетов apt:

$ sudo apt update && sudo apt install ntp

После установки запустим службу NTP:

$ sudo service ntp start

Настроим наш NTP-сервер. Для этого изменим в любом редакторе файл /etc/ntp.conf, который содержит настройки для работы сервера. В нем можно определить уровень сервера, указать ключи шифрования для обеспечения безопасности и задать правила доступа к клиентам.

$ sudo nano /etc/ntp.conf

Рассмотрим содержимое этого файла.

driftfile /var/lib/ntp/ntp.drift

В этой строке указывается файл для хранения информации о частоте смещения времени. В этом файле хранится значение, получаемое в результате предшествующих корректировок времени. Если внешние NTP-серверы по той или иной причине становятся недоступными, значение будет взятого из него.

Далее указывается файл, в который будут сохраняться логи синхронизации:

logfile /var/log/ntp.log

В конфигурационном файле указывается список NTP-серверов, с которыми наш сервер будет осуществлять синхронизацию. Могут быть указаны следующие сервера:

server 0.ubuntu.pool.ntp.org
server 1.ubuntu.pool.ntp.org
server 2.ubuntu.pool.ntp.org
server 3.ubuntu.pool.ntp.org

Уровень сервера 0 — 3 определяет его место в иерархии NTP серверов. Чем меньше значение стратума, тем ближе сервер к источнику времени с высшим уровнем.

Изменим (либо можно просто закомментировать) эти строчки, добавив следующий пул NTP-серверов:

server 0.ru.pool.ntp.org
server 1.ru.pool.ntp.org
server 2.ru.pool.ntp.org
server 3.ru.pool.ntp.org

Чтобы повысить точность синхронизации используем опцию iburst, она отвечает за отправку на сервер для синхронизации несколько пакетов вместо одного:

server 0.ru.pool.ntp.org iburst
server 1.ru.pool.ntp.org iburst
server 2.ru.pool.ntp.org iburst
server 3.ru.pool.ntp.org iburst

Также сделаем указанный пул серверов приоритетным для синхронизации, используя опцию prefer:

server 0.ru.pool.ntp.org iburst
server 1.ru.pool.ntp.org iburst prefer
server 2.ru.pool.ntp.org iburst
server 3.ru.pool.ntp.org iburst

Чтобы наш NTP-сервер не стал инструментов в руках злоумышленников, которые научились использовать NTP-сервера для усиления трафика в DDoS-атаках, установим ограничения на доступ для внешних клиентов:

restrict -4 default kod notrap nomodify nopeer noquery limited
restrict -6 default kod notrap nomodify nopeer noquery limited
  • restrict -4 default:

    • -4: Ограничения применяются к IPv4-адресам.

    • default: Ограничения применяются ко всем клиентам, которые не имеют более специфичных правил.

  • restrict -6 default:

    • -6: Ограничения применяются к IPv6-адресам.

    • default: Аналогично, это правило применяется ко всем клиентам IPv6.

  • kod: Включение использования «Kiss-o-Death» сообщений для ограничения частоты запросов от клиентов.

  • notrap: Запрет использования команды trap.

  • nomodify: Запрет клиентам изменять настройки сервера NTP.

  • nopeer: Запрет клиентам устанавливать пировые соединения с сервером.

  • noquery: Запрет клиентам выполнять запросы к серверу для получения информации о его состоянии.

  • limited: Ограничение количества запросов, которые сервер будет обрабатывать от клиентов.

Чтобы с NTP-сервером мог синхронизироваться с NTP-клиентом из локальной сети, добавим в конфигурационный файл следующую строку:

restrict 192.168.10.0 mask 255.255.255.0 nomodify notrap

Для того, чтобы сервер мог обмениваться NTP данными сам собой вносим строки:

restrict 127.0.0.1
restrict ::1

Сохраняем файл и возвращаемся в терминал. Теперь, чтобы наши изменения вступили в силу, перезапустим сервер и проверим его статус. Если все настроено верно, то получим примерно следующее:

$ sudo service ntp restart
$ sudo service ntp status
● ntp.service - Network Time Service
     Loaded: loaded (/lib/systemd/system/ntp.service; enabled; vendor preset: enabled)
     Active: active (running) since Sat 2024-12-21 21:53:13 MSK; 7s ago
       Docs: man:ntpd(8)
    Process: 64671 ExecStart=/usr/lib/ntp/ntp-systemd-wrapper (code=exited, status=0/SUCCESS)
   Main PID: 64677 (ntpd)
      Tasks: 2 (limit: 4551)
     Memory: 1.3M
        CPU: 27ms
     CGroup: /system.slice/ntp.service
             └─64677 /usr/sbin/ntpd -p /var/run/ntpd.pid -g -u 129:137

...

server-virtual-machine systemd[1]: Started Network Time Service.

...

Последним шагом проверим работоспособность нашего NTP-сервера, выполнив команду:

$ ntpq -pn
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
 51.250.107.88   .STEP.          16 u  193   64    0    0.000   +0.000   0.000
*85.113.58.74    89.109.251.25    3 u    4   64    1  100.311   +2.282  10.038
+192.36.143.130  .PPS.            1 u    3   64    1  105.163  +11.645  19.525
+188.124.37.90   194.190.168.1    2 u    4   64    1   81.926  +15.642   9.457
  • remote: Адрес удаленного NTP-сервера.

  • refid: Идентификатор референсного времени, с которым синхронизируется данный сервер.

  • st: Уровень (stratum) сервера.

  • t: Тип сервера:

  • when: Время (в секундах) с момента последнего запроса к серверу.

  • poll: Интервал опроса (в секундах) для этого сервера.

  • reach: Битовая маска, показывающая, были ли успешные запросы к серверу. Значение от 0 до 377 (восьмеричное представление). Если значение 0, это означает, что не было успешных ответов.

  • delay: Задержка (в миллисекундах) при получении ответа от сервера.

  • offset: Смещение (в миллисекундах) между временем на вашем клиенте и временем на сервере.

  • jitter: Изменчивость (в миллисекундах) времени, что указывает на стабильность соединения.

  • '*': Сервер выбран для синхронизации.

  • '+': Сервер, пригодный для обновления (с которым можно синхронизироваться).

  • '-': С сервером синхронизироваться не рекомендуется.

  • 'x': Сервер недоступен.

Настройка клиента для синхронизации с NTP-сервером

Следующие шаги мы выполним, чтобы обеспечить возможность постоянной синхронизации NTP-клиента с нашим NTP-сервером, который будет служить для него эталонным источником времени.

Устанавливаем NTP на системе клиента, аналогично нашему серверу:

$ sudo apt update && sudo apt install ntp

После установки запустим службу NTP:

$ sudo service ntp start

Также изменим файл конфигурации /etc/ntp.conf на клиенте, добавив наш сервер в качестве эталонного:

server 192.168.10.1 iburst prefer

Установим минимальный и максимальный интревал опроса NTP-сервера:

server 192.168.10.1 minpoll 4 maxpoll 10 iburst prefer

Значение 4 соответствует 16 секундам (2^4 = 16). Это означает, что клиент будет запрашивать время у сервера не реже чем раз в 16 секунд.

Значение 10 соответствует 1024 секундам (2^10 = 1024). Это означает, что клиент может запрашивать время у сервера не чаще чем раз в 1024 секунды.

Теперь чтобы наши изменения вступили в силу перезапустим демон:

$ sudo service ntp restart

Проверим работу клиента, если все настроено верно, то получим следующее:

$ ntpq -pn
     remote           refid      st t when poll reach   delay   offset  jitter
==============================================================================
*192.168.10.1    62.84.117.189    3 u    3   64   17    0.569   -1.016  14.298

Изменим дату на клиенте, чтобы проверить синхронизацию с сервером. Для этого воспользуемся командой date:

$ sudo date -s "2024-05-17 17:17:17"
Пт 17 мая 2024 17:17:17 MSK

Наконец проверим, что NTP-клиент синхронизируется с NTP-клиентом. Время на клиенте изменится с помощью демона ntpd, который работает в фоновом режиме и обеспечивает постоянную синхронизацию системного времени, для этого нам нужно просто подождать какое-то время или перезапустить систему,

Также, имея установленный пакет NTP, можно синхронизировать время принудительно и единоразово c помощью утилиты ntpdate. Она работает без настроенного файла конфигурации /etc/ntp.conf, достаточно просто от имени суперпользователя, указав IP-адрес NTP-сервера, выполнить команду:

$ sudo ntpdate 192.168.10.1

При возникновении ошибки с сокетом

При выполнении команды ntpdate может возникнуть примерно следующая ошибка:

ntpdate[2708]: the NTP socket is in use, exiting

Она возникает из-за того, что на вашей системе запущен NTP-демон, он использует сокет NTP (обычно порт 123). В этом случае ntpdate не сможет получить доступ к этому порту, так как он уже занят.

Чтобы это исправить, нужно остановить ntpd и выполнить команду снова:

$ sudo service ntp stop
$ sudo ntpdate 192.168.10.1
ntpdate[2898]: step time server 192.168.10.1 offset +19089349.973492 sec

Если все выполнено верно, то время на клиенте установится таким же, как и на сервере.

Таким образом, у нас получилось настроить свой NTP-сервер в локальной сети и синхронизировать с ним NTP-клиент.

© Habrahabr.ru