Точное время в цифровом мире: Разбираемся с 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-серверов
Процесс синхронизации
Процесс синхронизации включает в себя обмен небольшими пакетами данных, содержащими информацию о времени и представляющими собой запросы и соответствующие ответы (см. рисунок 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
Рисунок 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 сервера включает в себя несколько этапов:
Установка пакета ntp.
Конфигурация файла ntp.conf.
Проверка настроек и работоспособности сервера.
Для установки пакета 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-клиент.