Nextcloudにクライアント認証を導入し、クライアントアプリと両立する

初めに

本記事ではクライアントとつく単語が複数出てくるため、先に補足しておきます。

## 経緯 先日NextcloudにBasic認証を適用した。 とはいえパスワード入力でも万全とは言えないらしい。具体的に言えばSSHでは通常のユーザー名とパスワードでの認証よりも公開鍵認証という、あらかじめ認証用の鍵ファイルをサーバー側とクライアント側でもっておいて、パスワード入力はしないというやり方がセキュリティ上推奨されている。 (わかりやすい理由としては、ユーザー名とパスワードは複雑に、長くしたほうが安全だとは言うものの、人間が手動で入力できる程度に限られてしまうその点鍵のファイルであればもっと長くできるので、総当たりで突破されにくいのだとか。) HTTPでもそういうのないのかと思ったて調べたら、クライアント認証というのがあることを知ったので、早速導入してみる。

※2020/9/16追記 後日別件で自宅のネットワークとクライアントアプリを設定していたところ、クライアントサイドTLS証明書の設定画面が出てきました。そのためクライアントアプリはSSLクライアント認証に普通に対応しているようです、後日検証予定。 記事作成当時は知らなかったのでBasic認証同様できないものかと思っていたためかなり回りくどいやり方をしています。

構成

  • サーバ側
    • AWS Lightsail
    • Nginx
    • Nextcloud 19.0.2
  • クライアント側
    • Nextcloud デスクトップクライアント 2.6.5 (Windows)
    • Nextcloud Androidアプリ 3.13.0

証明書の作成

クライアント認証はやったことがなかったので、備忘録としてここに残しておく。 といっても手順的にはほとんど以下の通り。 RSA鍵、証明書のファイルフォーマットについて - Qiita

秘密鍵の生成

openssl genrsa -out  private-key.pem 2048

証明書署名要求の生成

このCSRというのは証明書署名要求というらしい。

今回はサーバ側で全部やっているためこの署名要求という中間ファイルを作成する意味があまりないが、 本来の流れではクライアント側で秘密鍵を生成し、それをもとに作った署名要求をサーバーに送って署名してもらうという流れで使われるらしい。

openssl req -new -key private-key.pem -out request.csr

証明書の作成

今回は自己署名という、自分自身の秘密鍵で証明書を作る形になる。

当初は自己署名証明書ではクライアント認証できないと思っていたので、自己署名で認証局(いわゆるオレオレ認証局)をつくり、その認証局でクライアント認証用の証明書を作るつもりだった。 しかし結果として自己署名証明書で問題なくクライアント認証できたので当面はこれで運用する。先に例に挙げたSSHの鍵も認証局の認証をしているわけではないし、セキュリティ的には問題ないはず…たぶん…。

openssl x509 -req -in request.csr -signkey private-key.pem -out certificate.crt -days 730

ちなみに有効期限は2年にした。基本的には一年ごとに見直すつもりではあるが、きっかり一年だとなんとなく心配なのでもう1年余裕をもった。

ブラウザに読み込ませるための個人情報交換ファイルの作成

クライアント側でブラウザに読み込ませるためのファイルを作る。 内容的には秘密鍵と証明書をまとめたものらしい。

openssl pkcs12 -export -in certificate.crt -inkey private-key.pem -out client.p12

Nextcloudへ導入

Nextcloudのクライアントアプリは当然クライアント認証には対応していないので、クライアントアプリの通信のみ認証の対象外とすることで両立できるようにする。 具体的な条件についてはこちらの記事と同様なので省略。

Basic認証の際はmapディレクティブで認証自体のon/offを切り替えることができたが、クライアント認証では出来なかった。

[nginx] SSLクライアント認証をしつつ、指定したIPは認証を許可する設定 – harumaki.netによるとクライアント認証をoptionにして、条件で認証結果を"SUCCESS"に上書きするという形で代用できるらしい。

試したが自分の環境では認証結果の$ssl_client_varifyを直接上書きすることができなかったので、mapで別の変数に認証結果を入れて、それで分岐させた。

map $http_user_agent $authentication {
    default             $ssl_client_verify;
    "~Nextcloud"        "SUCCESS"; 
    "~Samsung SM-N9700" "SUCCESS";
}
server {
	listen 443 ssl http2;
	listen [::]:443 ssl http2;
	server_name nextcloud.example.com;
	ssl_certificate /etc/letsencrypt/live/nextcloud.example.com/fullchain.pem;
	ssl_certificate_key /etc/letsencrypt/live/nextcloud.example.com/privkey.pem;
    ssl_verify_client optional;
    ssl_client_certificate /etc/nginx/certificate.crt;
    (略)
}

結論

  • Nextcloudにクライアント認証を適応して、クライアントアプリとの両立もできた。
  • クライアント証明書の作成が見様見真似で理解が追い付いていないので、あまり安全という気分がしない。

その他参考ページ

暗号化とハッシュ化に関する基本的な事柄まとめ - Qiita OpenSSLコマンドの備忘録 - Qiita