NGXIN + ngx_mrubyを用いたSSL動的読み込みの環境構築

構築環境


shell> hostnamectl status
   Static hostname: www.example.com
         Icon name: computer-vm
           Chassis: vm
〜
    Virtualization: kvm
  Operating System: CentOS Linux 7 (Core)
       CPE OS Name: cpe:/o:centos:centos:7
            Kernel: Linux 3.10.0-693.17.1.el7.x86_64
      Architecture: x86-64

※ 全て管理者権限(root)のあるユーザで実行している

NIGNX + ngx-mrubyの環境構築


各種ソースコードをダウンロード

NGINXのソースコード

最新版(Ver 1.14.0)をダウンロード(2018/05/04現在)

shell> cd /usr/local/src/ && \
curl -O http://nginx.org/download/nginx-1.14.0.tar.gz && \
tar zxf /usr/local/src/nginx-1.14.0.tar.gz

ngx_mrubyのソースコード

最新版(Ver 1.20.2)をダウンロード(2018/05/04現在)

shell> cd /usr/local/src/ && \
curl -LO https://github.com/matsumotory/ngx_mruby/archive/v1.20.2.tar.gz && \
tar zxf /usr/local/src/v1.20.2.tar.gz

Rubyをインストール

ngx_mrubyのビルドには、RubyのRakeライブラリが必要なためインストール

shell> yum install -y ruby ruby-devel openssl openssl-devel
shell> gem install rake

ngx_mrubyをビルド

shell> cd /usr/local/src/ngx_mruby-1.20.2/
shell> ./configure --with-ngx-src-root=/usr/local/src/nginx-1.14.0
shell> make build_mruby -j 2
shell> make generate_gems_config -j 2

NGINXをビルド

shell> cd /usr/local/src/nginx-1.14.0
shell> ./configure --with-compat \
--add-module=/usr/local/src/ngx_mruby-1.20.2/dependence/ngx_devel_kit \
--add-module=/usr/local/src/ngx_mruby-1.20.2 \
--with-http_realip_module \
--with-http_addition_module \
--with-http_sub_module \
--with-http_dav_module \
--with-http_flv_module \
--with-http_mp4_module \
--with-http_gzip_static_module \
--with-http_random_index_module \
--with-http_secure_link_module \
--with-http_stub_status_module \
--with-http_auth_request_module \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_gunzip_module \
--with-threads \
--with-stream \
--with-stream_ssl_module \
--with-http_slice_module \
--with-mail \
--with-mail_ssl_module \
--with-file-aio \
--without-stream_access_module \
--with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic'
shell> make install -j 2

NGINXに設定を投入

NGINXのコンフィグをバックアップ

既存のnginx.confをリネーム

shell> mv -v /usr/local/nginx/conf/nginx.conf{,.backup}

nginx.confを新規作成

shell> mkdir /usr/local/nginx/conf/conf.d
shell> cat /usr/local/nginx/conf/nginx.conf
user  nginx;
worker_processes  auto;
worker_rlimit_nofile 100000;
pid /usr/local/nginx/logs/nginx.pid;

events {
    worker_connections  2048;
}

http {
    log_format ltsv 'time:$time_iso8601\t'
                    'remote_addr:$remote_addr\t'
                    'request_method:$request_method\t'
                    'request_length:$request_length\t'
                    'request_uri:$request_uri\t'
                    'https:$https\t'
                    'uri:$uri\t'
                    'query_string:$query_string\t'
                    'status:$status\t'
                    'bytes_sent:$bytes_sent\t'
                    'body_bytes_sent:$body_bytes_sent\t'
                    'referer:$http_referer\t'
                    'useragent:$http_user_agent\t'
                    'forwardedfor:$http_x_forwarded_for\t'
                    'request_time:$request_time\t'
                    'upstream_response_time:$upstream_response_time';

    access_log /usr/local/nginx/logs/access.log ltsv;
    error_log /usr/local/nginx/logs/error.log info;

    proxy_set_header    Host                $host;
    proxy_set_header    X-Real-IP           $remote_addr;
    proxy_set_header    X-Forwarded-Host    $host;
    proxy_set_header    X-Forwarded-Server  $host;
    proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
    proxy_set_header    X-Forwarded-Proto   https;

    client_max_body_size 2G;

    gzip on;
    gzip_types text/css application/javascript application/json application/font-woff application/font-tff image/gif image/png image/jpeg application/octet-stream;

    server_tokens off;
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    include       mime.types;
    default_type  application/octet-stream;
    keepalive_timeout  10;
    server_names_hash_bucket_size 20000;
    include /usr/local/nginx/conf/conf.d/*.conf;
}

NGINXユーザを作成

shell> useradd -M --shell /sbin/nologin nginx

NGINXのシンボリックリンクを作成

shell> ln -s /usr/local/src/nginx-1.14.0/objs/nginx /usr/local/bin/nginx

シンタックスチェック

shell> nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

NGINXをSystemdに登録

sehll> cat /usr/lib/systemd/system/nginx.service
[Unit]
Description=nginx - high performance web server
After=network-online.target remote-fs.target nss-lookup.target
Wants=network-online.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStart=/usr/local/bin/nginx -c /usr/local/nginx/conf/nginx.conf
ExecReload=/bin/kill -s HUP $MAINPID
ExecStop=/bin/kill -s TERM $MAINPID

[Install]
WantedBy=multi-user.target

NGINXを起動

shell> systemctl enable nginx && systemctl start nginx

動作確認用

テストページ用のコンフィグを作成

http://www.example.com/helloにアクセスして「hello ngx_mruby world」が表示される設定を追加

shell> /usr/local/nginx/conf/conf.d/www.example.com.conf
server{
    listen 80;
    server_name  www.example.com;

    location /hello {
      mruby_content_handler_code 'Nginx.echo "hello ngx_mruby world"';
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/nginx/html;
    }
}

追加したコンフィグを反映

shell> nginx -s reload

テストページにアクセス

shell> curl -s http://www.example.com/hello
hello ngx_mruby world

Let’s Encryptの導入


SSL証明書を入手するために無料で期間の短い証明書を無料で発行してくれるサービスを利用する

ソースコードをダウンロード

shell> cd /opt && git clone https://github.com/letsencrypt/letsencrypt

Let’s Encryptの環境構築

shell> cd letsencrypt && ./letsencrypt-auto --help all

証明書を発行

コマンドオプションには、ドキュメントルート(-w)・コモンネーム(-d)・メールアドレス(-m)を指定

shell> ./letsencrypt-auto certonly \
--rsa-key-size 4096 \
--webroot -w /usr/local/nginx/html \
-d www.example.com \
-m root@www.example.com

SSL証明書を確認

shell> tree /etc/letsencrypt/live/
/etc/letsencrypt/live/
`-- www.example.com
    |-- README
    |-- cert.pem -> ../../archive/www.example.com/cert1.pem
    |-- chain.pem -> ../../archive/www.example.com/chain1.pem
    |-- fullchain.pem -> ../../archive/www.example.com/fullchain1.pem
    `-- privkey.pem -> ../../archive/www.example.com/privkey1.pem

SSL証明書パーミッションを変更

NGINXユーザからSSL証明書にアクセスする場合、「live」「archive」のパーミッションを700 -> 755に変更必要がある

shell> chmod 755 /etc/letsencrypt/{live,archive}

テストページ用のコンフィグにSSLの設定を追加

shell> /usr/local/nginx/conf/conf.d/www.example.com.conf
server{
    listen 80;
    listen 443 ssl http2;
    server_name www.example.com;

    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_timeout 1h;
    ssl_session_cache shared:SSL:10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;

    ssl_certificate "/etc/letsencrypt/live/www.example.com/fullchain.pem";
    ssl_certificate_key "/etc/letsencrypt/live/www.example.com/privkey.pem";
    ssl_dhparam "/usr/local/nginx/conf/dhparam.pem";

    location /hello {
      mruby_content_handler_code 'Nginx.echo "hello ngx_mruby world"';
    }

    location ^~ /.well-known/acme-challenge/ {
        root /usr/local/nginx/html;
    }
} 

dhparamを生成

shell> openssl dhparam 4096 -out /usr/local/nginx/conf/dhparam.pem

追加したコンフィグを反映

shell> nginx -s reload

テストページにアクセス

shell> curl -s https://www.example.com/hello
hello ngx_mruby world

SSL証明書の動的読み込み


ドキュメントルートは、「/usr/local/nginx/html/< ドメイン名 >」でドメイン毎に作成
今回の場合は、「/usr/local/nginx/html/www.example.com

shell> cat /usr/local/nginx/conf/conf.d/mruby.conf
server {
      listen 80;
      server_name _;

      location ^~ /.well-known/acme-challenge/ {
        root /usr/local/nginx/html;
    }
    location / {
        mruby_set_code $root '
          r = Nginx::Request.new
          "/usr/local/nginx/html/#{r.hostname}"
        ';

        root $root;
    }
}

server {
    listen 443 ssl http2;
    server_name  _;

    # SSL
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_timeout 1h;
    ssl_session_cache shared:SSL:10m;
    ssl_ciphers HIGH:!aNULL:!MD5;
    ssl_prefer_server_ciphers  on;
    add_header Strict-Transport-Security max-age=31536000;

    ssl_certificate "/etc/letsencrypt/live/www.example.com/fullchain.pem";
    ssl_certificate_key "/etc/letsencrypt/live/www.example.com/privkey.pem";
    ssl_dhparam "/usr/local/nginx/conf/dhparam.pem";

    mruby_ssl_handshake_handler_code '
      ssl = Nginx::SSL.new
      ssl.certificate     = "/etc/letsencrypt/live/#{ssl.servername}/fullchain.pem"
      ssl.certificate_key = "/etc/letsencrypt/live/#{ssl.servername}/privkey.pem"
    ';

    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }

    location / {
        mruby_set_code $root '
          r = Nginx::Request.new
          "/usr/local/nginx/html/#{r.hostname}"
        ';

        root $root;
    }

    ## リバースプロキシの設定例
    #location / {
    #   resolver 8.8.8.8 1.1.1.1 8.8.4.4 valid=15m;
    #   resolver_timeout 10s;
    #   add_header Strict-Transport-Security max-age=31536000;
    #   mruby_set_code $backend 'Nginx::Request.new.hostname';
    #   proxy_pass http://$backend;
    #}
}

シンタックスチェック

murbyの記述箇所については、下記コマンドではエラーを確認できないので「/usr/local/nginx/logs/error.log」を確認すること

shell> nginx -t

設定の反映

shell> nginx -s reload

正しく設定が反映されない場合は、再起動を実施

shell> systemctl restart nginx