TL;DR
システムクロックがAWSの許容範囲である15分を超えてずれています。AWSはそれほどずれたタイムスタンプで署名されたリクエストを拒否します。通常、以下のコマンド一つで解決できます:
# Linux (systemd)
sudo systemctl restart systemd-timesyncd
timedatectl status
# Or force sync with chrony
sudo chronyc makestep
# Amazon Linux 2 / EC2
sudo systemctl restart chronyd
クロックのずれが数分程度であれば、これだけで十分です。
根本原因
AWSはAPIリクエストの認証にSigV4(署名バージョン4)を使用しています。すべてのリクエストにタイムスタンプが含まれており、AWSはそれを自社サーバーと照合します。どちらの方向にも±15分を超えてずれると、以下のエラーが発生します:
RequestExpired: Request has expired. (Service: AmazonS3; Status Code: 403, Error Code: RequestExpired)
これはリプレイアタックへの防御機能です。タイムスタンプウィンドウが厳格であれば、古い署名済みリクエストは無効になります。認証情報は問題ありません。クロックが問題なのです。
特に気づきにくいケースを挙げます:
- 一時停止・サスペンドされ、その後再開されたVMやコンテナ — 一時停止中はクロックが止まる
- NTPの設定ミス、またはNTPデーモンがクラッシュしたサーバー
- ホストクロックのずれを引き継いだDockerコンテナ
- 数時間スリープした後、NTPがウェイクアップ時に十分な速さで同期できなかったノートPC
chronydが誤って停止されたEC2インスタンス
まず診断する
何かを変更する前にこれらを実行してください — クロックが本当に原因かどうかを確認します:
# 現在のシステム時刻とAWSが認識している時刻を比較
date -u
curl -s --head https://s3.amazonaws.com | grep -i date
# NTP同期ステータスを確認 (systemd)
timedatectl status
# chronyを確認 (Amazon Linux, RHEL, CentOS)
chronyc tracking
timedatectlがSystem clock synchronized: noまたはNTP service: inactiveを表示している場合、それが原因です。chronyc trackingでオフセットが約500msを超えている場合は、積極的にずれていることを意味します。
既知のソースに対する簡単な数値確認:
# ローカル時刻とAWSタイムサーバーを比較
ntpdate -q time.aws.com 2>/dev/null || ntpdate -q pool.ntp.org
修正方法:クロックを同期する
オプション1 — systemd-timesyncd(Ubuntu/Debian)
# NTP同期を有効化して開始
sudo timedatectl set-ntp true
sudo systemctl restart systemd-timesyncd
# 確認
timedatectl status
# 表示されるべき内容: System clock synchronized: yes
オプション2 — chrony(Amazon Linux 2、RHEL、CentOS)
# 即座に強制ステップ実行(段階的な調整を待たない)
sudo chronyc makestep
# デーモンが停止している場合は再起動
sudo systemctl enable chronyd
sudo systemctl restart chronyd
# 確認
chronyc tracking
# 確認ポイント: System time offset が理想的には1ms未満
オプション3 — ntpdate(ワンショット即時同期)
# インストールされていない場合はインストール
sudo apt install ntpdate # Debian/Ubuntu
sudo yum install ntpdate # RHEL/CentOS
# 即座に同期
sudo ntpdate -u pool.ntp.org
# またはAWS独自のNTPサーバーを使用(EC2上の169.254.169.123)
sudo ntpdate -u 169.254.169.123
注意:ntpdateはワンショットツールです。今すぐクロックを修正しますが、同期を維持しません。緊急対処以外の用途には、chronyまたはsystemd-timesyncdを使用してください。
オプション4 — macOS
# ネットワーク時刻を有効化
sudo sntp -sS time.apple.com
# またはシステム設定 → 一般 → 日付と時刻 → 「日付と時刻を自動設定」
オプション5 — Windows
# 管理者権限のPowerShellで実行
w32tm /resync /force
w32tm /query /status
修正を確認する
次に確認します。失敗していたコマンドを実行するか、以下を使用してください:
# シンプルなS3リストでテスト
aws s3 ls s3://your-bucket-name
# またはIDを確認(S3アクセスがなくても機能する)
aws sts get-caller-identity
RequestExpiredの代わりに正常なレスポンスが返れば、問題は解決しています。
特殊ケース:Dockerコンテナ
コンテナはホストのクロックを共有しており、独自のNTPを持ちません。そのため、ホストが20分ずれると、その上のすべてのコンテナが20分ずれます。コンテナではなく、ホストを修正してください:
# Dockerホスト上で実行
sudo chronyc makestep
# コンテナ内から確認
docker exec your-container date -u
VM上でDockerを実行している場合(macOSまたはWindowsのDocker Desktop)、ホストマシンがスリープするとVMのクロックが同期されなくなります。Docker Desktopを再起動することで通常解決します — 基盤となるVMが再起動され、クロックがリセットされます。
EC2固有:AWSタイム同期サービスを使用する
EC2インスタンスには169.254.169.123という独自のNTPエンドポイントがあります — リンクローカルアドレスのため、インターネット不要で、公開NTPプールよりも正確です。Amazon Linux 2はデフォルトでこれを参照しますが、他のAMIはそうでないことが多いです。
# Amazon Linux 2 — 既に設定済み、chronydが実行中であることを確認するだけ
sudo systemctl status chronyd
# EC2上のUbuntu — systemd-timesyncdにAWSタイムサーバーを追加
sudo nano /etc/systemd/timesyncd.conf
# 追加または編集:
# [Time]
# NTP=169.254.169.123
sudo systemctl restart systemd-timesyncd
timedatectl show-timesync
予防策
- 常にタイム同期デーモン(chronyまたはsystemd-timesyncd)を稼働させておく — 「リソース節約」のために無効にすると、後でデバッグにより多くの時間を費やすことになる
- EC2では
169.254.169.123をプライマリNTPソースとして使用する — 無料で、サブミリ秒の精度があり、インターネット接続も不要 - コンテナを使用したCI/CDパイプラインでは、AWSコマンドを実行する前に
date -uを既知のソースと比較するプリフライトチェックを追加する - HashiCorp Vaultやトークンベースのシークレット管理システムを使用している場合も、まったく同じ理由でクロックのずれが問題を引き起こす
パッケージをインストールできないロックダウンされたサーバーで時刻関連の認証問題をデバッグする場合、ブラウザベースのツールを使用してUTCオフセットを素早く確認する方法があります。ToolCraftは、まさにこのような状況で役立つ、アップロード不要・ブラウザ上で完全に動作するオプションを提供しています。
参考資料
- AWSドキュメント:Signing AWS API Requests — SigV4と15分ウィンドウについて説明
- Amazon EC2ユーザーガイド:Setting the time for your Linux instance
- chronyドキュメント:
man chronyc、特にmakestepディレクティブ

