Let’s EncryptによるWWWサーバーのHTTPS化

PHP騒動でサーバーを再構築したついでにこれまで面倒で放置していたWWWサーバーのHTTPS化をしようかと思ったら、どうやらLet’s Encryptを使うとほぼ全自動でHTTPS化に必要な証明書を無料で発行してもらえるらしい。

というわけでDebianでLet’s Encryptを利用する手順を記録しておく。ただし、この手順はWWWサーバーが発行しようとしているFQDNでHTTPで接続できる必要がある。というのも証明書を取得しようとしている相手が本当にそのサーバーの管理者であるかどうかをLet’s Encrypt側からHTTP接続して確認する作業が途中に挟まれるからである。そうじゃない場合もいろいろと方法があるみたいだが、全自動で設定というわけにはいかないので、すでにFQDNでHTTPを公開しているサーバーにおける手順として記録する。

まずはLet’s Encryptの自動設定パッケージとApacheをサーバーにしている場合にApacheの設定を自動でやってくれるスクリプトをインストールする。

sudo apt install certbot python3-certbot-apache

インストールが完了したらcertbotを実行する。オプションでこのサーバーのApacheの設定を自動でやるように指示をする。途中の過程でWWWサイトのルートに認証用のファイルを自動で置いてLet’s Encrypt側から読み出す過程があるので管理者権限で実行すること。

sudo certbot –apache

対話的に進めていくとApacheでServerNameが設定されているとそのドメインで証明書取得するかと聞かれるので取得したいドメインを選ぶ。あとはcertbotが自動で証明書の取得、適切なディレクトリへの配置とシンボリックリンクの作成、Apacheのmod_sslの有効化とVirtual Hostの設定まで済ませてくれる。以前はHTTPで接続したときにHTTPSに強制リダイレクトするかどうかを選択できたようだが、今はもう勝手に強制リダイレクトを設定されるようだ。

たったこれだけの手順でWWWサーバーのHTTPS化は完了する。実際にHTTPでアクセスすると自動的にHTTPSに変更されて有効な証明書を使った安全な接続であることがブラウザで確認できた。ルーターの静的NATでHTTPSのポート443を内部のWWWサーバーにマップすることを忘れないこと。でないと強制HTTPSリダイレクトなので外部から全くアクセスできなくなってしまう。

Let’s Encryptで発行される証明書は期限が90日(じきにもっと短くされるらしい)しかなく、期限切れまでにcertbotで更新する必要があるが、Debianのcertbotパッケージはインストール時にsystemdに期限を監視して自動で更新するタイマーを設定してくれ、更新が実行されるとApacheも自動でリロードされるようだ。一応、残り30日以下になると自動更新が実行されるみたいなので60日過ぎてからもHTTPSで問題なく接続できれば自動更新にまかせて大丈夫だろう。

WordPressのサイトネットワークアップグレードがタイムアウトする問題

巷を大騒ぎさせたPHPセキュリティ問題、多分に漏れずこのサーバーでもPHPが古いバージョンだったため、急遽対応を余儀なくされた。そこで、前々から考えていたラズパイを利用したサービス単位のマイクロサーバーのクラスター構成へと移行することにし、ラズパイのDebian Trixie上に最新のPHPを含むLAMP環境を構築し、その上にWordPress環境を再構築して旧サーバーのコンテンツをインポートした。

問題なく再構築できたと確信したので、外部公開するサーバーを変更することにした。といってもルーターでPort 80の静的NATの振り向け先を旧サーバーから新サーバーに変更するだけなのだが。実際、外部からアクセスしても何の問題もなく表示されるので無事サーバーの更新は完了したと思ったのだが、WordPressのメジャーバージョンが上がった時に行わなくてはならないサイトネットワークのアップグレード操作がConnection timed outのエラーを表示して失敗してしまうことが判明した。

ネットでConnection timed outのエラーが出たときの対処方法を調べると、PHPの割り当てメモリを増やすだとかPHPの実行時のTimeout時間を増やすといった対処法が出てくるが、実際に設定してみても最初のサイトのデータベースを更新しようとしたタイミングで相変わらず同じエラーで失敗する。どうやらこのエラーはPHPの実行が遅すぎて起きているわけではないらしい。

と、そこでこの環境の特殊性からピンときた。WordPressはもともとFQDNで直接アクセスできる環境であることを前提に設計されており、WordPressの表示される内容の応答もすべてFQDNを含むURLでしか返ってこない。そのため、FQDNからリゾルブされるIPアドレスが直接インターフェースに割り当てられている環境なら何の問題もないのだが、NAT内にサーバーを設置するような環境ではNAT内のアクセスではhosts等でFQDNからNAT内のプライベートアドレスにリゾルブされるようにしておかないとうまく表示できない。もし、サイトネットワークのアップグレードのルーチンがFQDNを使ってWordPress内の別のルーチンを叩いていたとしたら、そりゃリゾルブしたアドレスには直接接続できないのでうまくいかないよな。

というわけで、新サーバーのhostsファイルにFQDNに対して127.0.0.1を返すように設定する。これでもしWordPress内のルーチンがFQDNで他のルーチンを呼び出そうとした時もちゃんとlocalhostに接続してくれるようになるはずだ。

で、実際この状態でサイトネットワークのアップグレードを実行してみると、すんなりとすべてのサイトのアップグレードが完了した。やはり原因はFQDNでローカルにアクセスできない問題であった。

というわけでWordPressを運用しているサーバーがFQDNからリゾルブされるIPアドレスを割り当てられていない場合は、FQDNをリゾルブすると127.0.0.1が返ってくるようにhostsファイルを設定しようという教訓でした。

Debian Bookwormを入れたRaspberry Piが数日でWiFi経由で見えなくなる問題

WiFi接続で運用しているRaspberry Pi 3B+が、起動直後は問題なくWiFi経由でアクセスできるのに、数日たつとWiFiのアクセスポイントの接続機器リストから消え、WiFi経由でpingを打っても反応しなくなる問題が発生した。ヘッドレスで運用しているので、こうなってしまうと電源を切って入れなおすしか復活方法がなく、しばらくは遠隔で電源をオンオフできる電源スイッチを利用して症状がでると強制的に再起動していた。

しかし、この状態はどう考えてもおかしい。問題が始まったのはそれまでBullseyeだったベースのシステムイメージをBookwormベースにアップデートされたイメージに更新してからだ。ということはBookwormで何かがおかしくなったとしか思えない。そこで、IoTサーバー用の設定済みなイメージではなく、ラズパイ公式から配布されている素のBookwormをインストールして様子をみることにしたところ、やはりその状態でも数日でWiFi経由で見えなくなる現象が再発した。つまり犯人はBookwormだ。

さて、BookwormにしたらWiFiに問題が出たという情報はないかと探したところ、BookwormでRaspbianのWiFiインターフェースのPower Managementがオンにされたという情報が見つかった。WiFiのPower Managementがオンになると、無通信状態ではWiFiが省電力モードになってしまう。これはデスクトップ端末として使っているのであれば、通信が必要になればその時点でまたWiFiが再接続されるから問題ないが、サーバーとして使っている場合は省電力状態では外からのパケットに答えなくなってしまうので都合が悪い。というわけでこれが原因の可能性が高いので、WiFiのPower Managementをオフにすることにした。

実際、WiFi接続の状態を表示してPower Managementの設定を確認する。

iwconfig wlan0

すると Power Management:on と表示されるので確かにオンになっている。これを恒常的にオフにする。Bookworm以降はNetwork Manager経由でしか設定をいじれないので、Network Managerで対話的に設定を変更しなければならない。

まずはNetwork Managerを使ってWiFi接続の接続名を取得する。

nmcli connection show

すると接続名とUUIDと対応しているデバイスの一覧が表示される。ここでWiFiインターフェースであるwlan0に対応している接続名をみると preconfigured という接続名になっていた。

ここでこの preconfigured という接続の設定を変更してPower Managementをオフに設定する。

nmcli connection modify preconfigured 802-11-wireless.powersave 2

ネットで検索するとこの接続名の部分は接続しているSSIDを書くとされているものばかりがヒットするが、実際にやってみるとSSIDではそんな接続ありませんと拒否されて、ネットワークインターフェースの接続名にすると無事に設定が変更された。最後の数値は変更後の設定値だが、これが2が無効で3が有効という直感的じゃない設定値になっているので要注意だ。

これで設定が変更されたのでiwconfigで無事にPower Managementがオフになっていることを確認しよう。この操作はNetwork Manager経由でシステムの設定を変更する操作なので、再起動後もこの設定が保持される。再起動した後もiwconfigで設定を念のため確認してPower Managementがオフになったままであることが確認できた。

この設定変更後、1週間程度様子を見てみたが外部からの接続でちゃんと見えている状態が続いているので、やはりPower Managementが問題の原因だったようだ。

これで安心していろいろ設定ができるなあと思ってたら、いよいよDebianの次のバージョンのTrixieのリリースがせまってきた。う~ん、やはりTrixieが出てからTrixieで再構築した方がよさそうだ。


DebianのaptでリポジトリのGPG鍵が警告を出してくる件

OctoPrintのラズパイ用ディストリビューションのOctoPiのベースになるRaspbianがBullseyeからBookwormにアップデートされて新しいイメージが配布された。今回からデフォルトのユーザーやWiFiの設定がRaspberry Pi Imagerのオプションから変更できるようになったので、早速新しいイメージをつかってOctoPi環境を構築しなおした。

ところが、いつも通りapticronで毎日アップデートがあったパッケージを報告させるようにすると、毎度以下のようなエラーが送信されてくるようになった。

W: http://raspbian.raspberrypi.com/raspbian/dists/bookworm/InRelease: Key is stored in legacy trusted.gpg keyring (/etc/apt/trusted.gpg), see the DEPRECATION section in apt-key(8) for details.

Debianはaptで参照するリポジトリのGPG鍵をapt-keyによってこれまで管理してきたが、これはユーザーが追加したリポジトリの鍵をどんどん勝手に追加する仕様だった。万が一、不正なGPG鍵が追加されるとシステムが不正なリポジトリを信用してしまう可能性があるので、今後はapt-keyの鍵リングにDebian公式でない鍵が追加されていると警告を出して、ユーザーが追加する鍵はユーザーが別の場所できっちり自分で管理することを推奨することになったとのこと。

とはいえRaspbianの公式のリポジトリの鍵が不明なユーザー追加の鍵として扱われているのはなんだかなあと思わなくもないが、警告が毎度送られてくるのも面倒なのでapt-keyの鍵リングに格納されているraspbian.raspberrypi.comのGPG鍵を分離することにする。

まずは不正な鍵がどれかを抽出する。

$ apt-key list | grep -A4 “trusted.gpg$”

すると公式でない鍵の情報が以下のように出力される。

pub rsa2048 2012-04-01 [SC]
A0DA 38D0 D76E 8B5D 6388 7281 9165 938D 90FD DD2E
uid [ 不明 ] Mike Thompson (Raspberry Pi Debian armhf ARMv6+VFP) mpthompson@gmail.com

必要なのは鍵のIDなので16進の数字がずらずら並んでいる最後の8桁を抽出する。この場合は90FDDD2Eがこの鍵のIDだ。鍵IDが分かればこの鍵を抽出することができる。一旦この鍵は/tmpに書き出すことにする。鍵の名前はどのリポジトリの鍵かわかるようにFQDNをファイル名に含む形にしておいた。

$ sudo apt-key export 90FDDD2E | sudo gpg –dearmor -o /tmp/raspbian.raspberrypi.com.gpg

鍵が抽出できたら、fileコマンドを使って正しくGPG鍵として認識できるものであるか確認しておこう。

$ file /tmp/raspbian.raspberrypi.com.gpg

問題がなければfileコマンドの出力結果でこのファイルに含まれる内容がGPG鍵であることを表示してくれるはずだ。抽出された鍵が有効であることを確認した後に、apt-keyの鍵リングからこの鍵を削除する。

$ sudo apt-key del 90FDDD2E

最後にユーザーがリポジトリの鍵を追加するべきディレクトリである/etc/apt/trusted.gpg.d/に抽出した鍵を移動させよう。

$ sudo mv /tmp/raspbian.raspberrypi.com.gpg /etc/apt/trusted.gpg.d/

これでapt updateを行ってもapt-keyを使うなという警告が表示されなくなるはずだ。

Raspbianの時刻自動同期設定

どうやら最近のRaspbianではntpdを時刻同期に使っていないらしい。systemd-timesyncdというsystemdサービスに置き換わっているようだ。Raspberry PiはRTCを積んでいないので何らかの時刻同期を起動時に行わないと時刻がでたらめなものになってしまう。古いRaspbianでは起動時にntpdateで一回限りの時刻同期を行っていたように記憶しているが、stretch以降はsystemdサービスで自動的に時刻同期を行うように変更されたようだ。

デフォルトの設定ではこれはDebianが提供している公開NTPサーバーから時刻を同期するように設定されているようだ。日本国内からは遠すぎるので、国内で参照できる公開NTPサーバーに書き換えることにする。

書き換える設定ファイルは/etc/systemd/timesyncd.confだ。このファイル内の[Time]セクションにNTPサーバーのアドレスを指定する場所がある。デフォルトではコメントアウトされているが、これを国内のサーバーのFQDNに書き換える。

[Time]
NTP=ntp.nict.jp
FallbackNTP=ntp.jst.mfeed.ad.jp

メインはNICTの公開NTPサーバーを指定するが、ここに繋がらなかった場合の代替NTPサーバーもFallbackNTPに国内の公開NTPサーバーを指定しておく。

再起動すれば、指定したNTPサーバーから時刻を取得して自動的に同期してくれる。同期されているかどうかは以下のコマンドで確認できる。

sudo systemctl status systemd-timesyncd

表示されるログから時刻同期の状況が確認できる。念のためdateコマンドの出力で正しい今の時刻が吐き出されることを見ておこう。

RaspbianでNetworkManager経由で固定IPを設定する

Raspbian OSの最新版はDebian Bookwormベースになっている。久しぶりに余っていたRaspberry Pi 3B+で遊んでみようとRasbpian Bookwormをインストールしてみたところ、Bookwormのネットワーク関係の設定がNetworkManagerで行うお作法に変更されていたので、設定方法を記録しておく。

まずは設定したいインターフェースの情報を取得する。コマンドラインからNetworkManagerを使うコマンドはnmcliだ。

nmcli connection show

すると設定されているインターフェースの情報が表示される。有線接続のインタフェースはDEVICE欄でeth0を探せばいい。そのDEVICEのNAMEがプロファイル名だ。ロケールをja_JP.UTF-8にしているためか’有線接続 1’という日本語のプロファイル名になっていた。

このプロファイル名を使えばNetworkManagerでどういう情報が設定されているかを詳しく知ることができる。プロファイル名はシングルクウォートで囲むこと。

nmcli connection show ‘有線接続 1’

さて、プロファイル名が分かればコマンドラインから静的IPを設定することができる。管理者権限で実行したいのでsudoコマンドを使う。

sudo nmcli connection modify ‘有線接続 1’ ipv4.method manual ipv4.address 192.168.xxx.xxx/24 ipv4.gateway 192.168.xxx.yyy ipv4.dns 192.168.xxx.zzz

念のため、正しく設定されているかどうか上記の詳細を表示するコマンドで確認しておこう。

GmailはSPFがないとメールを受け取ってくれない問題に対処

このサーバーで運用しているMailman 3のメーリングリスト参加者からどうも一部の会員にメールが届いていないのではないかという苦情。Mailman 3のバウンスログが/var/log/mailman3/bounce.logにあるのでチェックしてみると確かにぞろぞろとバウンスされたという記録が並んでいる。しかし、妙なことにその大半がgmail.comかhotmail.comのアドレスだ。これは、gmail.comがこのサーバーからのメール受け取りを拒否してるのではないか?しかし返ってくるエラーメールについてはMailman 3が自動で破棄してしまうため、実際にどう拒否されてるのか分からない。そこでサーバーから手動で自分のgmail.comなアドレスに向けてテストメールを送ってみると、その理由が判明した。

Reason: 550 5.7.26 This message does not pass authentication checks (SPF and DKIM both

つまるところ発信者認証であるSPFとDKIMのどちらでも発信元が認証できないとはなっから受け取ってくれないという挙動をgmail.comが始めたということらしい。SPFは発信元の情報をDNSのTXTレコードから検証してIPアドレスの比較を行って確認する仕組みで、DKIMはサーバーの公開鍵をこれまたDNSのTXTレコードから取得して、メールがその鍵で署名されているかを検証して確認する仕組みだ。後者はPostfixにさらに小細工をして通過するメールがすべてDKIMで署名されるようにする必要があってとても面倒だ。そこでSPFで認証するために必要な情報をDNSのTXTレコードに登録することにする。幸いなことにこのサーバーが利用しているダイナミックDNSはTXTレコードの書き換えに対応しているので、とりあえずSPFで解決することは可能なようだ。

さて、このサーバーは接続しているプロバイダーが非固定IPからのOutbound Port 25 Blockingを行っているために、SMTPサーバーが直接外部にメールを送信することができない。そこでASAHI-NETのメールサーバーをリレーサーバーにしてすべてのメールをASAHI-NET経由で送信している。なので、SPFレコードに書く内容もこのリレーサーバーが正しく認証されるようにしなければならない。

さて、gmail.comにSPFなしで送ったメールのエラー情報を見ると検証しようとした送信元IPアドレスがこのサーバーのFQDNからのSPF情報で検証できなかったとご丁寧に検証対象になったIPアドレスが記載されている。そこでこのIPアドレスを逆引きしてドメイン名を確認するとhsmtpd-dty.xpsmail.jpというFQDNのようだ。ここから順引きするとAレコードが5つ表示されて、そのうちの一つがエラー情報に書かれていたIPアドレスと一致していたので、ASAHI-NETのSMTPサーバーはAレコードの多重化でラウンドロビンしているタイプのようだ。

今度はこのhsmtpd-dty.xpsmail.jpのSPFレコードを調べてみよう。

dig hsmtpd-dty.xpsmail.jp TXT

これでTXTレコードからSPFの情報を取得できる。詳細はこうだ。

v=spf1 include:spf.xpsmail.jp ~all

どうやらSPFの情報は別のDNSレコードで一括して管理しているようだ。メールサーバーのIPアドレスはいつ変更されるか分からないので、それをカバーできるようなSPFレコードを別に用意してあるということだろう。実際、このspf.xpsmail.jpのTXTレコードを取得してみるとSPFにAレコードの参照がずらずらと書いてある。

ここまで分かれば、あとはこのSPFレコードをこのサーバーのTXTレコードに書き込んで取得できるようにしてあげれば問題ないはずだ。というわけで、このSPFをダイナミックDNSのTXTレコード更新ツールを使って書き込んでからキャッシュされた情報が消えるタイミングであるTTLに書かれた秒数が経過するのをじっと我慢して待つ。そして、おもむろに先ほどやった手動のgmail.comへのメール送信をやってみると、あにはからんや、無事にメールが届いたのでこの対処法で完了である。

やれやれ。(まだhotmail.comにも無事に届くかは確認できていないw)

Mailman3のFedora認証プラグインを無効にする

突然、Mailman3からエラーメールが送られてきた。

[Django] ERROR (EXTERNAL IP): Internal Server Error: /mailman3/accounts/fedora/login/

こんなタイトルのメールだ。どうやらよく読むとFedora認証プラグインを使って認証しようとしたけれど認証に必要な設定がされていないからエラーになったということのようだ。前からPostoriusの画面にFedoraのアイコンが出てるのは気になっていたが、設定しないと機能しないのならこんなもの出しておかれても困るよなあ。

というわけでこれを消す。修正するのは以下のファイル。

/usr/share/mailman3-web/settings.py

この中にINSTALLED_APPS = (で始まるセクションがある。この中のFedora認証プラグインを有効にしている行をコメントアウトする。

django_mailman3.lib.auth.fedora

これでFedora認証プラグインが無効になるので間違って押してエラーを出すこともなくなるはず。

USB-GbEドングルのドライバロード時のエラー

先日、Linuxカーネルがapt経由で更新されてから、logwatchで報告されるレポートに謎のエラーが報告されるようになった。

platform regulatory.0: Direct firmware load for regulatory.db failed with error -2

とりあえずdmesgで表示されるブート時のログを眺めてみるとどうやらax88179のドライバーをカーネルがロードする時にこのエラーが出ているようだ。

このax88179は先日入れ替えたUSB-GbEドングルのTP-Link UE305に搭載されているチップなのだが、このチップのドライバーがカーネルにロードされる時にregulatory.dbなるファイルを読もうとして失敗しているというログのようだ。

このregulatory.dbというのが何かというと、コミュニティベースで各国の通信規制の内容をまとめたデータベースのようで、LinuxではCRDAという仕組みでWiFiアダプターなどを使用する時に国別の使用可能なチャンネルなどの規制をダイナミックに適用するための仕組みの様だ。なぜ有線LANであるax88179でこれを読み込もうとするのかはよく分からないし、実際、これの読み込みに失敗しても通信自体は何の問題もなく行えているのだが、それでも気持ち悪いので対策しよう。

で、解決方法は簡単。CRDAはDebianではcrdaというパッケージで提供されているのだが、これがインストールされていないので追加でインストールする。

apt install crda

全く不要なiwなどのWiFi用のパッケージも追加されるがまあ気にしないでおこう。インストールが完了したら再起動してブートログ中のax88179周りのログを確認すると、無事にエラーを出さずにregulatory.dbの読み込みに成功していた。

platform regulatory.0: firmware: direct-loading firmware regulatory.db

これで再起動するたびに気持ち悪いエラーが報告されずに済む。

Debian busterからbullseyeへのアップグレード

先日、ObsoleteになるPython2.7に依存したmailman2をmailman3へと移行したのは、busterで動いていたこのサーバーをbullseyeへアップグレードするためだ。ファイルシステム全体のバックアップもできるようになったので、念のためファイルシステムを一度全部dumpした後、Debianを最新に更新することにした。

busterからbullseyeへのアップグレードはいろんなところに情報があるので割愛。まあ、いつも通り/etc/apt/sources.listをbullseyeに書き換えて、apt updateからのapt full-upgradeをするだけだ。

途中、mailman3のパッケージの更新時にデータベース設定の対話的ダイアログが出てきた。本来ならすでに設定済みのmailman3ではこれを聞いてはいけないはずだが、最初のダイアログで自分でやるから勝手にすんな、を選んでやれば設定をぶち壊されずに済む。あと、mailman3-webも設定を新しいデフォルトに置き換えようとするが、すでに設定済みのローカルファイルを残すように指示してあげる必要がある。

すべてのパッケージが更新されたら再起動して、サービスがちゃんと動いているかを確かめると、あらら、いろいろ動いていないじゃないの。具体的にどんなサービスが動いていないかは以下のコマンドで見ることができる。

systemctl list-units –type=service

サービス起動に失敗したサービスは赤字で表示される。

このうち、uwsgiのサービスが動いていない理由は、どうやらsystemctlの古いユニットファイルが残っていて、そのユニットファイルに基づいて起動しようとして失敗しているようだ。このような問題はrcステートになっている設定だけが残骸で残っているパッケージを総パージしてあげればよい。

apt purge $(dpkg -l | awk ‘/^rc/ { print $2 }’)

これで大丈夫かと思ったら、なんとmailman3が動いていない。いろいろと調べてみるとどうも/var/lib/mailman3/data/内にlist:listな所有権でないファイルがパッケージの更新によって出現してしまったからのようだ。というわけで、このディレクトリ内の所有権が間違ってるファイルをlist:listにchownしてあげればサービスが起動する。

あと、sslまわりの証明書を用意しないと文句を言って起動しないdovecotはこのサーバーでは必要ないので自動起動しないように設定した。

サービスがなぜ起動しないのかについては、実際に

systemctl start サービス名

でサービスを起動してみると、エラーメッセージがちゃんと表示されるので、それを見ると原因追及がはかどる。

これで必要なサービスを一通りちゃんと起動できるようにしたら、実際にそれらのサービスが外部から見て正しく動いているかどうかを確認して、ようやくDebianのbullseyeへのアップグレードは完了だ。