VPSの時刻ずれを確認して直す方法
VPSの時刻ずれで2FAに入れない原因を確認します。chronycとtimedatectlの出力から同期停止や競合、UDP port 123の遮断を特定して修正します。
VPS の時刻がずれる理由
VPS の時刻がずれるのは、時刻を補正する仕組みが動作していないためです。カーネルは、少し速く、または少し遅く動作するハードウェアカウンターを基に時刻を数えます。時刻同期クライアントが動作していないと、この小さな誤差が時間の経過とともに大きくなります。仮想マシンには、もう1つ原因があります。ゲストは他のゲストと物理 CPU を共有しているため、スケジュールされていない間は時刻を数えられません。
現在の KVM ゲストでは、カウンター自体が本当の問題であることはほとんどありません。準仮想化された kvm-clock ソースは、ホストが維持する値を読み取るため、正常なゲストはホストの時刻に近い状態を保ちます。明らかにずれているクロックは、通常、もっと単純な理由でずれています。同期デーモンが動作していない、2つの同期デーモンが動作して競合している、または送信元の UDP port 123 がプロバイダーのネットワークから外へ出ていない、といった原因です。ゲストの時刻調整は、独自の発振器ではなく、ホストまたは NTP (network time protocol) から行われます。
実際に時計がずれると何が壊れるか
- TOTP(時間ベースのワンタイムパスワード)による2要素認証コードが一致しなくなり、パスワードと暗号鍵がどちらも正しいサーバーから締め出されます。
- 1分前に発行された証明書が拒否され、
curlはSSL certificate problem: certificate is not yet validを出力します。 apt updateはE: Release file for ... is not valid yet (invalid for another 1d 2h 3min 4s)のためリポジトリを拒否します。- スケジュールされた処理が誤った時刻に実行されます。また、時計が大きく進むと、あるジョブが2回実行される一方で、別のジョブがスキップされることがあります。
- 2台のサーバーのログを時系列に並べられず、インシデントのタイムラインを推測で組み立てる必要が生じます。
許容範囲は、多くの人が想定するより小さいものです。以下の数値は文書化されたデフォルト値であり、テストで測定した値ではありません。
The data behind this chart
[
{
"label": "TLS certificate boundary",
"tolerance_seconds": 0,
"documented_in": "RFC 5280"
},
{
"label": "chrony steps instead of slewing",
"tolerance_seconds": 1,
"documented_in": "Debian and Ubuntu chrony.conf"
},
{
"label": "TOTP code, one time step",
"tolerance_seconds": 30,
"documented_in": "RFC 6238"
},
{
"label": "Kerberos clock skew",
"tolerance_seconds": 300,
"documented_in": "MIT krb5 default"
}
]TOTPコードは、30秒ごとに進むカウンターから計算されます。多くの検証システムは、前後1ステップまで受け入れます。つまり、どちらの方向でも30秒のずれが許容範囲のすべてです。Kerberosはこれよりはるかに寛容で、デフォルトのずれの許容値は300秒です。証明書には許容がありません。固定された時刻と照合され、猶予時間は0秒です。そのため、時計が1秒進んでいるだけで、完全に有効な証明書が拒否されます。
3 つの時計と、重要なのはどれか
システムクロックが重要です。これはカーネルの CLOCK_REALTIME です。メモリ上で保持される、1970 年 1 月 1 日 UTC からの経過秒数であり、時刻を記録するすべての処理が読み取ります。ログ行、証明書の検証、TOTP コード、ファイルの更新時刻は、すべてこの時計を基にします。サーバーの時刻が間違っていると言うときは、通常この時計を指します。
ハードウェアクロックは RTC (real time clock) とも呼ばれ、マシンの電源が切れている間も動作する別のカウンターです。物理マシンでは、バッテリーで保持されるチップです。ゲスト内ではハイパーバイザーがエミュレートするため、ほぼホスト側の情報です。Linux は起動時に初期値として 1 回だけ読み取り、その後は独自のカウントを維持します。timedatectl は RTC time 行にこの値を表示します。VPS では、この行を基にトラブルシューティングしないでください。システムクロックの同期状態ではなく、ホストが認識している時刻を示すためです。コンテナには通常 /dev/rtc 自体がないため、hwclock --show は hwclock: Cannot access the Hardware Clock via any known method. で失敗します。
クロックソースは、これらの読み取りの間にカーネルがカウントに使用するものです。カーネルが選択したクロックソースは、次のコマンドで確認できます。
cat /sys/devices/system/clocksource/clocksource0/current_clocksource
cat /sys/devices/system/clocksource/clocksource0/available_clocksourceKVM では通常、kvm-clock と表示されます。これはホストが維持する値を読み取ります。そのため、NTP クライアントをまったく実行していない KVM ゲストでも、しばらくの間はおおむね正確な時刻を維持できます。tsc は CPU 自身のカウンターです。Xen ゲストでは xen、Hyper-V ゲストでは hyperv ソースが報告されます。この設定は、変更する測定可能な理由がない限り、そのままにしてください。カーネルは、そのハードウェアで信頼できる最適なソースをすでに選択しているためです。
ホストによっては、PTP (precision time protocol) デバイスをゲストに提供することもあります。これにより、chrony はネットワーク経由ではなく、ホストのクロックを直接読み取れます。確認する価値はありますが、共有 VPS では利用できないことがよくあります。
sudo modprobe ptp_kvm
ls /dev/ptp*
cat /sys/class/ptp/ptp0/clock_namemodprobe が失敗するかデバイスが表示されない場合、ホストはその機能を提供していません。ネットワーク経由の NTP を使用してください。clock_name に KVM の仮想クロックが表示される場合、chrony の設定に refclock PHC /dev/ptp0 poll 2 行を追加して使用できます。
自分のマシンの時刻状態を確認する
まず 1 つのコマンドを実行します。これだけで、「このクロックを正しい状態に保っているものがあるか」という疑問に 1 画面で答えられます。
timedatectl記憶している数値をそのまま信頼せず、次の行を確認します。
Local timeとUniversal timeは、同じ時刻をローカルのタイムゾーンと UTC で表示したものです。両者が同じなら、そのマシンはすでに UTC で動作しています。RTC timeは前述したハードウェアクロックです。VPS では無視してください。Time zoneは、システムがローカル時刻の表示に使用する設定です。System clock synchronizedは kernel 自身のフラグです。時刻デーモンは、時刻ソースを信頼できると判断した時点でこのフラグを設定します。そのため、noは、ブート後にこのクロックを補正した時刻デーモンがないことを示します。NTP serviceは systemd-timesyncd 専用の状態を示します。chrony が動作しているマシンでは timesyncd がインストールされていないため、n/aが正常です。System clock synchronized: yesとNTP service: n/aが同時に示される場合は、chrony が処理を行い、kernel もその状態を認識しています。
次に、時刻がどの程度ずれているかを確認します。スマートフォンと目視で比較しないでください。chrony が動作している場合は、次を実行します。
chronyc tracking
chronyc sources -vchronyc tracking に、確認に必要な数値が表示されます。System time は NTP 時刻からの現在のずれで、その後に fast または slow が表示されます。Last offset は直近の補正量です。Frequency は chrony がクロックについて測定し、すでに補正している進み・遅れの割合です。Leap status は Normal になるはずです。Not synchronised で、Reference ID が 00000000 () の場合、chrony はまだ使用する時刻ソースを決定できていません。
chronyc sources -v は一覧の上に凡例を表示するため、記号を覚えておく必要がありません。意味の大部分は 2 つの列に含まれています。各行の先頭にある状態文字は、chrony がそのソースをどう評価しているかを示します。* は現在使用中のソースを示し、すべての行が ? の場合は、応答しているソースがありません。Reach は直近 8 回のポーリングに対する応答履歴を 8 進数で表示します。377 は 8 回すべて応答があり、0 は 1 回も応答がないことを示します。
systemd-timesyncd が管理している場合は、次を実行します。
timedatectl timesync-status
timedatectl show-timesync --alltimesync-status に、接続しているサーバー、ポーリング間隔、Offset 値が表示されます。状態ではなくサービスに関するエラーが返る場合、このマシンでは timesyncd が時刻同期を担当していません。これ自体が疑問への答えです。
追加のツールを使わずに外部時刻と大まかに比較するには、公開 HTTP Date ヘッダーと自分のクロックを比較します。このヘッダーは GMT で提供され、精度は 1 秒です。
date -u
curl -sI https://www.cloudflare.com | grep -i '^date:'ここで 1 秒または 2 秒の差があるのは正常で、問題を意味しません。1 分の差があるなら、設定に問題があります。
VPS では chrony と systemd-timesyncd のどちらを使うか
Ubuntu と Debian では、デフォルトで systemd-timesyncd がインストールされます。これは SNTP(simple network time protocol)クライアントです。一度に 1 台のサーバーへ問い合わせ、そのサーバーに合わせて時計を少しずつ調整します。常時オンラインで、起動時点の時刻が大きくずれていないマシンであれば、これで十分です。実行に必要なリソースもほとんどありません。
chrony は完全な NTP 実装であり、仮想マシンではこちらをデフォルトにする方が適しています。その理由は、chrony 自身の出力からも確認できます。複数のソースを同時にポーリングし、互いに一致しないソースを破棄します。時計のレート誤差を測定して drift ファイルに書き込むため、各サンプルを追いかけるのではなく、時計のずれる傾向を補正します。また、物理サーバーにはない、VM 特有の次の 2 つの状況からも迅速に復旧できます。ホストによって一時停止されることと、実行中に別のホストへ移動されることです。ホストの PTP デバイスが提供されている場合、それを読み取るのも chrony です。
sudo apt update && sudo apt install -y chrony
systemctl status chrony --no-pager
chronyc sources -v
chronyc tracking実行中の apt の出力を確認してください。Debian と Ubuntu では、chrony と systemd-timesyncd のパッケージがどちらも time-daemon を提供します。そのため、apt は chrony のインストール時に timesyncd を削除します。これは正しい動作であり、必要な処理です。両方を実行しないでください。同じ時計を設定する 2 つのデーモンが競合するため、実行中はどちらが報告するオフセットも信頼できません。Rocky と AlmaLinux では sudo dnf install -y chrony でインストールします。この場合、unit の名前は chrony ではなく chronyd です。
設定ファイルは、Debian と Ubuntu では /etc/chrony/chrony.conf、Rocky と Alma では /etc/chrony.conf です。ディストリビューションのデフォルト設定は VPS に適した内容になっているため、理由がある場合だけ変更してください。理解しておくべきディレクティブは 2 つあります。
poolとserverの行で時刻ソースを指定します。iburstを追加すると、起動時に chrony が高速バーストを送信します。これにより、最初の同期が数分ではなく数秒で完了します。makestepは、chrony が時計を段階的に調整する代わりに、一気に変更するタイミングを決めます。grep -n makestep /etc/chrony/chrony.confで現在の設定を確認してください。Debian と Ubuntu のデフォルトであるmakestep 1 3は、次の意味です。chronyd の起動後、最初の 3 回の更新では、時計が 1 秒を超えてずれている場合に時刻を一気に補正します。その後は、slew による緩やかな補正だけを行います。
経路上の改ざんに対して時刻通信を認証したい場合、chrony 4 以降では NTS(network time security)を利用できます。まず chronyd -v でバージョンを確認してください。また、NTS には UDP 123 に加えて、送信方向の TCP 4460 も開放されている必要があります。
server time.cloudflare.com iburst nts信頼して利用する前に、再起動して動作を確認してください。設定ファイルの解析に失敗すると、時刻デーモンがまったく動作しない状態になります。時計はそのことを通知してくれません。
sudo systemctl restart chrony
chronyc sources -v
chronyc tracking数分ずれている時計がずれたままになる理由
時刻デーモンがオフセットを修正する方法は2つあります。スルーイングでは、誤差がなくなるまで時計を進める速度を上げるか、遅くします。これにより時刻は常に前へ進み、タイムスタンプの重複や欠落も発生しません。ステップでは、正しい値へ一気に変更します。高速ですが、時計が逆方向に進むことがあります。実時間を基準に経過時間を測定する処理では、時間が逆戻りすると問題になるため、どちらのデーモンもスルーイングを優先します。
この優先動作により、大きくずれた時計が長時間ずれたままになることがあります。chrony は、makestep で許可された範囲内でのみステップします。デフォルトでは、デーモンの起動直後に行われる最初の数回の更新が対象です。1週間稼働した chronyd が40秒の誤差を検出した場合、その誤差はスルーイングで修正されます。しかし、40秒のスルーイングには待ちたくないほど長い時間がかかります。負荷の低い時間帯に、意図的に1回だけ強制修正します。
sudo chronyc makestep
chronyc trackingchronyc tracking には System time オフセットがほぼ0として表示され、Last offset には今回修正された量が表示されるはずです。負荷の高いデータベースホストで実行する前に、よく検討してください。時計が逆方向に進むと、時刻は常に前へ進むと想定しているソフトウェアが混乱する可能性があります。デーモンを再起動する方法は、同じ修正をより穏やかに行う手段です。起動時に makestep のウィンドウが再び開くためです。
コンテナはホストのクロックを共有します
コンテナは独自の実時間クロックを持たないため、コンテナ内で時刻を同期する対象はありません。Linux の time namespace が仮想化するのは monotonic clock と boot-time clock だけです。CLOCK_REALTIME は仮想化されないため、コンテナは実行元ホストと同じシステムクロックを読み取ります。ホストのクロックを修正すれば、そのホスト上のすべてのコンテナも同じ時点で修正されます。
このことから、いくつかの注意点があります。イメージに chrony や ntpd をインストールしないでください。インストールしても、よくて何も起こりません。非特権コンテナ内で日付を設定すると date: cannot set date: Operation not permitted で失敗します。この処理にはカーネルが CAP_SYS_TIME を要求するためです。CAP_SYS_TIME を付与しても、コンテナ専用のクロックは得られません。コンテナからホストのクロックを変更できるようになり、その結果、他のすべてのコンテナのクロックも変更されます。
コンテナ内のタイムゾーンが異なることは、クロックの問題ではありません。独自の /etc/localtime を含むイメージは、同じ時点を別のタイムゾーン向けに整形して表示します。そのため、date が誤って見えても、クロック自体は正しい場合があります。コンテナの環境変数に TZ=UTC を設定すれば、この混乱を解消できます。選択したランタイムによって、この点は変わりません。ランタイムによって変わる点については、rootless Podman と Docker の比較を参照してください。
タイムゾーン: サーバーは UTC、人間には現地時刻
マシンは UTC に設定し、そのまま維持してください。
timedatectl list-timezones | grep -i utc
sudo timedatectl set-timezone UTC
timedatectlUTC には夏時間がなく、それだけで採用する理由になります。夏時間を採用するタイムゾーンで 02:30 に実行するジョブは、時計を戻す日に 2 回実行され、時計を進める日にはまったく実行されません。man 8 cron には、3 時間未満の時刻変更に対する特別な処理が記載されています。時計を進めたことでスキップされたジョブは変更直後に実行され、時計を戻したことで重複した時間帯に入ったジョブは 2 回目には実行されません。この動作は妥当ですが、03:00 にその動作を考慮する必要はありません。UTC では、ジョブは 1 年の毎日、1 日 1 回実行されます。ジョブが通常と異なる時刻に実行されるのではなく完全に実行されない場合は、cron ジョブが何も記録せずに実行されない理由のほうが可能性の高い原因です。
ログの読み取りにも同じことが当てはまります。journalctl はシステムのタイムゾーンでタイムスタンプを整形し、journalctl --utc は UTC を強制します。異なる 2 つのタイムゾーンにある 2 台のサーバーでは、インシデントのたびに時刻の変換が必要になります。緊急時に行う変換は、タイムラインの読み違いにつながります。システムを UTC に設定し、タイムスタンプを UTC で保存し、人間が読む時点で 1 回だけ変換してください。1 つのコマンドだけ現地時刻で表示したい場合は、マシンの設定を変更せずに指定できます。
TZ=Europe/Berlin datetimedatectl の出力には、このセクションに関係する行がもう 1 つあります。RTC in local TZ は no になっている必要があります。これを yes に設定するのは、ノート PC で Windows とデュアルブートする場合の回避策です。サーバーでは、後で誰かが問題にする時差を追加するだけです。これが設定されていると、timedatectl は、システムが RTC の時刻を現地タイムゾーンで読み取る設定になっているという警告を表示します。
症状別のトラブルシューティング
1 台のサーバーで二要素認証コードが拒否されます。 最初に時刻を確認してください。コードは 30 秒ごとに進むカウンターから生成されるため、サーバーが 90 秒遅れていると、スマートフォンですでに過ぎたステップのコードを計算します。timedatectl で System clock synchronized: no を確認でき、chronyc tracking では大きな System time オフセットが報告されます。これは、鍵が完全に拒否される場合とは異なる問題です。その場合は専用のメッセージが表示され、公開鍵認証の失敗に関するガイドで説明しています。
apt update で Release ファイルがまだ有効ではないと表示されます。 完全なメッセージにはリポジトリと、無効な状態が続く時間が表示されます。たとえば is not valid yet (invalid for another 1d 2h 3min 4s) です。リポジトリの Release ファイルに記載された日付より、システムの時刻が遅れています。その期間は、遅れの大きさを直接示しています。時刻を修正してください。回避のために apt の日付チェックを無効にしないでください。このチェックにより、古いパッケージインデックスを提供されることを防いでいます。
すべてのソース行が到達不能状態を示し、Reach が 0 です。 応答している相手がいないため、設定ではなく送信方向の通信を確認してください。NTP の送信には UDP port 123 を使用しますが、ネットワークによってはこの通信をフィルタリングまたはリダイレクトします。sudo chronyc ntpdata で Total TX や Total RX など、ソースごとのカウンターを表示できます。TX カウントだけが増加し、RX が 0 のままなら、パケットは送信されていますが応答が返っていません。自分とソースの間にあるファイアウォールが原因である可能性があります。
時刻は正しかったのに、その後に大きく変わりました。 ホスト側のイベントが原因になることがあります。復元したスナップショット、一時停止していたゲスト、別のホストへのライブマイグレーションなどにより、ゲストが認識する時刻が実際の時刻より遅れる場合があります。chrony は次回のポーリング時に検知して修正しますが、systemd-timesyncd は長いポーリング間隔が経過するまで待つことがあります。systemctl is-enabled chrony でデーモンがブート時に起動することを確認してください。手動で起動したデーモンは、次回の再起動後には終了しています。
オフセットは小さいのに、安定しません。 CPU steal を確認してください。タイマー割り込みが発生する時点でゲストがスケジュールされていないと、サンプルの取得が遅れるため、オフセットが収束せずに変動します。top では、CPU 行の st としてこの状態を表示します。共有ホストで CPU steal time を読み取る方法で、この数値の意味と対処方法を説明しています。
発行したばかりの証明書が、まだ有効ではないとして拒否されます。 curl で SSL certificate problem: certificate is not yet valid が表示され、ブラウザーでも同様のメッセージが表示されます。証明書に問題はありません。証明書を検証している側の時刻が遅れています。問題があるのはクライアントまたはサーバーのどちらかの可能性があるため、両方を確認してください。証明書を発行したサーバーの時刻が誤っている場合は、certbot と nginx の証明書ガイドで、同じ構成における更新手順を説明しています。
すでに実行している確認項目に追加する
時刻同期は、起動時に設定されても数か月後にひそかに失敗することがあります。これは、定期的な確認なら検出できますが、記憶だけでは見落としやすい問題です。timedatectl と chronyc tracking は、続けて確認しても 2 秒で済みます。新しい VPS の最初の 10 分に実行する確認項目へ追加し、通常の Linux サーバー保守チェックリストを確認するときにも、もう一度実行してください。オフセットが大きくなったときに自動で確認して通知させる場合は、systemd の service と timer の作成で、一定間隔で状況を報告する小規模な unit の構成例を確認できます。
FAQ
VPS のクロックが同期しているかどうかを確認するにはどうすればよいですか?
timedatectl を実行し、System clock synchronized の行を確認します。これは、クロックを調整しているデーモンが設定する、カーネル自身のフラグです。そのため、chrony を使用しているマシンで yes と NTP service: n/a が併記されているのは正常で、健全な状態です。誤差の大きさを確認するには、chronyc tracking を実行して System time を確認します。systemd-timesyncd が管理している場合は、timedatectl timesync-status を実行して Offset を確認します。マシン外部の基準と比較するには、date -u と、任意の HTTPS サイトから返される Date ヘッダーを比較します。
VPS では chrony と systemd-timesyncd のどちらを使用すべきですか?
重要な用途では chrony を使用します。systemd-timesyncd は 1 台のサーバーに従う SNTP クライアントです。常時オンラインで、起動時点の時刻がほぼ正しいマシンであれば問題ありません。chrony は複数のソースをポーリングし、値が一致しないソースを除外し、クロックの進み方の誤差を学習します。また、ホストの一時停止やライブマイグレーションの後も短時間で復旧します。Debian または Ubuntu に chrony をインストールすると、systemd-timesyncd は自動的に削除されます。両方のパッケージが time-daemon を提供するためです。2 つの時刻デーモンを同時に実行しないでください。
1 台のサーバーでは TOTP コードが失敗するのに、他の環境では機能するのはなぜですか?
TOTP コードは現在時刻の関数だからです。コードは 30 秒ごとに進むカウンターから生成されるため、サーバーとスマートフォンが同じステップを参照する必要があります。多くの検証処理は前後 1 ステップを許容するため、両方向におよそ 30 秒の余裕があります。そのサーバーで timedatectl を確認します。System clock synchronized が no であれば、時刻同期を修正してください。共有シークレットを変更しなくても、コードが再び一致します。
Docker コンテナ内で時刻を設定できますか?
いいえ。設定する必要もありません。コンテナはホストの CLOCK_REALTIME を共有します。Linux の時刻名前空間が仮想化するのは、単調時計とブート時刻のクロックだけだからです。権限のないコンテナには date: cannot set date: Operation not permitted が付与されます。CAP_SYS_TIME を追加すると、コンテナ固有のクロックを与えるのではなく、ホストのクロックを変更できるようになります。ホスト側を同期してください。コンテナ内で異なる現地時刻を表示する場合、それはタイムゾーンの設定です。コンテナ環境で TZ を設定してください。
サーバーでは UTC と現地時間のどちらを使用すべきですか?
UTC を使用し、人が出力を読む時点で現地時間を適用します。UTC は夏時間によって変わらないため、日次ジョブは年間を通じて 1 日 1 回実行され、異なるサーバーのタイムスタンプも変換なしで揃います。sudo timedatectl set-timezone UTC で設定します。現地時間で読みたい場合は、単一のコマンドの前に指定できます。たとえば TZ=America/New_York date です。これはシステムクロックを変更しません。