0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

クライアント認証を有効にしたサイトで特定のパスだけ認証不要とする

Last updated at Posted at 2024-08-29

はじめに

このようなシチュエーションはあまりないような気がしますが、実際これを設定する必要があり、暫く試行錯誤してしまったのでまとめておきます。

  • Apache+SSLで、ユーザー毎のクライアント証明書による認証を有効化
  • 当該サーバー上の特定のパスだけは、認証を外して誰でもアクセスできるようにする

前提

Apacheでは、このような設定をしているものとします。典型的な設定先は /etc/httpd/conf.d/ssl.conf あるいは /etc/apache2/sites-available/default-ssl.conf となるでしょう。

ssl.conf
<VirtualHost _default_:443>

# サーバー証明書の設定
SSLCertificateFile /etc/letsencrypt/live/example.com/cert.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
SSLCertificateChainFile /etc/letsencrypt/live/example.com/chain.pem

# クライアント認証局(自己署名)の設定
SSLCACertificateFile /etc/pki/local/ca/cert.pem
SSLVerifyClient require
SSLVerifyDepth 10
SSLCARevocationCheck leaf
SSLCARevocationFile /etc/pki/local/ca/crl.pem

</VirtualHost>

この設定によって、/etc/pki/local/ca/cert.pemで署名したクライアント証明書をインストールしていない端末からはアクセスできなくなります。1

やりたいこと

新しくアクセスを許可するユーザーのために作った証明書をダウンロードしてもらうパスを同じサーバー上に用意するようなシチュエーション、を考えます。

https://example.com/client_certs/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

のような長いURLでアクセスさせる感じです。

失敗例

LocationなりDirectoryなりのディレクティブで制御すればいいのよね、ということで、安易に

ssl.conf
<Location />
  SSLVerifyClient require
</Location>

<Location /client_certs/>
  SSLVerifyClient none
</Location>

としてみましたが、ダメでした。

他に、

ssl.conf
<If "%{REQUEST_URI} !~ m!^/client_certs/!">
  SSLVerifyClient require
</If>

も試してみました。これもダメ。

落ち着いて考えればわかるのですが、証明書の検証とクライアント認証は、SSLハンドシェーク時に行うので、ハンドシェーク後のHTTP GET等のコマンドを与えた後に分岐するような設定は効果がないのです。

クライアントIPアドレスのような、ハンドシェーク時にわかる値であれば、後者のやり方で制御できます。

考察

結局のところ、証明書なしで接続を許すリソースが存在する以上、ハンドシェーク時には証明書があってもなくても繋がる状態にする必要があります。

ssl.conf
SSLVerifyClient optional

で、証明書が必要なリソースについては、別の手段でアクセス制御をかけることになります。

今回はSSLRequireが使えそうです。クライアント証明書のDNのベース部分(例えばC/ST/O)を固定にして、ユーザー毎にその下(CN等)を変えるような運用を想定すると、SSL_CLIENT_S_DN_Oを条件にすれば、「クライアント認証を必須とするパス」を定義可能となるはずです。2

成功例

これでうまくいきました。ついでにいうと、証明書なしでアクセスした際に、SSLエラーでなくForbiddenが出るようになったので、これはこれでわかりやすい気がします。34

ssl.conf
SSLVerifyClient optional

<If "%{REQUEST_URI} !~ m!^/client_certs/!">
  SSLRequire %{SSL_CLIENT_S_DN_O} eq "Your Company"
</If>

おわりに

本件、ちょっと必要にかられてこういうのを作っていて、はて、と止まってしまったやつです。せっかくなので切り出して記事化してみました。

もし興味あればどうぞ。特に、なんとなく勢いでI18nなyml書いちゃったのだけど、多分ものすごく不自然な翻訳になってるだろうから(特に中台韓独5)、このへんフィードバックいただける奇特な方がおられましたら嬉しいです。もちろん翻訳の追加も大歓迎。需要あるかはわかりませんが…

  1. SSLエラーになります。サーバー側でのハンドシェークエラーなので、無視してアクセスすることはできません。

  2. もちろん、DNの条件を揃えた適当な証明書ではそれを用いたハンドシェークができないので、正規の証明書である必要があります。

  3. 一度Apacheに接続を許してるのは大丈夫なのか、という議論はあるかもしれませんが、制限対象のリソースにはアクセスできないので問題ないでしょう。

  4. なお、CRLに突っ込んで証明書を無効化した場合はハンドシェークに失敗するようで、SSLエラーとなります。

  5. ようするに日英以外全部じゃねーか…

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?