1. はじめに
IBM CloudのVPC環境でmutual TLS(つまり、Server証明書だけでなくClient証明書を使った認証も行うケース)がうまくいき、尚且つClient証明書のDN情報がログに残せるかについて聞かれたので、実際にnginxを使って構築してみた。
Client ----(mutual TLS)---- Load Balancer(nginx) ---- backend server(Apache)
Client: 192.168.100.4
nginx: 10.50.0.20
backend server: 10.50.0.101, 10.50.0.102
2. backend serverの作成
- Apacheで構成する
- Load Balancer(nginx)経由で割り振られるので、クライアントのIPアドレスおよびClient証明書のDN情報をログに出力するように構成する。
- 以下は、backend serverごとに構成した作業
ApacheのインストールとサンプルHTMLの作成
[root@syasuda-backend1 ~]# dnf install -y httpd
[root@syasuda-backend1 ~]# echo "This is $(hostname)" > /var/www/html/index.html
ログの構成(デフォルトではLogFormatとしてcombinedが使われている)
[root@syasuda-backend1 ~]# cat /etc/httpd/conf/httpd.conf |grep -v "#" |grep Log
ErrorLog "logs/error_log"
LogLevel warn
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b" common
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
CustomLog "logs/access_log" combined
[root@syasuda-backend1 ~]# cat /etc/httpd/conf/httpd.conf |grep "combined$"
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog "logs/access_log" combined
以下のように書き換えて、X-Forwarded-ForとX-Client-DNをHTTPヘッダーから抽出して出力。
[root@syasuda-backend1 ~]# cat /etc/httpd/conf/httpd.conf |grep "combined$"
#LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{X-Forwarded-For}i\" \"%{X-Client-DN}i\"" combined
CustomLog "logs/access_log" combined
apacheの起動
[root@syasuda-backend1 ~]# systemctl enable httpd
[root@syasuda-backend1 ~]# systemctl start httpd
3. nginxに配置するServer証明書と、ユーザーに配布するClient証明書の作成
作業ディレクトリの作成
[root@syasuda-server1 ~]# mkdir ca-work
[root@syasuda-server1 ~]# cd ca-work
CA鍵とCA証明書を作成
[root@syasuda-server1 ca-work]# openssl genrsa -out ca.key 4096
[root@syasuda-server1 ca-work]# openssl req -x509 -new -nodes -key ca.key -sha256 -days 3650 -out ca.crt -subj "/C=JP/ST=Tokyo/O=MyOrg/CN=MyTestCA"
[root@syasuda-server1 ca-work]# openssl x509 -in ca.crt -noout -subject -issuer -dates
subject=C=JP, ST=Tokyo, O=MyOrg, CN=MyTestCA
issuer=C=JP, ST=Tokyo, O=MyOrg, CN=MyTestCA
notBefore=Dec 27 07:17:04 2025 GMT
notAfter=Dec 25 07:17:04 2035 GMT
nginxに配置するサーバー鍵とサーバー証明書の作成
[root@syasuda-server1 ca-work]# openssl genrsa -out server.key 4096
[root@syasuda-server1 ca-work]# openssl req -new -key server.key -out server.csr -subj "/C=JP/ST=Tokyo/O=MyOrg/CN=test.private"
[root@syasuda-server1 ca-work]# openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days 3650 -sha256
Certificate request self-signature ok
[root@syasuda-server1 ca-work]# openssl x509 -in server.crt -noout -subject -issuer -dates
subject=C=JP, ST=Tokyo, O=MyOrg, CN=test.private
issuer=C=JP, ST=Tokyo, O=MyOrg, CN=MyTestCA
notBefore=Dec 27 07:18:50 2025 GMT
notAfter=Dec 25 07:18:50 2035 GMT
クライアント側に配置するクライアント鍵とクライアント証明書の作成
[root@syasuda-server1 ca-work]# openssl genrsa -out client-syasuda1.key 4096
[root@syasuda-server1 ca-work]# openssl req -new -key client-syasuda1.key -out client-syasuda1.csr -subj "/C=JP/ST=Tokyo/O=MyOrg/OU=UserDept/CN=syasuda1"
[root@syasuda-server1 ca-work]# openssl x509 -req -in client-syasuda1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client-syasuda1.crt -days 3650 -sha256
Certificate request self-signature ok
subject=C=JP, ST=Tokyo, O=MyOrg, OU=UserDept, CN=syasuda1
[root@syasuda-server1 ca-work]# openssl x509 -in client-syasuda1.crt -noout -subject -issuer -dates
subject=C=JP, ST=Tokyo, O=MyOrg, OU=UserDept, CN=syasuda1
issuer=C=JP, ST=Tokyo, O=MyOrg, CN=MyTestCA
notBefore=Dec 27 07:23:39 2025 GMT
notAfter=Dec 25 07:23:39 2035 GMT
4. nginxのインストールと構築
nginxのインストール
[root@syasuda-server1 ~]# dnf install -y nginx
CA証明書、サーバー鍵、サーバー証明書の配置
[root@syasuda-server1 ~]# mkdir -p /etc/nginx/ssl
[root@syasuda-server1 ~]# chmod 700 /etc/nginx/ssl
[root@syasuda-server1 ~]# cp ca-work/ca.crt /etc/nginx/ssl/
[root@syasuda-server1 ~]# cp ca-work/server.crt /etc/nginx/ssl/
[root@syasuda-server1 ~]# cp ca-work/server.key /etc/nginx/ssl/
nginx.confの退避
[root@syasuda-server1 ~]# mv /etc/nginx/nginx.conf /etc/nginx/nginx.conf_bkup
/etc/nginx/nginx.confの作成
cat << 'EOF' > /etc/nginx/nginx.conf
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# --- Custom log format including client certificate Subject DN ---
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'client_dn="$ssl_client_s_dn"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
include /etc/nginx/conf.d/*.conf;
}
EOF
/etc/nginx/conf.d/mtls.confの作成。ここで、backend serverへのロードバランシングや、X-Client-DNにClient証明書のDN情報を設定している。
cat << 'EOF' > /etc/nginx/conf.d/mtls.conf
upstream backend_servers {
# Multiple backend servers (round-robin)
server 10.50.0.101:80;
server 10.50.0.102:80;
}
server {
listen 443 ssl;
server_name example.com;
# --- Server certificate (for Nginx itself) ---
ssl_certificate /etc/nginx/ssl/server.crt;
ssl_certificate_key /etc/nginx/ssl/server.key;
# --- Client certificate authentication (mTLS) ---
ssl_client_certificate /etc/nginx/ssl/ca.crt;
ssl_verify_client on;
ssl_verify_depth 3;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# --- Pass client certificate DN/Issuer/Serial to backend ---
proxy_set_header X-Client-DN $ssl_client_s_dn;
proxy_set_header X-Client-Issuer $ssl_client_i_dn;
proxy_set_header X-Client-Serial $ssl_client_serial;
# --- Common proxy headers ---
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# --- Backend configuration using upstream ---
location / {
proxy_pass http://backend_servers;
}
}
EOF
nginxの起動
[root@syasuda-server1 ~]# systemctl enable nginx
[root@syasuda-server1 ~]# systemctl start nginx
5. テスト
5-1. IPアドレスでのアクセス
自己証明書を使っているのでこのままではサーバーの証明書を確認できないためアクセスできない
syasuda@MacBook-Pro ~ % curl https://10.50.0.20
curl: (60) SSL certificate problem: unable to get local issuer certificate
More details here: https://curl.se/docs/sslcerts.html
curl failed to verify the legitimacy of the server and therefore could not
establish a secure connection to it. To learn more about this situation and
how to fix it, please visit the web page mentioned above.
サーバー証明書を無視しても、Client証明書を送っていないのでアクセスに失敗する。
syasuda@MacBook-Pro ~ % curl -k https://10.50.0.20
<html>
<head><title>400 No required SSL certificate was sent</title></head>
<body>
<center><h1>400 Bad Request</h1></center>
<center>No required SSL certificate was sent</center>
<hr><center>nginx/1.20.1</center>
</body>
</html>
クライアント証明書を利用することで認証に成功し、アクセスも成功
syasuda@MacBook-Pro ~ % curl -k --cert ssl-test/client-syasuda1.crt --key ssl-test/client-syasuda1.key https://10.50.0.20
This is syasuda-backend1
5-2. ホスト名でのアクセス
/etc/hostsに"10.50.0.20 test.private"と書いておく。
syasuda@MacBook-Pro ~ % cat /etc/hosts|grep test.private
10.50.0.20 test.private
CA証明書を指定すれば-kオプションを使わずにアクセス可能
syasuda@MacBook-Pro ~ % curl -k --cert ssl-test/client-syasuda1.crt --key ssl-test/client-syasuda1.key https://test.private
This is syasuda-backend1
syasuda@MacBook-Pro ~ % curl --cacert ssl-test/ca-cert --cert ssl-test/client-syasuda1.crt --key ssl-test/client-syasuda1.key https://test.private
This is syasuda-backend1
5-3. アクセスログ
Load Balancer(nginx)
[root@syasuda-server1 ~]# tail -f /var/log/nginx/access.log
192.168.100.4 - - [27/Dec/2025:08:39:56 +0000] "GET / HTTP/1.1" 200 25 "-" "curl/8.7.1" "-" client_dn="CN=syasuda1,OU=UserDept,O=MyOrg,ST=Tokyo,C=JP"
backend server(Apache)
[root@syasuda-backend1 ~]# tail -f /var/log/httpd/access_log
10.50.0.20 - - [27/Dec/2025:08:39:56 +0000] "GET / HTTP/1.0" 200 25 "-" "curl/8.7.1" "192.168.100.4" "CN=syasuda1,OU=UserDept,O=MyOrg,ST=Tokyo,C=JP"
6. nginxをKeepalivedで冗長化した構成
IBM Cloud: ZoneまたぎのHA構成環境をkeepalivedを使って自動化してみたに従って、nginxを冗長化すると、nginx1を停止・再起動してもnginx2にVIPが引き継がれて以下でサービス継続できることを確認できた。
IPアドレスでのアクセス
syasuda@MacBook-Pro ~ % curl -k --cert ssl-test/client-syasuda1.crt --key ssl-test/client-syasuda1.key https://192.168.200.10
ホスト名を使ったアクセス
syasuda@MacBook-Pro ~ % cat /etc/hosts|grep test.private
192.168.200.10 test.private
syasuda@MacBook-Pro ~ % curl -k --cert ssl-test/client-syasuda1.crt --key ssl-test/client-syasuda1.key https://test.private
This is syasuda-backend1