TORIPIYO DIARY

recent events, IoT, programming, security topics

nginxでクライアント証明書認証の設定と運用時に失効させる方法

WEBサイトの管理者ツールサイトを、例えば協力会社など外部の人にも操作してもらう必要があるときには、どうしてもインターネットに公開しないといけなくなることがあると思います。こういったとき、一般のユーザからのアクセスを拒否するためにユーザ名とパスワードで認証するように実装することが多いのですが、nginxではクライアント認証機能も利用できるのでその方法を紹介します。

クライアント認証にすると以下のメリットがあります
1. クライアント証明書を持っていないユーザに対してはログインを拒否できる
2. 証明書を失効できる

1について、ユーザ名・パスワードがたとえ漏洩していなくても、インターネット上からは絶えずブルートフォースアタックなどの攻撃を受けることになるので認証を突破される可能性があります。クライアント証明書認証ではクライアント証明書を持っている人しかログイン出来ないのでよりセキュアにインターネット上に管理ツールサイトを公開できます。
2について、協力会社との契約が終わった後に、証明書を失効することでクライアント証明書を持っていてもログインを拒否できるようになります。

nginxでのクライアント証明書認証の設定方法

サーバ側の設定

nginxのインストールされているサーバでrootユーザで実行します。nginxは/usr/local/nginxディレクトリの配下にインストールされている想定で進めます。自分の環境に合わせて適時修正してください。

1. CA証明書と鍵の作成

mkdir -pv /usr/local/nginx/conf/client-certificates
cd /usr/local/nginx/conf/client-certificates

openssl genrsa -out ca.key 2048
# 証明書の期限は適切に設定する、ここでは2年(730日)に設定している
# CNには管理ツールサイトのドメイン名を記載する
openssl req -new -x509 -days 730 -key ca.key -out ca.crt -subj "/C=JP/ST=Tokyo/L=Ota-ku/O=Cat Inc./OU=Development/CN=tool.toripiyo.com/emailAddress=admin@toripiyo.com"

2. CRLの作成

# クライアント証明書の失効に利用するのでCRLの設定をする
touch /etc/pki/CA/index.txt  && echo '01' > /etc/pki/CA/crlnumber
openssl ca -name CA_default -gencrl -keyfile ca.key -cert ca.crt -out ca.crl -crldays 730

diff -U0 <(openssl x509 -noout -modulus -in ca.crt) <(openssl rsa -noout -modulus -in ca.key)


3. クライアント証明書をユーザ数分作成

# ここではtoripiyoというユーザのクライアント証明書を作成している
_CLIENTNAME='toripiyo'

mkdir /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
cd /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
openssl genrsa -out ${_CLIENTNAME}.key 2048
openssl req -new -key ${_CLIENTNAME}.key -out ${_CLIENTNAME}.csr -subj "/C=JP/ST=Tokyo/L=Ota-ku/O=Cat Inc./OU=Development/CN=${_CLIENTNAME}/emailAddress=admin@toripiyo.com"

openssl x509 -req -days 365 -in ${_CLIENTNAME}.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -CAserial ../ca.seq -out ${_CLIENTNAME}.crt

# 実行するとパスワードの入力を求められるので入力したパスワードを忘れないように注意!
openssl pkcs12 -export -clcerts -in ${_CLIENTNAME}.crt -inkey ${_CLIENTNAME}.key -out ${_CLIENTNAME}.p12

# set file permission to 640
chmod 640 ${_CLIENTNAME}.*
ls -lta

4. nginxの設定を変更

cd /usr/local/nginx/conf
cp -ivp nginx.conf{,.$(date +%Y%m%d)}
vi nginx.conf
# client証明に関連した以下のdirectiveを追加する
# - clientcertというログフォーマットを追加
# - ssl_verify_client on: クライアント認証する、証明書の提示がないとアクセス拒否
----------------------------------------------------------------------------------------------
log_format clientcert '$remote_addr - $remote_user [$time_local] "$request" '
                   '$status $body_bytes_sent "$http_referer" '
                   '"$http_user_agent" "$http_x_forwarded_for" '
                   '$request_time $upstream_response_time '
                   '"$ssl_client_s_dn($ssl_client_serial)"';

ssl_client_certificate client-certificates/ca.crt;
ssl_crl client-certificates/ca.crl;
ssl_verify_client on;

access_log  logs/host.access.log  clientcert;
----------------------------------------------------------------------------------------------

5. nginxを再起動

# nginxを止める
ps aux | grep -i nginx
/etc/init.d/nginx stop
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

# nginxを開始する
/etc/init.d/nginx start
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

クライアント側の設定

クライアント証明書のファイルは、サーバ側の設定で作成した${_CLIENTNAME}.p12 となります。設定の際には、クライアント証明書作成時に設定したパスワードが必要になります。

macOSsafariブラウザ環境でのクライアント証明書の設定方法

Safari(Mac版) - cybozu.com ヘルプ

iOSsafariブラウザ環境でのクライアント証明書の設定方法

iPhone/iPad版 - cybozu.com ヘルプ

nginxでのクライアント証明書認証の運用で定常的に発生しそうな設定変更

新しいユーザ向けのクライアント証明書の新規発行方法

新しく管理ツールサイトを操作するtamaさんが入ってきたときは、以下のようにしてクライアント証明書を新規に発行します。

1. クライアント証明書を発行
_CLIENTNAME='tama'

mkdir /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
cd /usr/local/nginx/conf/client-certificates/${_CLIENTNAME}
openssl genrsa -out ${_CLIENTNAME}.key 2048
openssl req -new -key ${_CLIENTNAME}.key -out ${_CLIENTNAME}.csr -subj "/C=JP/ST=Tokyo/L=Ota-ku/O=Cat Inc./OU=Development/CN=${_CLIENTNAME}/emailAddress=admin@toripiyo.com"

openssl x509 -req -days 365 -in ${_CLIENTNAME}.csr -CA ../ca.crt -CAkey ../ca.key -CAcreateserial -CAserial ../ca.seq -out ${_CLIENTNAME}.crt

# パスワードを直接引数に指定することも可能。doraemonというパスワードを今回は指定している
openssl pkcs12 -password doraemon -export -clcerts -in ${_CLIENTNAME}.crt -inkey ${_CLIENTNAME}.key -out ${_CLIENTNAME}.p12

# set file permission to 640
chmod 640 ${_CLIENTNAME}.*
ls -lta

2. nginxの再起動は不要

3. クライアント証明書を配布するためにローカルPCに取得

特定のクライアント証明書の無効化方法

tamaさんが、管理ツールサイトを利用しなくなった場合は、以下のようにしてクライアント証明書を無効にさせます。

1. index.txt を更新する
_CLIENTNAME='tama'
cd /usr/local/nginx/conf/client-certificates
openssl ca -name CA_default \
    -revoke ${_CLIENTNAME}/${_CLIENTNAME}.crt \
    -keyfile ca.key \
    -cert ca.crt

2. ca.crl を更新する
openssl ca -name CA_default -gencrl \
    -keyfile ca.key \
    -cert ca.crt \
    -out ca.crl \
    -crldays 730

3. nginxを再起動する
ps aux | grep -i nginx
/etc/init.d/nginx stop
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

/etc/init.d/nginx start
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

無効化してしまった証明書を再び有効化する方法

tamaさんのクライアント証明書を無効にしたけれど、やっぱり管理ツールサイトを利用する必要がありクライアント証明書の再配布をしたくない場合は以下のようにします。

1. index.txtのファイルを編集
vi /etc/pki/CA/index.txt
====================
- 該当する行の最初の列の"R" と記載のある項目を "V" に変更
- 該当する行の3番目の列を削除 (2列目と4列目にあるカラムを残しておかないと次のopensslコマンドでエラーを出すので文字だけを消すようにすること。3番目の列は""となる。)
====================

2. ca.crlを更新
cd /usr/local/nginx/conf/client-certificates
openssl ca -name CA_default -gencrl \
    -keyfile ca.key \
    -cert ca.crt \
    -out ca.crl \
    -crldays 730

3. nginxを再起動する
ps aux | grep -i nginx
/etc/init.d/nginx stop
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log

/etc/init.d/nginx start
ps aux | grep -i nginx
less /usr/local/nginx/logs/error.log