ntpd, NTPsec и chrony принимают просроченный файл високосных секунд, но chrony 4.6.1 отвергает файл без срока действия
В октябре 28-я Генеральная конференция по мерам и весам постановила прекратить добавление високосных секунд в UTC с 20 мая 2027 года. База часовых поясов tzdb отреагировала на это в выпуске 2026f от 27 октября: в примечаниях появилась строка «leap second tables no longer expire», а zic перестал учитывать срок действия таблицы. Однако файл leap-seconds.list, который tzdb распространяет вместе с данными о зонах и который используют NTP-демоны, по-прежнему имеет ограниченный срок действия. Строка #@ указывает, что он истекает 28 июня 2027 года. Будет ли IERS продлевать этот срок после отмены високосных секунд, пока неизвестно.
Я запустил классический ntpd, NTPsec и chrony с просроченным файлом, со свежим файлом при подменённой дате после 28 июня 2027 года и с файлом без строки срока действия. Просроченный файл принимают все три программы: ntpd и NTPsec загружают таблицу со смещением 37 секунд и записывают ошибку в журнал, а chrony выдаёт предупреждение, но не отвергает файл. Другая ситуация оказывается проблемнее. Файл без строки срока chrony 4.6.1 полностью отклоняет, а оба демона семейства ntpd либо вовсе его не загружают, либо считают просроченным с 1900 или 1970 года.
Зачем файлу срок действия
IERS сообщает о новой високосной секунде примерно за полгода в бюллетене C, после чего leap-seconds.list переиздают с той же периодичностью. Строка #@ задаёт срок действия файла: до указанной даты таблица считается актуальной. В базе знаний Meinberg это объясняется следующим образом: после истечения срока программа уже не может быть уверена, что новая секунда не была назначена. Последнюю високосную секунду добавили 1 января 2017 года; с тех пор UTC отстаёт от TAI на 37 секунд. Бюллетень C 72 от 6 июля 2026 года подтвердил, что в конце декабря добавления не будет. Текущий файл обновлён на основании этого бюллетеня и действует до 28 июня 2027 года.
В tzdb 2026f срок действия убрали из таблиц внутри файлов TZif, используемых зонами right/*. Это соответствует RFC 9636: таблицы, опубликованные после отмены, «SHOULD NOT expire». При этом в NEWS сам файл leap-seconds.list не упоминается. Его публикует IERS, а tzdb лишь включает в свой комплект. Ещё в выпуске 2023a скрипт leapseconds.awk научили обрабатывать файл, «that never expires; this might be useful if leap seconds are discontinued». Таким образом, возможны два сценария: IERS продолжит продлевать #@ либо опубликует файл без срока действия. Третий сценарий уже встречается на практике: файл в системе просто не обновили.
Источники при этом противоречат друг другу. Meinberg утверждает, что ntpd 4.2.8 и более новые версии «really check the expiration date of the leap second file, and stop using it after the file has expired». При буквальном прочтении это означает, что после 28 июня 2027 года демон потеряет таблицу у всех, кто не обновит tzdata. Я решил проверить, как демоны ведут себя на самом деле.
Методика проверки
Пакеты я взял из архивов: NTPsec 1.2.3 и chrony 4.6.1 из Debian 13, ntpd 4.2.8p15 из Ubuntu 22.04. Распаковывал их командой dpkg-deb -x в одноразовом контейнере и запускал без прав root. Проверку getuid() в ntpd обходил небольшим шимом через LD_PRELOAD. Системное время демоны не меняли: в контейнере отсутствует CAP_SYS_TIME. Дату после окончания срока подменял с помощью libfaketime внутри контейнера, не затрагивая часы хоста. Поскольку chrony проверяет срок действия файла только после синхронизации, в качестве сервера для него работал второй chronyd на loopback.
Для тестов использовались следующие файлы:
-
актуальный файл из tzdb со сроком действия до 2027-06-28;
-
файл из tzdata2023c со сроком действия до 2023-12-28;
-
актуальный файл при подменённых датах 2027-07-01 и 2030-01-01;
-
актуальный файл без строки
#@— с исходной подписью#hи без неё; -
актуальный файл со сроком до 2100-01-01 и пересчитанной подписью.
Подпись #h представляет собой SHA-1 от цифр в строках #$, #@ и строках данных. Чтобы случайно не нарушить её, я сначала пересчитал хеш исходного файла собственным скриптом и убедился, что результат совпадает с #h.
Просроченный файл: ошибка в журнале, но таблица загружена
Вывод классического ntpd при работе с файлом 2023 года:
leapsecond file ('/work/e2/old.list'): expired 1042 days ago
leapsecond file ('/work/e2/old.list'): good hash signature
leapsecond file ('/work/e2/old.list'): loaded, expire=2023-12-28T00:00:00Z last=2017-01-01T00:00:00Z ofs=37
ntpq: leap=11, leapsec=201701010000, expire=202312280000
Таблица загрузилась, а ntpq сообщает дату последней високосной секунды и срок действия файла. NTPsec выводит почти аналогичные сообщения: «expired less than 1042 days ago» и loaded … ofs=37. При использовании свежего файла с датой 1 июля 2027 года оба демона сообщают «expired 3 days ago», а с датой 1 января 2030 года — «918 days ago». В обоих случаях таблица загружается.
Следовательно, формулировка Meinberg «stop using it» применительно к этим версиям не означает потерю таблицы. В коде NTPsec процедура загрузки вообще не проверяет срок действия. Он используется в другом месте: функция leapsec_add_dyn позволяет добавить секунду, объявленную вышестоящими серверами, только после истечения срока таблицы. Просроченный файл больше не считается окончательным источником сведений о будущих секундах, но данные о предыдущих 37 секундах сохраняются. Приём объявления от сервера я не проверял: для этого требуется вышестоящий сервер с установленным флагом високосной секунды.
Главное последствие просроченного файла — сообщения в журнале. Согласно коду, NTPsec после истечения срока раз в сутки записывает ошибку уровня LOG_ERR и продолжает делать это постоянно. Именно так выглядит журнал в отчёте об ошибке Ubuntu на Launchpad #2144453: «expired less than 68 days ago», спустя сутки — 69, ещё через сутки — 70. После синхронизации chrony выводит предупреждение Leap second list … needs update; судя по исходному коду leapdb.c, не чаще двух раз в сутки. До синхронизации, в режиме chronyd -Q, предупреждения нет, поэтому первая проверка его не выявила.
|
файл |
ntpd 4.2.8p15 |
NTPsec 1.2.3 |
chrony 4.6.1 |
|---|---|---|---|
|
актуальный, срок до 2027-06-28 |
загружен, ofs=37 |
загружен, ofs=37 |
без сообщений |
|
срок истёк 2023-12-28 |
ошибка, загружен, ofs=37 |
ошибка, загружен, ofs=37 |
WARN «needs update» |
|
актуальный, дата 2027-07-01 |
ошибка, загружен, ofs=37 |
ошибка, загружен, ofs=37 |
WARN «needs update» |
|
без |
signature mismatch, не загружен |
signature mismatch, не загружен |
отклонён |
|
без |
загружен, срок 1900-01-01 |
загружен, срок 1970-01-01 |
отклонён |
|
срок до 2100-01-01, подпись пересчитана |
загружен |
загружен |
без сообщений |
Файл без срока действия: chrony отказывается его принимать
В chrony 4.6.1 обработка leapseclist завершается следующей проверкой:
/* Make sure the file looks sensible */
if (!feof(f) || !lsl_updated || !lsl_expiry)
goto error;
Если строка #@ отсутствует, значение lsl_expiry остаётся нулевым, и файл признаётся некорректным. Для обоих вариантов файла без срока в журнале появляются такие сообщения:
Failed to parse leap seconds list /work/e3/noexp.list
Leap second list /work/e3/noexp.list failed check, ignoring
Selected source 127.0.0.1
chrony продолжает синхронизировать время, но работает так, словно директива leapseclist в конфигурации отсутствует. Файл без срока с подписью, пересчитанной без строки #@, я не создавал. Однако chrony отклонил бы и его: приведённое условие проверяет наличие строки срока действия независимо от подписи.
Поведение ntpd зависит от подписи. Если удалить только #@, исходная подпись перестаёт совпадать: оба демона выводят signature mismatch и не загружают таблицу, а поля leapsec и expire исчезают из вывода ntpq. Если удалить и подпись, файл загружается, но срок становится нулевым — началом соответствующей эпохи. Для классического ntpd это 1900 год и сообщение «expired 46328 days ago», для NTPsec — 1970 год и «expired less than 20761 days ago». Такой «вечный» файл для ntpd ничем не лучше просроченного: ежедневная ошибка появляется уже с первого дня.
Без предупреждений все три демона приняли лишь вариант с далёкой датой в #@ и пересчитанной с её учётом подписью. Есть нюанс: классический ntpq отобразил срок в 2100 году как expire=196311251731. Причина — переполнение 32-битной NTP-эры, которая завершается 7 февраля 2036 года. При этом сам демон загрузил дату правильно: в журнале указано expire=2100-01-01.
Как проверить файл в своей системе
Узнать срок действия установленного файла можно одной командой. В строке #@ указано число секунд с 1900 года, поэтому необходима поправка:
date -u -d @$(( $(awk '/^#@/{print $2}' /usr/share/zoneinfo/leap-seconds.list) - 2208988800 )) +%F
Аналогичную проверку я провёл для пакетов tzdata из индексов Debian, Ubuntu и Alpine по состоянию на 3 ноября 2026 года. Для Debian выбирал последнюю версию из релизного кармана и -updates, для Ubuntu дополнительно учитывал -security, а для Alpine использовал ветку main:
|
выпуск |
tzdata |
срок |
|---|---|---|
|
Debian 11 |
2024a |
2024-12-28, истёк |
|
Debian 12 |
2026b |
2026-12-28 |
|
Debian 13 |
2026f |
2027-06-28 |
|
Ubuntu 20.04 |
2025b |
2025-12-28, истёк |
|
Ubuntu 22.04, 24.04, 26.04 |
2026e |
2027-06-28 |
|
Ubuntu 25.10 |
2026b |
2026-12-28 |
|
Alpine 3.19 |
2025b |
2025-12-28, истёк |
|
Alpine 3.20 |
2026b |
2026-12-28 |
|
Alpine 3.21–3.23, edge |
2026e, 2026f |
2027-06-28 |
В трёх выпусках файл уже просрочен, ещё в трёх его срок закончится через восемь недель — 28 декабря. Если образ собран исключительно из релизного кармана, без -updates, файл просрочен во всех версиях Ubuntu, кроме 26.04. В марте в баге #2144453 сообщалось об истечении срока во всех LTS-выпусках. Для 22.04 и 24.04 проблему уже исправили пакетом 2026e, для 20.04 — нет.
Выводы
Если IERS продолжит продлевать срок каждые полгода, привычный порядок не изменится: достаточно обновлять tzdata. Если продление прекратится и файл истечёт 28 июня 2027 года, то, согласно моим тестам, ntpd и NTPsec сохранят таблицу со смещением 37 секунд, а chrony не отвергнет файл. При этом ntpd и NTPsec продолжат ежедневно записывать ошибку в журнал, а chrony — выводить предупреждение не чаще двух раз в сутки. Пока не появится файл с продлённым сроком, настроенный на эти сообщения мониторинг будет срабатывать без практической пользы.
Опаснее другой сценарий. Если будет выпущен «вечный» файл без строки #@, то после обновления tzdata chrony 4.6.1, использующий для чтения файла директиву leapseclist, отвергнет его и останется без таблицы. У ntpd ошибка появится уже в первый день. Если же chrony настроен с директивой leapsectz, он берёт данные о секундах из зоны tzdb, и этот файл на него не влияет. После подобного обновления стоит проверить журнал chrony на наличие сообщения Failed to parse leap seconds list. Тем, кто готовит такой файл, следует учесть: все три демона без предупреждений приняли только вариант с далёкой датой и пересчитанной подписью. Если важна корректность вывода ntpq в классическом ntpd, срок лучше назначать до 2036 года.
За пределами проверки остались синхронизация ntpd с реальным вышестоящим сервером и приём объявленной им секунды при просроченной таблице, значение tai= в выводе ntpq — ntpd публикует его только после синхронизации — и поведение других версий демонов. Все тесты проведены 1 и 3 ноября 2026 года в контейнере на базе образа python:3.15 (Debian 13).
Как один нейрон может повлиять на личность
Почему советская модель гражданской авиации с большими экипажами и бортинженерами лучше
Правильно ли мы спрашиваем о тёмной материи? Пять неоспоримых фактов о её существовании
Первые шаги в мире микроконтроллеров: основы цифровых схем
Мышь из томографа и мозг из атласа: как я создаю 3D-модели, которые можно разобрать руками перед веб-камерой
Три нейрона заставили Qwen назвать себя кофейным зерном
Гендерное неравенство: 120 лет до паритета, 38% женщин в IT и половина потенциала ВВП
Учёные: прошлые движения цены могут предсказать её дальнейшее направление — Journal of Financial Economics