Ubuntu

Apacheへのアクセスをクライアント証明書で保護する

Apacheで公開しているサーバーがある。
LAN内からは制限なくアクセスできるが、インターネットからアクセスするときにはクライアント証明書が必要、という設定にしたい。



広告


環境はこちら。

$ apachectl -v
Server version: Apache/2.4.52 (Ubuntu)
Server built:   2023-03-01T22:43:55

例えば、

  • 192.0.2.0/24
  • 2001:db8:1234:5678/64

の範囲を制限なくアクセスできるようにする場合に、以下の設定ができた。

/etc/apache2/sites-available/hoge.conf

<VirtualHost *:443>
...
        SSLCACertificateFile    /etc/ssl/private/hoge-ca.crt
        SSLCARevocationFile     /etc/ssl/private/hoge.crl
        SSLCARevocationCheck    leaf
        <LocationMatch ^/(hoge|piyo)>
                <If "%{REMOTE_ADDR} =~ m/192\.0\.2\.[0-9]+/ || \
                     %{REMOTE_ADDR} =~ m/2001:db8:1234:5678:[0-9a-f:]+/">
                </If>
                <Else>
                        SSLVerifyClient require
                        SSLVerifyDepth 1
                        Require expr (%{SSL_CLIENT_VERIFY} == "SUCCESS")
                </Else>
        </LocationMatch>
...

クライアント証明書が必要なケースでは、SSLCACertificateFileで指定したCA(PEMの連結が可能)が署名したものだけが使える。

SSL_CLIENT_VERIFYがSUCCESSであることを求めているが、書き方のメモ程度。
この条件がなくても、証明書は確認される。

証明書の中身に条件を付けたい場合には、それを書いていく。
Apache Module mod_ssl

調べたこと

以前は、SSLVerifyClientをoptionalに設定し、Require exprで接続を許容するIPアドレスを設定していた。
そうすると、接続を許容するIPアドレスにいても、クライアント証明書を持っていると、ブラウザにクライアント証明書の選択画面が表示されていた。
今回の設定であれば、許容するIPアドレスにいれば、選択画面は表示されなくなる。

この若干不便な設定を改善するために、以下のページに教えてもらった。

クライアント証明書で保護したい場所を限定する方法として、2.4からIf文が使えることを教えてくている。
Stack Exchange / Apache ~ how to force SSL client auth for specific IP

If文で使える比較方法を教えてくれている。
Qiita / Apache 2.4 で使える If 文

If文でANDやORが使えることを教えてくれている。
rougeref's diary / Apache2.4のに and とか or 条件をつける

正規表現の書き方で、記号は/に限らないことを教えてくれている。
Stack Overflow / How to resolve unexpected T_STR_BEGIN regex error in Apache HTTP config?

条件を複数行で書く方法を教えてくれている。
Stack Exchange / Break up a long line in a .htaccess file

動作確認

ブラウザでアクセスしてみたところ、正当なクライアント証明書しか提示することができなかった。
そこで、cURLコマンドで2種類の証明書を提示してみた。

正当なクライアント証明書

日頃使用しているクライアント証明書を提示する。

$ curl -v https://example.net/hoge --cert-type p12 --cert cert.p12:<password>

※<password>には、クライアント証明書のパスワードを入れる。

問題なくアクセスすることができた。

最初、P12ファイルのアルゴリズムが古すぎるらしく、以下のエラーが発生していた。

curl: (58) could not parse PKCS12 file, check password, OpenSSL error error:0308010C:digital envelope routines::unsupported

これ、OpenSSLがP12ファイルを開こうとしたが、古すぎてダメということのようだった。
Github / curl / crul / curl: (58) could not parse PKCS12 file error #8966

変換方法を教えてくれていたけれども、上手く動かないので、当時P12ファイルを作成するときの元ネタ(証明書と秘密鍵のPEM)を使い、改めてP12ファイルを作り直すことで、cURLで使えるようになった。

自己署名したクライアント証明書

新たなCAを作り、正当なクライアント証明書と同じ署名要求に署名し、P12にして提示してみたところ、ページにアクセスすることはできなかった。

/var/log/apache2/error.log

[Fri Jul 28 06:28:01.136650 2023] [ssl:error] [pid 2427662:tid 139667718428224] [client 192.0.2.55:38996] AH02039: Certificate Verification: Error (19): self-signed certificate in certificate chain
[Fri Jul 28 06:28:01.136844 2023] [ssl:error] [pid 2427662:tid 139667718428224] [client 192.0.2.55:38996] AH02261: Re-negotiation handshake failed
[Fri Jul 28 06:28:01.137021 2023] [ssl:error] [pid 2427662:tid 139667718428224] SSL Library Error: error:0A000086:SSL routines::certificate verify failed

self-signed certificateといわれているので、CAをWebサーバーのいわゆる「信頼できるルート認証局」として登録してみたが、結果は変わらなかった。
これは予測だけれど、OpenSSLコマンド(ライブラリ?)にCAのリストとしてSSLCACertificateFileを渡しているのかなと思われ、だとすれば、一般に信頼されているルート認証局であっても自己署名証明書であることは変わらないから、SSLCACertificateFile以外はすべてこの結果になるのではないか。

ということで、まず問題はないだろうと思われる。

さいごに

6年前、クライアント証明書をどうやって作るのか、Apacheでどうやって保護するのか、というところを整理していた

この記事は色々と間違っているけれども、その時に色々と教えてくれた先輩方のサイトが素晴らしかったので、結果としては問題のないクライアント証明書ができていて、アクセスの保護もできている。本文で書いたとおり、常にクライアント証明書を求める不便な設定になっていたものの、上手く動いている。

この「理解を間違っていても、正しい結果が得られるやり方」を教えてくれるのは、とても助かったりする。
できないよりはできた方が良く、それで目的は達成できるのだから。

今回の設定をすれば、ローカルからのアクセスには証明書を求めないので、一手間減らすことができる。
これ、とりあえず正しい結果が得られるものになっていると良いなぁ。

広告

コメントはこちらから お気軽にどうぞ ~ 投稿に関するご意見・感想・他