OSのサポート切れのデバイスの時刻同期をなるべく安全に行いたいと思ったのが、やろうと思ったきっかけです。
ぶっちゃけた話、外部からの通信を制限しているLAN内ならば普通にインターネット接続して時刻同期してもいいと思うし、手間がかかりません。
(基本的にはOSのサポート切れのデバイスでインターネット接続をするのはお勧めしません。接続する場合は自己責任で行ってください。)
概要
- Ubuntu 22.04 LTSのDockerコンテナでChronyを動かして、LAN内のWindows 7とAndroid 10の時刻同期をする
- Windows 7とAndroid 10の時刻同期をするときは、Router上流のネットワーク接続を切断してオフラインで行う
想定読者
- セキュリティリスクを考慮して、サポート切れのOSが搭載されているデバイスをオフラインで時刻同期したいというニッチな要望をお持ちの方
- ホストPCに余計なソフトウェアを入れたくないとか環境を汚したくないという事情があるが、NTPサーバーを立てて時刻同期してみたいという方
ネットワーク構成
![docker-container-ntp-diagrams](https://qiita-user-contents.imgix.net/https%3A%2F%2Fqiita-image-store.s3.ap-northeast-1.amazonaws.com%2F0%2F3715783%2Fd0016899-4e14-3fe2-e794-2aad64899f76.png?ixlib=rb-4.0.0&auto=format&gif-q=60&q=75&s=ec806d6d535adc8e99dc0f6c619cf9bc)
ChronyのDockerコンテナを動かすホストPC
- Ubuntu 22.04 LTS: 192.168.11.3
- Docker rootless
- Client Version 27.4.1
- Server Version 27.4.1
- Docker Compose version v2.32.1
- Docker rootless
インターネット接続させたくないデバイス
- LIFEBOOK AH42/C Windows 7(2011年製っぽい): 192.168.11.2
- Xperia Ace SO-02L Android 10(2019年6月1日発売っぽい): 192.168.11.4
手順
- Dockerコンテナを準備する
- Chronyの設定ファイルを編集する
- ホストPCのufwの設定を変更する
- Dockerコンテナを動かしてChrony Serverの時刻同期をする
- Router上流のインターネット接続を切る
- Windows 7とAndroid 10をLANに接続する
- Windows 7にNTPサーバーの参照先を設定する
- Android 10にNTPサーバーの参照先を設定する
- Windows 7を時刻同期する
- Android 10を時刻同期する
1. Dockerコンテナを準備する
- 任意のディレクトリに下記のディレクトリ構成を作成します
./chrony-related
│
├─debi-test
│
└─ub-test
- ub-testディレクトリに移動してChrony ServerのDockerfileを作成します
Chrony ServerのDockerfile
FROM ubuntu:22.04
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
iproute2 \
iputils-ping \
chrony \
vim \
tzdata \
tcpdump \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/* \
&& ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
-
docker image build -t chrony-ub:1 .
コマンドを実行してイメージをビルドします -
ビルドができたら
docker container run -dit --name chrony-ub --rm chrony-ub:1 bash
で一度コンテナを動かします -
docker container cp chrony-ub:/etc/chrony/chrony.conf .
でchronyの設定ファイルをホストPCにコピーします -
docker container stop chrony-ub
でコンテナを停止します -
debi-testディレクトリに移動してChrony ClientのDockerfileを作成します
Chrony ClientのDockerfile
FROM debian:latest
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
iproute2 \
iputils-ping \
chrony \
vim \
tcpdump \
tzdata \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/* \
&& ln -sf /usr/share/zoneinfo/Asia/Tokyo /etc/localtime
-
docker image build -t chrony-debi:1 .
コマンドを実行してイメージをビルドします - ビルドができたら
docker container run -dit --name chrony-debi --rm chrony-debi:1 bash
で一度コンテナを動かします -
docker container cp chrony-debi:/etc/chrony/chrony.conf .
でchronyの設定ファイルをホストPCにコピーします -
docker container stop chrony-debi
でコンテナを停止します
chronyがあれば十分ですが、トラブルシューティング用にiproute2、iputils-ping、tcpdump等をインストールしています。
tzdataとln -sfのところは、Dockerコンテナ内で日本時間を表示できるようにするためのものですが、時刻同期には無関係なので無くても問題ありません。
- ディレクトリ構成は下記のようになります
./chrony-related
│
├─debi-test
│ chrony.conf
│ Dockerfile
│
└─ub-test
chrony.conf
Dockerfile
2. Chronyの設定ファイルを編集する
- Chrony Serverの/ub-test/chrony.confに下記を追記します
元々記載されていた時刻同期先をコメントアウトして、任意の同期先を追加
#pool ntp.ubuntu.com iburst maxsources 4
#pool 0.ubuntu.pool.ntp.org iburst maxsources 1
#pool 1.ubuntu.pool.ntp.org iburst maxsources 1
#pool 2.ubuntu.pool.ntp.org iburst maxsources 2
server ntp.nict.jp minpoll 6 maxpoll 8
server 0.jp.pool.ntp.org minpoll 6 maxpoll 8
server 1.jp.pool.ntp.org minpoll 6 maxpoll 8
server 2.jp.pool.ntp.org minpoll 6 maxpoll 8
アクセス許可するホストのネットワークを指定しますが、Dockerネットワーク外のLANからのアクセスは全てデフォルトゲートウェイになるため、実質飾りみたいなものです
allow 192.168.254.0/24
Chrony Server自体が時刻同期できない状態でも時刻同期を提供できるように、自身のstratumを10として設定します
local stratum 10
- Chrony Clientの/debi-test/chrony.confに下記を追記します
元々記載されていた時刻同期先をコメントアウトして、Chrony ServerのIPアドレスを追加
#pool 2.debian.pool.ntp.org iburst
server 192.168.254.10 iburst
3. ホストPCのufwの設定を変更する
-
sudo ufw allow from 192.168.11.0/28 to any port 123 proto udp
を実行して、LAN内からの時刻同期の問い合わせを受け入れられるようにします- これが実質的なアクセス許可設定になります
- iptablesやfirewalldを使っている場合は、そちらのやり方でUDP123番ポートを受け入れられるようにしてください
4. Dockerコンテナを動かしてChrony Serverの時刻同期をする
-
sudo sysctl -w net.ipv4.ip_unprivileged_port_start=123
を実行して、Dockerが123番ポートを公開できるようにします- rootlessなDockerの場合は、1024番ポート以下をデフォルトで公開できないために必要な操作になります
- 上記のコマンドは一時的に123番ポート以上を公開できるようにするもので、ホストPCを起動しなおすと元の1024番ポート以下が公開できない状態に戻ります
- chrony-relatedディレクトリに移動してdocker-compose.ymlを作成します
docker-compose.yml
services:
ubtest:
build: ub-test
image: chrony-ub:1
container_name: ubuntu
command: >
bash -c '
chronyd -x && bash
'
tty: true
ports:
- "123:123/udp"
volumes:
- ./ub-test/chrony.conf:/etc/chrony/chrony.conf
networks:
internal:
ipv4_address: 192.168.254.10
debian:
build: debi-test
image: chrony-debi:1
container_name: debi
command: >
bash -c '
chronyd -x && bash
'
tty: true
volumes:
- ./debi-test/chrony.conf:/etc/chrony/chrony.conf
networks:
internal:
ipv4_address: 192.168.254.20
networks:
internal:
name: ntp-network
ipam:
config:
- subnet: 192.168.254.0/24
- ディレクトリ構成は下記のようになります
./chrony-related
│ docker-compose.yml
│
├─debi-test
│ chrony.conf
│ Dockerfile
│
└─ub-test
chrony.conf
Dockerfile
-
docker compose up -d
を実行してDockerコンテナを動かします - 5~10分くらい経ってから
docker compose exec ubtest chronyc sources
を実行します- 下記のように
*
がついている現在同期しているソースがあれば、/ub-test/chrony.confに記載した同期先と通信できています
- 下記のように
MS Name/IP address Stratum Poll Reach LastRx Last sample
===============================================================================
^* ntp-k1.nict.jp 1 6 77 53 +13ms[ +16ms] +/- 28ms
^+ ntp-b2.nict.go.jp 1 6 77 52 +19ms[ +19ms] +/- 28ms
^+ v160-16-113-133.ntp.tky2> 3 6 77 52 +13ms[ +13ms] +/- 24ms
^- 45.159.48.231 3 6 77 52 +23ms[ +23ms] +/- 163ms
-
docker compose exec ubtest chronyc tracking
を実行してReference IDを確認します- 下記のように/ub-test/chrony.confに記載した同期先のどれかになっていれば、Chrony Serverは時刻同期できています
Reference ID : 3DCD7882 (ntp-k1.nict.jp)
Stratum : 2
Ref time (UTC) : Sat Jan 25 06:59:50 2025
System time : 0.038299233 seconds fast of NTP time
Last offset : +0.000589726 seconds
RMS offset : 0.003223524 seconds
Frequency : 92.161 ppm fast
Residual freq : +0.378 ppm
Skew : 52.468 ppm
Root delay : 0.015766550 seconds
Root dispersion : 0.030345995 seconds
Update interval : 64.8 seconds
Leap status : Normal
-
docker compose exec debian chronyc tracking
を実行してReference IDを確認します- 下記のようにChrony Serverになっていれば、Dockerネットワーク内での時刻同期ができています
Reference ID : C0A8FE0A (ubuntu.ntp-network)
Stratum : 3
Ref time (UTC) : Sat Jan 25 07:00:59 2025
System time : 0.046149909 seconds fast of NTP time
Last offset : -0.000116804 seconds
RMS offset : 0.004550459 seconds
Frequency : 110.957 ppm fast
Residual freq : +0.325 ppm
Skew : 27.168 ppm
Root delay : 0.015842594 seconds
Root dispersion : 0.016304253 seconds
Update interval : 64.7 seconds
Leap status : Normal
5. Router上流のインターネット接続を切る
- Router上流側のLANケーブルを外してインターネット接続を切ります
6. Windows 7とAndroid 10をLANに接続する
- Windows 7とAndroid 10をRouterに接続してLANにつなげます
7. Windows 7にNTPサーバーの参照先を設定する
- 管理者権限でcmd(コマンドプロンプト)を開きます
-
sc start w32time
を実行してWindows タイム サービス(W32Time)を起動します-
net start w32time
でも起動できます - GUIでインターネット時刻設定から時刻同期するときも、Windows タイム サービスが起動しています
-
-
w32tm /config /update /manualpeerlist:192.168.11.3,0x8 /syncfromflags:manual
を実行して、時刻同期の参照先としてホストPCを設定します -
sc stop w32time
を実行してからsc start w32time
を実行して、Windows タイム サービスを再起動し、設定を反映させます -
w32tm /query /configuration
を実行して、NtpServer:
の値にホストPCのIPアドレスが記載されていることを確認します
上記と同様の操作はGUIのインターネット時刻設定からも可能です。
8. Android 10にNTPサーバーの参照先を設定する
- adbが使えるPCとAndroid 10をUSBケーブルで接続します
- adbの導入や接続時の手順は省略します
- PCでPowerShellを開きます
-
adb shell settings put global ntp_server 192.168.11.3
を実行して、時刻同期の参照先としてホストPCを設定します -
adb shell settings get global ntp_server
を実行して、ホストPCのIPアドレスが表示されることを確認します -
adb kill-server
を実行してPCとの接続を切れるようにします- これを実行しないとPC側でadbのプロセスが残ったままになります
- USBケーブルを外します
9. Windows 7を時刻同期する
- Windows 7の管理者cmdで
w32tm /resync
を実行して、手動で時刻同期を行います- 「コマンドは正しく完了しました。」というような表示が出れば時刻同期が成功しているはずです
-
w32tm /query /source
を実行して、ホストPCのIPアドレスが表示されることを確認します
これでWindows 7の時刻同期は完了です。
個人的に、0.何秒とかのずれに収まっていれば十分かなと思っております。
10. Android 10を時刻同期する
- デバイスを再起動して再度LANにつなげることで時刻同期を行います
- adbで時刻同期を行う方法は見つけられませんでした
- デバイスのWi-Fiをオンオフする操作では時刻同期を行うことができませんでした
- ホストPCのWiresharkで、デバイスからの時刻同期問い合わせのパケットが届いていないことを確認しています
これでAndroid 10の時刻同期は完了です。
docker compose exec ubtest chronyc -a clients
を実行すると、時刻同期問い合わせをしてきたクライアントを確認できます。
Hostname NTP Drop Int IntL Last Cmd Drop Int Last
===============================================================================
debi.ntp-network 23 0 6 - 25 0 0 - -
192.168.254.1 2 0 7 - 116 0 0 - -
debi.ntp-network
がChrony Clientで、192.168.254.1
がLAN内のデバイスになります。
参考にしたサイト
- chrony(NTPd代替)の設定方法
- 【NTPサーバ】Chrony ver. 4.6 の設定について
- 【Centos7】chronyc sources コマンドの出力結果の見方
- Chronyd inside LXC conatiner
- chronyd(8) Manual Page
- WindowsサーバNTPクライアント設定/確認コマンドまとめ
- Windows-NTPコマンド設定
- android でLAN内のNTPサーバーに時刻を合わせる
ハマったところ
- DockerコンテナでのChronydの起動方法を見つけるのに手間取った
-
chronyd -x
を使えば起動できることを見つけて解決できました - Dockerコンテナなので
systemctl
は使えないですし、service
コマンドは無意味でした
-
- Chrony Serverの/etc/chrony/chrony.confでアクセス許可するIP範囲を、LANの
192.168.11.0/28
にしていて時刻同期できなかった- Dockerネットワークの
192.168.254.0/24
を許可することで解決できました - Chrony Serverでtcpdumpを使って、LANからの全てのアクセスはDockerネットワークのデフォルトゲートウェイのIPアドレスになっていることがわかりました
- Dockerネットワークの