Google Cloudの無料枠のe2-microを利用しています。まずはタイトルとは別にSSLの設定も記述したいと思います。

スペックに余裕がないのでPythonのWebサーバーを立ち上げています。このサーバーにSSLの設定をしていきます。以下は、Pythonで構築したWebサーバーにSSLを設定するためにCertbotを使って証明書を取得する基本的な手順です。以下の手順はUbuntuなどのDebian系OSを前提としていますが、環境に合わせて適宜読み替えてください。ちなみに私はUbuntu24.04がOSとなっています。
- ドメインとIPアドレスの紐づけの重要性
- 1. Certbotのインストール
- 2. 証明書の取得方法
- 3. Python Webサーバーへの組み込み
- 4. 自動更新の設定
- 1. Cronを使った自動更新の設定方法
- 2. systemdタイマーを使った自動更新の設定方法
- VSCode のリモート開発環境
- システムリソースの確認方法について
- エラー対策
- 問題点とその解決策
- ディレクトリの作成とFastAPIの設定更新
- サービスの再起動
- Certbot で証明書の取得
- 証明書の適用
- 再度サービスを再起動
- ポートフォワーディングの設定
- プロセスの実行ユーザーを確認する方法は?
- 箇条書きでまとめると、ACLの利点は以下になります。
- 大きな勘違いがあった原因について
ドメインとIPアドレスの紐づけの重要性
SSL証明書を取得するためには、対象のドメインが正しくサーバーのIPアドレスに向いている必要があります。具体的には、以下の点に注意してください。
- DNS設定の確認
まず、利用するドメインのDNS設定で、Aレコード(または必要に応じてCNAMEレコード)が正しいIPアドレスを指しているか確認しましょう。これにより、訪問者がドメインにアクセスした際に、正しいサーバーへ接続されることが保証されます。 - 認証プロセスへの影響
Let’s Encrypt の Certbot は、ドメインが正しくサーバーに向いているかを確認するためにHTTP(ポート80)でアクセスを試みます。そのため、DNS設定が不適切だと認証に失敗し、SSL証明書を取得できません。 - タイムラグの考慮
DNS設定を変更した場合、設定が反映されるまでに時間がかかることがあります。設定後、少なくとも数十分から数時間は待つようにしましょう(場合によっては24時間程度かかることもあります)。 - セキュリティ上のポイント
正しいDNS設定は、SSL証明書の発行だけでなく、将来的なセキュリティ対策の基本ともなります。誤った設定では、中間者攻撃(MITM)などのリスクが高まる可能性もあるため、慎重に設定することが重要です。
このように、Certbotを利用してSSL証明書を取得する前に、必ずドメインとサーバーのIPアドレスの紐づけが正しく行われているか確認しましょう。これが整っていることで、証明書の認証プロセスがスムーズに進み、セキュアな通信が実現されます。
1. Certbotのインストール
【Debian/Ubuntuの場合】
- パッケージリストを更新:
sudo apt-get update
- Certbotをインストール:
sudo apt-get install certbot
※ もしWebサーバーとしてNginxやApacheを利用している場合は、専用のプラグイン(例:python3-certbot-nginx
やpython3-certbot-apache
)もインストール可能です。ただし、PythonのWebサーバーを直接運用している場合は以下の「スタンドアロンモード」を使うのが一般的です。
2. 証明書の取得方法
PythonのWebサーバー(例:Flask、FastAPIなど)を直接運用している場合、スタンドアロンモードでCertbotを実行する方法がおすすめです。このモードでは、Certbotが一時的に自身のサーバーを起動し、ポート80(HTTP)で認証を行います。

スタンドアロンモードでの証明書取得
- 現在実行中のWebサーバーがあれば、一時停止してください。(Certbotがポート80を使用するため)
- 以下のコマンドを実行して証明書を取得:
sudo certbot certonly --standalone -d yourdomain.example
※yourdomain.example
は実際のドメイン名に置き換えてください。 - 証明書が取得できると、通常は
/etc/letsencrypt/live/yourdomain.example/
以下に証明書ファイルが配置されます。
3. Python Webサーバーへの組み込み
取得した証明書と秘密鍵を、PythonのWebサーバー(例:Flaskやgunicornなど)の設定で利用します。例えば、FlaskをSSL有効で実行する場合:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello():
return "Hello, SSL!"
if __name__ == '__main__':
# 証明書と秘密鍵のパスを指定
context = ('/etc/letsencrypt/live/yourdomain.example/fullchain.pem',
'/etc/letsencrypt/live/yourdomain.example/privkey.pem')
app.run(host='0.0.0.0', port=443, ssl_context=context)
※ gunicorn等の場合も、同様にSSL証明書のパスを指定するオプションがあるのでご利用のサーバーに合わせて設定します。
4. 自動更新の設定
Let’s Encryptの証明書は90日間有効なため、自動更新を設定するのが望ましいです。以下のコマンドをcron
またはsystemd
タイマーに登録します。
sudo certbot renew --dry-run
このコマンドで更新のテストができるので、問題なければ自動更新が正常に動作するはずです。
1. Cronを使った自動更新の設定方法
Cronは、指定した時間や間隔でコマンドを実行するためのLinux標準の仕組みです。以下の手順で設定できます。
- crontabの編集
ターミナルで以下のコマンドを実行して、現在のユーザーのcrontabを編集します。crontab -e
- cronジョブの追加
エディタが開いたら、以下の行を追加します。これは毎日午前3時に「sudo certbot renew –dry-run」を実行する設定例です。0 3 * * * sudo certbot renew --dry-run >> /var/log/certbot-renew.log 2>&1
0 3 * * *
は「毎日午前3時」を意味します。- コマンド実行結果は
/var/log/certbot-renew.log
に出力されるので、後でログを確認できます。
- 設定の保存
エディタを保存して終了すると、cronジョブが登録されます。
※注意: 一部のシステムでは、sudo
コマンドの使用に制限があるため、必要に応じてrootユーザーのcrontabに設定するか、適切な権限を付与しましょう。
2. systemdタイマーを使った自動更新の設定方法
systemdタイマーは、cronの代替として利用でき、systemdの管理下でジョブを実行する方法です。以下の手順で設定します。
サービスユニットファイルの作成
まず、Certbotの更新テストを実行するサービスユニットファイルを作成します。/etc/systemd/system/certbot-renew.service
という名前で、以下の内容を記述します。
[Unit]
Description=Certbot Renewal Dry-Run
[Service]
Type=oneshot
ExecStart=/usr/bin/sudo /usr/bin/certbot renew --dry-run
タイマーユニットファイルの作成
次に、このサービスを定期的に実行するタイマーを作成します。/etc/systemd/system/certbot-renew.timer
という名前で、以下の内容を記述します。
sudo systemctl daemon-reload
sudo systemctl enable certbot-renew.timer
sudo systemctl start certbot-renew.timer
タイマーの状態を確認するには、以下のコマンドを実行します。
sudo systemctl status certbot-renew.timer
以上の設定により、Certbotの自動更新テスト(--dry-run
)が毎日指定した時間に実行され、更新処理が正しく動作するか確認できます。どちらの方法もシステム環境に合わせて選択します。
補足
- ファイアウォール設定:ポート80および443が外部からアクセス可能になっているか確認しましょう。
- DNS設定:取得前に対象ドメインのDNSが正しく設定され、サーバーのIPアドレスを指している必要があります。
- サーバー停止タイミング:スタンドアロンモードでは一時的にWebサーバーを停止する必要があるため、メンテナンス時間中に行うのが安全です。
VSCode のリモート開発環境
普段、VSCodeのリモート接続はとても便利に使っていますが、私が利用しているGoogle Cloud Platform(GCP)の無料枠のe2-microインスタンスは、以下のスペックとなっています。
- vCPU: 0.25 vCPU(バースト機能により、一時的に2 vCPUまで利用可能)
- メモリ: 1GB
- ストレージ: 30GBの標準永続ディスク(無料枠内)
- ネットワーク: リージョンによっては下りネットワークの無料枠あり
補足情報として以下もご参照ください。
無料枠でも関数のデプロイには料金が発生する場合があります。
e2-microインスタンスは、小規模なアプリケーションや開発・テスト環境に適しています。
無料枠の利用には、いくつかの制限があります。例えば、リージョンが限定されていることや、利用時間に上限があることなどです。
無料枠には、e2-microインスタンスの他に、30GBの標準永続ディスク、下りネットワークの無料使用量などが含まれます。
無料枠には使用量の制限があります。例えば、月間200万リクエスト、360,000GB秒のメモリ、180,000vCPU秒コンピューティング時間、5GBの下りネットワークが利用できます。
このように、無料枠のためシステムリソースに余裕がありません。実際、プロセス一覧を確認すると、全体の負荷として特に目立つのは node プロセス(PID 3062) でした。
- 仮想メモリサイズ: 31.3GBと非常に大きい
- 実際のメモリ使用率: 全体の13.1%を占め、他のプロセスと比べても明らかに負荷が大きい
システムリソースの確認方法について
Linuxにある程度詳しくても、システムリソースの状態を把握する手順がわからないという方もいらっしゃいます。以下の方法で、CPUやメモリの使用状況を簡単に確認できます。
- top コマンド
ターミナルでtop
を実行すると、リアルタイムのプロセス情報やシステム全体のCPU・メモリ使用率が表示されます。表示されたリストから、どのプロセスがどの程度リソースを使用しているかを確認できます。 - htop コマンド
htop
はtop
の視覚的にわかりやすい代替ツールです。インストールされていない場合は、sudo apt install htop
(Ubuntuなどの場合)でインストールし、htop
と入力するだけで使用できます。 - free コマンド
メモリの全体状況を確認するにはfree -m
を実行すると、使用中・空きメモリがMB単位で表示されるため、現在のメモリ消費が把握しやすいです。
これらのコマンドを組み合わせることで、サーバーがどのプロセスによってどの程度のリソースを消費しているかを効率的に確認できます。特に、VSCodeのRemote – SSH利用時には、Node.jsプロセスがバックグラウンドで動作していることが原因でリソースの使用量が増えている可能性があるため、これらのツールを使って状況を把握することが重要です。
以上の情報から、nodeプロセスがシステムリソースに大きな影響を与えている可能性が考えられます。とはいえ、私のサーバーはFastAPIとUvicornを使ったPythonの実装であり、Node.js自体は全く利用していません。サーバーの起動にはPythonのみが関わっているので、Node.jsのプロセスが動作している理由は別の用途、もしくはVSCodeのリモート接続による影響かもしれません。
実際、どうやらVSCodeのRemote – SSH機能や、一部拡張機能がバックグラウンドでNode.jsを利用しているようです。Node.jsはJavaScriptの実行環境で、VSCodeのエディタ機能や言語サーバーなど、便利な機能を支えるために内部で動作しているため、直接利用していなくても、リモート接続時のメモリ使用量が増えることがあります。ちなみに、TeraTermで接続した場合は、メモリ使用量が半分程度に抑えられていることからも、VSCode関連のプロセスが影響していると考えられます。
以上の理由から、VSCodeでSSH接続しているときに、Node.jsプロセスが起動してメモリの消費が増えているのは、快適なリモート開発環境を提供するための必要な処理と捉えるのが良いでしょう。
エラー対策
証明書取得時に以下のエラーになりました。
o certbot certonly --standalone -d stellar.minokamo.tokyo
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Enter email address (used for urgent renewal and security notices)
(Enter 'c' to cancel): summer@minokamo.xyz
Please read the Terms of Service at

Page not found You must
agree in order to register with the ACME server. Do you agree?
(Y)es/(N)o: y
Would you be willing, once your first certificate is successfully issued, to
share your email address with the Electronic Frontier Foundation, a founding
partner of the Let's Encrypt project and the non-profit organization that
develops Certbot? We'd like to send you email about our work encrypting the web,
EFF news, campaigns, and ways to support digital freedom.
(Y)es/(N)o: y
Account registered.
Requesting a certificate for stellar.minokamo.tokyo
Certbot failed to authenticate some domains (authenticator: standalone). The Certificate Authority reported these problems:
Domain: stellar.minokamo.tokyo
Type: unauthorized
Detail: 34.71.218.202: Invalid response from http://stellar.minokamo.tokyo/.well-known/acme-challenge/ta2ZWJxbF37vku_iX0pSC8FoBluxEimCphE2NHfViYY: "\n\n\n\n \n <meta name=\"viewport\" content=\"width=device-width, ini"
Hint: The Certificate Authority failed to download the challenge files from the temporary standalone webserver started by Certbot on port 80. Ensure that the listed domains point to this machine and that it can accept inbound connections from the internet.
Some challenges have failed.
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.
原因を照査していると心当たりがありました。現状では、このサーバーはCloudflareに登録しているドメインであることです。もっとも今回のドメインはCloudflareに登録していないものを使用します。
問題点とその解決策
1. サーバーがポート 8080 で動作している
- 問題:
Certbot の standalone モードは、ACME チャレンジのために ポート 80 を使います。しかし、現在のweb.py
の設定では FastAPI サーバーが ポート 8080 で起動しているため、Certbot が正しく動作できません。 - 解決策:
Certbot を実行する前に、現在の Python サーバーを一時停止し、Certbot の standalone モードで証明書を取得することにします。
その後、サーバーを再起動します。
sudo systemctl stop my-python-service # サーバーを一時停止(systemdの場合)
sudo certbot certonly --standalone -d stellar.minokamo.tokyo
sudo systemctl start my-python-service # サーバーを再起動
※ もしくは、FastAPI サーバーをポート 80 で起動し、–webroot モードで証明書を取得する方法もあります。ポート転送で外部の80番ポートから8080番にリダイレクトしている場合でも、Certbotのstandaloneモードは実際に80番ポートを直接バインドしてACMEチャレンジを実行する必要があります。
つまり、現在のPythonサーバーが8080番で動いていても、iptablesで80番へのアクセスが8080番に転送されるだけで、Certbotがstandaloneモードで80番ポートを使おうとすると、既に何らかのプロセス(例えば、サーバー)が関与しているため、ポートが確保できずに動作しない可能性があります。
そのため、standaloneモードで証明書を取得する場合は、証明書取得中に一時的にサーバーを停止するか、もしくは–webrootモードなど、サーバーを停止せずに証明書取得ができる方法を検討する必要があります。
2. –standalone モードではなく –webroot モードを使う方法
- 問題:
現在のweb.py
ではすべてのリクエストが同じ HTML コンテンツに返されるようになっており、Certbot のチャレンジ用ファイル(.well-known/acme-challenge/)へのアクセスを妨げる可能性があります。 - 解決策:
–webroot モードを利用し、チャレンジファイルを配置するディレクトリを用意します。
以下の手順で設定してください。/var/www/html/
に Certbot 用のディレクトリを作成し、権限を設定します。
sudo mkdir -p /var/www/html/.well-known/acme-challenge
sudo chown -R $USER:$USER /var/www/html
web.py
に、.well-known/acme-challenge/
へのアクセス用ルートを追加します。
from fastapi.staticfiles import StaticFiles
# 他のコードの下部など適当な場所に追加
app.mount("/.well-known", StaticFiles(directory="/var/www/html/.well-known"), name="well-known")
以下のコマンドで Certbot を実行します。
sudo certbot certonly --webroot -w /var/www/html -d stellar.minokamo.tokyo

3. Cloudflare の設定について(対象ドメインは minokamo.tokyo ではなく stellar.minokamo.tokyo)
- ポイント:
今回、Cloudflare は使用していないため、この項目は Cloudflare 経由の HTTP アクセスブロックの問題は発生しません。
※ 以前は別のドメインで運用していたものを stellar.minokamo.tokyo に変更したため、Cloudflare の設定は関係ありません。
4. ssl_certfile の指定が誤っている
- 問題:
現在、web.py
のuvicorn.run()
ではssl_certfile
にcloudflare_origin.pem
を指定していますが、Let’s Encrypt で取得した証明書に変更する必要があります。 - 解決策:
証明書取得後、uvicorn.run()
の設定を以下のように修正します。
ssl_certfile="/etc/letsencrypt/live/stellar.minokamo.tokyo/fullchain.pem",
ssl_keyfile="/etc/letsencrypt/live/stellar.minokamo.tokyo/privkey.pem"
その後、FastAPI サーバーを再起動します。
sudo systemctl restart my-python-service
実は、ポート 80 でページが表示されます。これは、正常な状態ですが、問題はその状態がCertbotのstandaloneモードで証明書を取得する際に影響する点です。
具体的には、既にFastAPIサーバーがポート80で動作している場合、Certbotが一時的なWebサーバーとしてポート80をバインドできず、ACMEチャレンジに正しく応答できなくなる可能性があります。
そのため、証明書取得時は以下のいずれかの対策が必要です:
- サーバーを一時停止してCertbotを実行
既存のFastAPIサーバーを一時的に停止し、Certbotのstandaloneモードで証明書を取得する。取得後にサーバーを再起動します。 - –webrootモードを使用する
FastAPIで.well-known/acme-challenge/
のルートを設定し、既存サーバーがそのディレクトリを提供できるようにする。これにより、既存サーバーを停止せずに証明書取得が可能になります。
つまり、ポート80でアクセスできること自体は問題ではありませんが、証明書取得の際にはポート80の使用状況が影響するため、上記の対策が必要になります。
例え話:建物の扉
- ポート80(標準の扉)
- 例え: 80番ポートは、建物の正面玄関のようなものです。
- 説明: 通常、Webサイトにアクセスするとき、ブラウザは特に指定がなくても「正面玄関(ポート80)」から入るように設計されています。
- メリット: 一般の人は玄関から入るので、特に番号を意識する必要はありません。
- ポート8080(別の扉)
- 例え: 8080番ポートは、裏口やサイドドアのようなものです。
- 説明: これは開発者や特定の目的のために使われることが多く、通常はアクセスする際にURLに「:8080」と付け加える必要があります。
- 注意: もしサーバーが8080番で動作している場合、一般の人が「http://あなたのドメイン」と入力しても裏口(8080番)は開かないので、特別な設定が必要になります。

どうしてこれが問題になるのか?
- 証明書の取得時の話:
Certbot の standalone モード は、「正面玄関(ポート80)」を使って証明書のチャレンジ(認証)を行います。- もしサーバーが別の扉(8080番)で動作していると、証明書を取得するために必要な正面玄関(ポート80)が使われていないか、他のサービスが邪魔をしてしまう可能性があります。
- そのため、証明書の取得時には、正面玄関(ポート80)を使うようにサーバーを調整する必要があります。
大きな勘違いと忘れていたこと
最近、自分のサーバーで「http://stellar.minokamo.tokyo/」にアクセスすると、ちゃんとページが表示されるのに「ポート8080でサーバーを起動しているのにどうしてアクセスできるのか?」と大いに混乱していました。
実は、以前にiptablesを使ってポート80へのアクセスをポート8080に転送するポートフォワーディングの設定をしていたのを、すっかり忘れていたのです。

たとえば、以下のコマンドで確認した結果、
sudo iptables -t nat -L -n -v
の出力には、
Chain PREROUTING (policy ACCEPT 6341 packets, 394K bytes)
pkts bytes target prot opt in out source destination
4041 238K REDIRECT 6 -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 redir ports 8080
とあり、ポート80へのTCPリクエストがポート8080にリダイレクトされていることが確認できました。
これにより、外部から「http://stellar.minokamo.tokyo/」でアクセスすると、iptablesが自動的にポート80からポート8080に転送しているため、FastAPIサーバーが正しく応答し、ページが表示されるのです。
また、もしUFW(Uncomplicated Firewall)を使っている場合でも、内部ではiptablesを利用しているため、同様のポートフォワーディング設定が行われている可能性があります。
UFWの状態を確認するには、以下のコマンドで詳細なルールが表示されるので、ポート転送の設定がないか確認してみましょう。
sudo ufw status verbose
このように、iptablesやUFWを利用してポートフォワーディングの設定をしている場合、サーバー自体はポート8080で動作していても、外部からはポート80でアクセスできるようになっているのです。
この点を忘れていたために大きな混乱を招いていましたが、設定内容を確認できたことで原因が明らかになりました。
最適と思われる方法
私の環境では上記のように、ポート80へのリクエストがiptablesで自動的にポート8080に転送されるため、サーバーを停止することなく証明書を更新できる**–webroot** モードの利用が最もおすすめです。
理由:
- 停止不要: サーバーを一時停止せずに証明書更新ができるので、サービスダウンを防げます。
- 既存の環境に対応: 現在、リダイレクトが設定されている状態でも、FastAPIサーバーが正しくリクエストを処理しているため、ウェブルートにチャレンジファイルを配置すれば問題なく動作します。
対策手順の概要:
/var/www/html/.well-known/acme-challenge
ディレクトリを作成し、適切な権限を設定します。- FastAPIサーバーに、
.well-known/acme-challenge
へのリクエストをこのディレクトリから提供するルートを追加します。 - Certbot を
--webroot
モードで実行して証明書を取得します。
この方法で、現在のiptablesのポート転送設定をそのまま利用しながら、証明書の取得や更新をスムーズに行うことができます。
具体的には、FastAPI アプリケーションの初期設定部分で、他のルートを定義する前に追加しました。たとえば、以下のように、app.mount("/static", ...)
の直後に記述するのがおすすめです。
app = FastAPI()
# 既存の静的ファイルのマウント
app.mount("/static", StaticFiles(directory="static"), name="static")
# .well-known/acme-challenge 用ディレクトリをマウント
app.mount("/.well-known", StaticFiles(directory="/var/www/html/.well-known"), name="well-known")
この位置に配置することで、.well-known
以下のリクエストは、他のキャッチオールルートに到達する前に静的ファイルとして正しく提供されるようになります。
上記のことを全体の流れとしてまとめると手順は以下になります。実際に行った手順です。
ディレクトリの作成とFastAPIの設定更新
Certbotのチャレンジ用に、以下のコマンドでディレクトリを作成し、権限を設定します。
sudo mkdir -p /var/www/html/.well-known/acme-challenge
sudo chown -R $USER:$USER /var/www/html
その後、web.py
に以下を追加します(たとえば、既存の静的ファイルのマウント設定の直後などに):
from fastapi.staticfiles import StaticFiles
# 例:
app.mount("/static", StaticFiles(directory="static"), name="static")
app.mount("/.well-known", StaticFiles(directory="/var/www/html/.well-known"), name="well-known")
サービスの再起動
変更後は必ずサービスを再起動します。
sudo systemctl restart web.service
これで、新たなルート設定が反映され、.well-known
以下のリクエストが正しく処理されるようになります。
Certbot で証明書の取得
次に、--webroot
モードで証明書を取得します。
sudo certbot certonly --webroot -w /var/www/html -d stellar.minokamo.tokyo
成功例
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for stellar.minokamo.tokyo
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/stellar.minokamo.tokyo/fullchain.pem
Key is saved at: /etc/letsencrypt/live/stellar.minokamo.tokyo/privkey.pem
This certificate expires on 2025-06-09.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
* Donating to ISRG / Let's Encrypt: https://letsencrypt.org/donate
* Donating to EFF: https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
証明書の適用
証明書取得後、web.py
の uvicorn.run()
の設定部分で、以下のようにsslのパスを更新します。
ssl_certfile="/etc/letsencrypt/live/stellar.minokamo.tokyo/fullchain.pem",
ssl_keyfile="/etc/letsencrypt/live/stellar.minokamo.tokyo/privkey.pem"
再度サービスを再起動
変更を反映させるため、再びサービスを再起動します。
sudo systemctl restart web.service
この流れであれば、まずはCertbotのチャレンジが正しく機能し、その後取得した証明書がFastAPIサーバーに適用されるので、HTTPS通信が可能になります。
ポートフォワーディングの設定
HTTPでは表示されるのにHTTPSで表示されない場合、主な原因はサーバーが実際にはポート8080で動作しているため、HTTPSの標準ポートである443へのアクセスが正しく転送されていないことが考えられます。
例えば、HTTPアクセスの場合はiptablesのルールでポート80から8080に転送されているので問題なく表示されますが、HTTPSの場合はポート443へのアクセスが行われるため、同様の転送ルールが必要です。
対策としては以下のいずれかを検討してしょう。
iptablesで443→8080の転送ルールを追加する
以下のコマンドを実行して、HTTPSのリクエスト(ポート443)もポート8080に転送するように設定します。
sudo iptables -t nat -A PREROUTING -p tcp --dport 443 -j REDIRECT --to-port 8080
※ このルールを追加することで、外部からのHTTPSアクセスも内部のFastAPIサーバーに届くようになります。
FastAPIサーバーを直接ポート443で起動する
ただし、443は特権ポートであるため、root権限が必要になります。セキュリティや運用の観点からはiptablesでの転送が推奨されるケースが多いです。
これでもSSLでアクセスできない場合、証明書があるディレクトリにアクセスしてみます。この場合拒否されています。
ls -l /etc/letsencrypt/live/stellar.minokamo.tokyo/
ls: cannot access '/etc/letsencrypt/live/stellar.minokamo.tokyo/': Permission denied
このエラーメッセージは、Let’s Encrypt の証明書ファイルへのアクセス権限に問題があることを示しています。つまり、サーバープロセス(uvicorn)が証明書ファイルを読み取れず、結果としてHTTPS接続がうまくいっていない可能性が高いです。
対策例:
- 証明書ファイルのパーミッションを確認・変更する
証明書ファイル(例: fullchain.pem、privkey.pem)の所有者やパーミッションが適切か確認してください。- サーバープロセスを実行しているユーザー(例: www-data や他の専用ユーザー)が証明書ファイルに読み取り権限を持っていることを確認します。
- もし必要なら、適切なグループに所属させるか、ACLを使って読み取り権限を付与するなどの対応を検討します。
- プロセスの実行ユーザーを確認する
サービスがどのユーザー権限で動作しているかを確認し、そのユーザーが /etc/letsencrypt/live/stellar.minokamo.tokyo/ 内のファイルにアクセスできるように設定します。
例えば、一時的に以下のように権限を変更してテストすることもできます(ただし、セキュリティには十分注意してください):
sudo chmod -R o+r /etc/letsencrypt/live/stellar.minokamo.tokyo/
このように設定することで、全ユーザーが読み取り可能になりますが、本番環境ではセキュリティリスクを考慮し、適切な方法で必要なユーザーにのみアクセス権を付与するようにします。
プロセスの実行ユーザーを確認する方法は?
1. ps コマンドを使う方法
例えば、uvicorn を使用している場合、以下のコマンドを実行すると、どのユーザーが uvicorn プロセスを実行しているか確認できます。
ps aux | grep uvicorn
このコマンドは、実行中の uvicorn プロセスの一覧を表示し、「USER」列に実行ユーザーが表示されます。
2. systemctl コマンドを使う方法
もし FastAPI サーバーを systemd で管理している場合は、サービスの状態を確認することで実行ユーザーを知ることができます。例えば、サービス名が web.service
の場合、以下のコマンドを実行してください。
sudo systemctl status web.service
この出力の中に、サービスの設定内容や実行ユーザーに関する情報が表示されることがあります。特に、サービスファイル(通常は /etc/systemd/system/web.service
など)内に User=
の設定がある場合、そのユーザーがプロセスを実行しています。
1と2でユーザーの確認をすると以下のような表示がされました。
ps aux | grep uvicorn
minok 14277 0.2 3.8 133764 37884 ? Ssl 10:07 0:05 /home/minok/web/venv/bin/python3 /home/minok/web/venv/bin/uvicorn web:app --host 0.0.0.0 --port 8080 --proxy-headers --forwarded-allow-ips=*
minok 14561 0.0 0.2 4036 2100 pts/4 S+ 10:40 0:00 grep --color=auto uvicorn
psの出力から判断すると、FastAPIサーバー(uvicorn)は minok ユーザーで実行されています。
sudo systemctl status web.service
● web.service - Uvicorn instance to serve web application
Loaded: loaded (/etc/systemd/system/web.service; enabled; preset: enabled)
Active: active (running) since Tue 2025-03-11 11:40:28 UTC; 7min ago
Invocation: 8b56497376e7426d9dacc54d7f45d5a9
Main PID: 569 (uvicorn)
Tasks: 1 (limit: 1063)
Memory: 35.2M (peak: 44.9M)
CPU: 2.563s
CGroup: /system.slice/web.service
└─569 /home/minok/web/venv/bin/python3 /home/minok/web/venv/bin/uvicorn web:app --host 0.0.0.0 --port 8080 --proxy-headers "--forwarded-allow-ips=*"
Mar 11 11:40:28 instance-20250112-143947 systemd[1]: Started web.service - Uvicorn instance to serve web application.
Mar 11 11:40:38 instance-20250112-143947 uvicorn[569]: INFO: Started server process [569]
Mar 11 11:40:38 instance-20250112-143947 uvicorn[569]: INFO: Waiting for application startup.
Mar 11 11:40:40 instance-20250112-143947 uvicorn[569]: INFO: Application startup complete.
Mar 11 11:40:40 instance-20250112-143947 uvicorn[569]: INFO: Uvicorn running on http://0.0.0.0:8080 (Press CTRL+C to quit)
Mar 11 11:42:21 instance-20250112-143947 uvicorn[569]: INFO: 45.149.229.66:51303 - "GET / HTTP/1.1" 200 OK
Mar 11 11:46:26 instance-20250112-143947 uvicorn[569]: INFO: 104.248.229.49:42432 - "GET /aaa9 HTTP/1.1" 200 OK
Mar 11 11:46:40 instance-20250112-143947 uvicorn[569]: INFO: 104.248.229.49:34258 - "GET /aab8 HTTP/1.1" 200 OK
Mar 11 11:47:01 instance-20250112-143947 uvicorn[569]: INFO: 104.248.229.49:38610 - "GET / HTTP/1.1" 200 OK
minok
ユーザーがLet’s Encryptの証明書ファイルを適切に読み取れるようにするには、以下の手順で権限を設定します。
1. 現在の所有者と権限を確認
まず、Let’s Encrypt の証明書ディレクトリの所有者と権限を確認します。
sudo ls -l /etc/letsencrypt/live/stellar.minokamo.tokyo/
また、/etc/letsencrypt/archive/stellar.minokamo.tokyo/
のディレクトリにも実際の証明書が格納されているため、同様に確認します。
sudo ls -l /etc/letsencrypt/archive/stellar.minokamo.tokyo/
表示例
-rw-r--r-- 1 root root 1294 Mar 11 10:02 cert1.pem
-rw-r--r-- 1 root root 1566 Mar 11 10:02 chain1.pem
-rw-r--r-- 1 root root 2860 Mar 11 10:02 fullchain1.pem
-rw------- 1 root root 241 Mar 11 10:02 privkey1.pem
通常、所有者は root
で、権限は 700
や 600
になっているため、minok
ユーザーが直接アクセスできません。
方法1:minokユーザーに証明書の読み取り権限を個別付与(簡易版)
もし特定の証明書だけ minok
に読み取り権限を与えたい場合は、以下のように設定します。
sudo chown -R minok:minok /etc/letsencrypt/live/stellar.minokamo.tokyo/
sudo chown -R minok:minok /etc/letsencrypt/archive/stellar.minokamo.tokyo/
sudo chmod -R 755 /etc/letsencrypt/live/stellar.minokamo.tokyo/
sudo chmod -R 755 /etc/letsencrypt/archive/stellar.minokamo.tokyo/
この方法は簡単ですが、minok
が証明書を管理することになり、Let’s Encrypt の更新プロセスと競合する可能性があるため、あまり推奨されません。
方法2:ACL を使って minok ユーザーにのみアクセスを許可する
ACL を利用すると、ディレクトリ全体のパーミッションを大きく変更せずに、特定のユーザーに追加のアクセス権を与えられます。以下は、minokユーザーが証明書ファイルにアクセスできるようにACLを設定する、実際に動作する方法です。
以下の手順は、/etc/letsencrypt ディレクトリ全体(live と archive の両方)に対して、minokユーザーに「読み取り」と「実行(ディレクトリ内移動)」の権限を追加する方法です。
箇条書きでまとめると、ACLの利点は以下になります。
- 特定ユーザーへの権限追加
ACL を使えば、ディレクトリ全体のパーミッションを大きく変えることなく、特定のユーザーに対してアクセス権を付与できます。 - 柔軟なアクセス制御
既存のファイルやディレクトリの権限設定に影響を与えず、細かいアクセス制御が可能です。 - セキュリティの維持
全体のパーミッションを変更するリスクを避けつつ、必要なユーザーにのみ追加のアクセス権を与えられます。
手順
/etc/letsencrypt 自体へのアクセスを許可する
sudo setfacl -m u:minok:rx /etc/letsencrypt
/etc/letsencrypt/live とその中の対象ディレクトリへのアクセスを許可する
sudo setfacl -m u:minok:rx /etc/letsencrypt/live
sudo setfacl -R -m u:minok:rx /etc/letsencrypt/live/stellar.minokamo.tokyo
/etc/letsencrypt/archive とその中の対象ディレクトリへのアクセスを許可する
sudo setfacl -m u:minok:rx /etc/letsencrypt/archive
sudo setfacl -R -m u:minok:rx /etc/letsencrypt/archive/stellar.minokamo.tokyo
補足
- これにより、minokユーザーは /etc/letsencrypt 以下のディレクトリに対して「読み取り」および「実行(中のファイルにアクセスするための権限)」が付与されます。
- シンボリックリンクはデフォルトでACLの設定を引き継がない場合がありますが、上記の設定で実際のリンク先(archive 内のファイル)にも適用されるため、minokユーザーは証明書ファイルにアクセスできるはずです。
- 設定後、以下のコマンドで権限が正しく設定されているか確認できます。
sudo getfacl /etc/letsencrypt/live/stellar.minokamo.tokyo/fullchain.pem
表示されたメッセージ
getfacl: Removing leading '/' from absolute path names
# file: etc/letsencrypt/live/stellar.minokamo.tokyo/fullchain.pem
# owner: root
# group: 1004
user::rw-
user:minok:r-x
group::r--
mask::r-x
other::r--
設定を反映させた後、uvicorn(FastAPI)サービスを再起動します。
sudo systemctl restart web.service
この方法で、minokユーザーにのみ必要なアクセス権を追加できるので、証明書ファイルのセキュリティを大幅に緩めずにSSL接続を有効にできるはずです。
以下のメッセージが表示された場合
sudo setfacl -m u:minok:rx /etc/letsencrypt
sudo: setfacl: command not found
「setfacl: command not found」というエラーは、setfacl コマンドがインストールされていないことを示しています。Ubuntuでは、ACL管理ツールがデフォルトでインストールされていない場合があるため、まずACLパッケージをインストールする必要があります。
以下のコマンドを実行しましょう。
sudo apt install acl
ACLコマンドは、通常のパーミッション設定コマンドと比べると、少し独自の書き方をします。最初は慣れにくいかもしれませんが、個別のユーザーやグループに対して細かくアクセス権を設定できるので、セキュリティや柔軟性が向上します。例えば、setfaclコマンドを使えば、特定のユーザーにのみ読み書きの権限を追加することができるんです。
3. 動作確認
HTTPS でアクセスできるかを確認します。
curl -I https://stellar.minokamo.tokyo/
エラーが出ないかを確認し、ブラウザからもHTTPS接続が成功するか試してみましょう。
それでも接続ができない場合は?
大きな勘違いがあった原因について
ここまで設定を進めてきましたが、実は重要な勘違いがありました。
FastAPI(uvicorn)をHTTPS(SSL)対応にするためには、uvicorn起動時に証明書を指定して起動しなければなりません。
私の場合、systemdサービス(web.service
)に登録していたuvicornの起動コマンドに、SSL証明書の指定が完全に抜けていたのです。そのため、何度試してもHTTPSアクセスができず、混乱していました。
実際の誤っていた設定は以下の通りです。
間違った設定例(web.serviceのExecStart)
ExecStart=/home/minok/web/venv/bin/uvicorn web:app --host 0.0.0.0 --port 8080 --proxy-headers --forwarded-allow-ips="*"
上記の設定ではSSL証明書を指定していないため、HTTPでしかサーバーが起動していません。
正しい設定方法
SSL対応を正しく行うには、以下のようにsystemdサービスファイルを修正し、uvicorn起動時に証明書を明示的に指定する必要があります。
修正版(web.serviceのExecStart)
ExecStart=/home/minok/web/venv/bin/uvicorn web:app \
--host 0.0.0.0 \
--port 8080 \
--proxy-headers \
--forwarded-allow-ips="*" \
--ssl-certfile /etc/letsencrypt/live/stellar.minokamo.tokyo/fullchain.pem \
--ssl-keyfile /etc/letsencrypt/live/stellar.minokamo.tokyo/privkey.pem
修正前は以下になっていました。該当部分だけを修正します。
[Unit]
Description=Uvicorn instance to serve web application
After=network.target
[Service]
User=minok
Group=minok
WorkingDirectory=/home/minok/web
Environment="PATH=/home/minok/web/venv/bin"
ExecStart=/home/minok/web/venv/bin/uvicorn web:app --host 0.0.0.0 --port 8080 --proxy-headers --forwarded-allow-ips="*"
[Install]
WantedBy=multi-user.target
設定変更後は、systemdをリロードし、サービスを再起動します。
sudo systemctl daemon-reload
sudo systemctl restart web.service
再起動後、以下のログが表示されればSSLでの起動に成功しています。
Uvicorn running on https://0.0.0.0:8080 (Press CTRL+C to quit)
これで、iptablesで設定したポート443→8080への転送が有効に機能し、HTTPSで問題なくアクセスできるようになります。
最後に、iptablesの設定を再起動後も永続化する場合は、以下のコマンドでルールを保存します。これは盲点でした。再起動後に設定が消えてしまっていたのです。
sudo apt install iptables-persistent
sudo netfilter-persistent save
以上で、SSL設定に関するすべての問題が解決し、安全で快適なHTTPSアクセスが可能になります。