パスベースというのは、例えば、https://example.com/login にだけクライアント証明書を要求し、他の URL は要求しないという設定です。結論から言うと、Nginx はパスベースのクライアント証明書による認証はできません。Apache はできますね。Nginx 公式の見解としては「面倒くさい」(超意訳)ということなので、今後の実装を期待するのは無駄でしょう。少々複雑になりますが、代替案のご紹介です。ネットでよく見かける ssl_verify_client optional; を使ったお手軽設定ではありません。

おことわり: この記事は、Nginx や認証が必要なアプリは既に正常に動いていて、クライアント証明書による認証に切り替えたくて、ssl_verify_client optional; を試してみたけど、ちょっとなー、という人に向けたものです。

読むと分かりますが、サブドメインが必要なことと、アプリ側の協力も必要なこともあって、はっきり言ってかなり面倒臭いです。なぜこんな面倒な設定をしてでも、ssl_verify_client optional; を避けたいかというと、この設定では、認証ポイントとして色々な URL がクライアント PC に登録されてしまい、だんだんウザくなってくるからです。

Trac のレシピ

例として Trac のレシピをご紹介します。他のアプリケーションでもやることは同じなので、読んでポイントを抑えていただければと思います。

今、https://trac.example.com で Trac を運用しているとします。Trac の認証ポイントは、https://trac.example.com/login ですので、ここの URL にアクセスしたときにだけクライアント証明書による認証を行い、それ以外の URL でのアクセスはクライアント証明書なしで、そのままアクセスさせたい、という要求を満たす代替案です。

最初に、trac.example.com 側の Nginx の設定です。Nginx の制限により、trac.example.com ではクライアント証明書の要求ができませんので、代わりに認証専用のサブドメイン auth.trac.example.com を作って、/login に対するアクセスは、そこにリダイレクトします。

server {

    server_name  trac.example.com;
  
    ...

    ssl_verify_client off;

  
    location / {
        try_files $uri @uwsgi;
    }
  
    location /login {
        rewrite ^/(.*) https://auth.trac.example.com/login permanent;
    }
  
    # Trac
    location @uwsgi {
        include uwsgi_params;
        uwsgi_pass unix:/var/run/uwsgi/trac.sock;
    }
  
    ...

}

次に、auth.trac.example.com 側の Nginx の設定です。こちらでは、全域でクライアント証明書を要求した上で、trac.example.com と同じアプリを動かします。と言っても、実際に使うのは、認証ポイントの https://auth.trac.example.com/login だけです。

map $ssl_client_s_dn $ssl_client_s_dn_cn {
    default "";
    ~CN=(?[^,]+) $CN;
}

server {

    server_name  auth.trac.example.com;
  
    ...
  
    ssl_verify_client on;
  
    location / {
        try_files $uri @uwsgi;
    }
  
    # Trac
    location @uwsgi {
        include uwsgi_params;
        uwsgi_pass unix:/var/run/uwsgi/trac.sock;
        uwsgi_param REMOTE_USER $ssl_client_s_dn_cn;
    }
  
    ...

}

これで認証はうまくいきますが、このままでは発行したクッキーが https://auth.trac.example.com/ でしか通用しませんので、https://trac.example.com/ でもクッキーが使えるように、アプリ側での対応が必要になります。Trac の設定ファイル trac.ini の [trac] セクションに、以下の設定を追加します。

[trac]
auth_cookie_domain = .trac.example.com

認証完了後に元の URL へリダイレクトするために、 trac.ini の [trac] セクションに、以下の設定を追加します。

[trac]
base_url = https://trac.example.com
use_base_url_for_redirect = enabled

以上で、設定は終了です。元々 Trac がこのような設定に対応しているため、設定ファイルの変更だけで済みました。こうした仕掛けが全くないアプリの場合は、自力で仕掛けを作るしかないですね。

Trac 以外のアプリでも同じようにできるはずです。ポイントは、① 認証専用のサブドメインを作ること、② 認証ポイントから認証専用のサブドメインへリダイレクトすること、③ クッキーの有効ドメインを元のドメインへ広げること、④ 認証終了後に元のドメインへリダイレクトすることの 4つです。サブドメインにも SSL 証明書が必要になりますが、今は Let's Encrypt があるので金銭的負担はありません。設定をいじる手間だけです。がんばって挑戦してみてください。