.tw活動

反向代理伺服器啟用SSL憑證教學

前言:

網站建置時,為了保護後端伺服器或實現負載平衡,通常都會採用「反向代理(Reverse Proxy)」的方式來實作,但,有些反向代理伺服器僅支援HTTP連線,無法有效保護客戶端與伺服器之間的資料傳遞安全,如預使用HTTPS的加密連線時,就會面臨到如何設定反向代理伺服器的SSL憑證、或是否能支援多組網域名稱的SSL憑證問題。因此,本篇文章,會教你如何使用Let's Encrypt申請SSL憑證及設置反向代理伺服器。

示範說明:

一、示範的環境及網域名稱:

  1. 網域名稱: epaper.idv.tw、talk.idv.tw
  2. 作業系統: FreeBSD v11.0
  3. 反向代理伺服器:Hitch + Varnish

二、參考相關資料:

  1. Let's Encrypt網站: https://letsencrypt.org/
  2. Hitch網站: https://hitch-tls.org/
  3. Varnish網站: http://varnish-cache.org/
  4. Acmetool網站: http://www.ctolib.com/acme.html

三、線上測試憑證:

  1. Qualys SSL Labs: https://www.ssllabs.com/ssltest/
  2. SSL Shopper: https://www.sslshopper.com/ssl-checker.html
  3. SSLBUYER: https://www.sslbuyer.com/

四、備註:

  1. Let's Encrypt所提供的憑證有效期限僅有三個月,使用者可以每兩個月使用SCRIPT自動更新憑證。
  2. Let's Encrypt會限制每個網域名稱(或IP)可以申請的憑證數量,詳細的限制,請參考
    https://letsencrypt.org/docs/rate-limits/
  3. Hitch + Varnish的模式,只能實現「客戶端(Client)到反向代理(Reverse Proxy)」之間的SSL加密連線,因此建議反向代理伺服器與後端的網頁伺服器(Backend Web Server)在同一機房內,避免兩者之間傳輸的資料被有心人士攔截而產生資安漏洞。若「反向代理與後端網頁伺服器」之間也要實作SSL加密連線,則須使用Varnish Plus軟體才能實現(https://www.varnish-software.com/plus/ssl-tls-support/)。

安裝及設定方式

  1. 安裝相關套件(使用port與package安裝的套件版本會有些許差異)
    使用port安裝:
    # cd /usr/ports/security/ca_root_nss/ && make install clean
    # cd /usr/ports/ftp/wget/ && make install clean
    # cd /usr/ports/ftp/curl/ && make install clean
    # cd /usr/ports/www/nginx/ && make install clean
    # cd /usr/ports/www/varnish5/ && make install clean
    # cd /usr/ports/security/hitch/ && make install clean
    

    使用package安裝:
    # pkg install ca_root_nss wget curl nginx varnish5  hitch
    

  2. 安裝acmetool工具(查詢最新版本:https://github.com/hlandau/acme/releases)
    # wget --no-check-certificate https://github.com/hlandau/acme/releases/download/v0.0.59/acmetool-v0.0.59-freebsd_amd64.tar.gz
    # tar zxvf acmetool-v0.0.59-freebsd_amd64.tar.gz
    # cd acmetool-v0.0.59-freebsd_amd64
    # cp -a bin/acmetool /usr/local/bin/
    # chown root:wheel /usr/local/bin/acmetool

  3. 設定本機的Nginx伺服器(申請SSL憑證,驗證網域名稱使用)
    建立相關目錄
    # mkdir -p /var/log/nginx
    # mkdir -p /usr/local/www/acme/.well-known/acme-challenge/
    # chown -R www:www /usr/local/www/acme

    修改nginx.conf檔
    # vi /usr/local/etc/nginx/nginx.conf
    user  www www;
    worker_processes  1;
    
    events {
        use kqueue;
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
    
        log_format  main  '$remote_addr - $remote_user [$time_iso8601] "$request" '
    	                  '$status $body_bytes_sent "$http_referer" '
    	                  '"$http_user_agent" "$http_x_forwarded_for" '
    					  '"$host" $request_time';
    
        access_log      /var/log/nginx/access.log main;
        error_log       /var/log/nginx/error.log;
    
        charset                         utf-8;
        sendfile                        on;
        tcp_nopush                      on;
        tcp_nodelay                     on;
        keepalive_timeout               65;
        types_hash_max_size             2048;
        server_names_hash_bucket_size   128;
    
        client_header_buffer_size       2k;
    
        server_tokens     off;
    
    	add_header        X-Frame-Options SAMEORIGIN;
    
        gzip              on;
        gzip_vary         on;
        gzip_comp_level   3;
        gzip_disable      "MSIE [1-6]\.";
        gzip_types        text/xml text/plain text/css application/javascript application/x-javascript application/rss+xml;
    
    	server {
    	    listen         127.0.0.1:402 default;
    	    root           /usr/local/www/acme;
    	    try_files      $uri $uri/ =404;
    	    access_log     /var/log/nginx/acme-access.log main;
    	    error_log      /var/log/nginx/acme-error.log;
    	}
    
    }

    修改/etc/rc.conf檔
    # vi /etc/rc.conf
    nginx_enable="YES"

    啟動Nginx伺服器
    # /usr/local/etc/rc.d/nginx start

  4. 設定Varnish反向代理伺服器
    建立相關目錄
  5. # mkdir /usr/local/etc/varnish

    新增acmetool.vcl檔(將Let's Encrypt的HTTP連線轉至本機的402埠)
    # vi /usr/local/etc/varnish/acmetool.vcl
    backend acmetool {
    	.host = "127.0.0.1";
    	.port = "402";
    }
    
    sub vcl_recv {
    	if ( req.url ~ "^/.well-known/acme-challenge/" ) {
    		set req.backend_hint = acmetool;
    		return( pass );
    	}
    }

    新增default.vcl檔
    # vi /usr/local/etc/varnish/default.vcl
     vcl 4.0;
    import std;
    
    ### 先執行acmetool.vcl裡的vcl_recv函式
    include "/usr/local/etc/varnish/acmetool.vcl";
    
    ### 根據後端網頁伺服器的 IP 修改
    backend EPAPERIDVTW { .host="211.72.xxx.xxx"; .port="80"; }
    backend TALKIDVTW   { .host="211.72.xxx.xxx"; .port="80"; }
    
    sub vcl_recv {
        set req.http.X-Forwarded-For = client.ip;
    	set req.http.x-clientip = client.ip;
    	set req.http.x-serverip = server.ip;
    	set req.http.x-localip = local.ip;
    	set req.http.x-remoteip = remote.ip;
    
    	### 將 HTTP 的連線轉至 HTTPS
    	if ( std.port(local.ip) == 80 ) {
    		set req.http.x-redir = "https://" + req.http.host + req.url;
    		return( synth(850,"Moved permanently") );
    	}
    
    	if ( std.port(server.ip) == 443 ) {
    		set req.http.X-Forwarded-Proto = "https";
    	}
    
    	### 根據 URL 來判斷使用那一個後端伺服器
    	if ( req.http.host ~ "^epaper.idv.tw$" ) { set req.backend_hint = EPAPERIDVTW; }
    	elseif ( req.http.host ~ "^talk.idv.tw$" ) { set req.backend_hint = TALKIDVTW; }
    }
    
    sub vcl_synth {
    	if ( resp.status == 850 ) {
    		set resp.http.Location = req.http.x-redir;
    		set resp.status = 301;
    		return( deliver );
    	}
    }
    
    sub vcl_deliver {
    	call modify_http_headers;
    }
    
    ### 設定及移除部分的HTTP Header參數
    sub modify_http_headers {
        set resp.http.Strict-Transport-Security = "max-age=31536000; includeSubDomains";
        set resp.http.Server = "TWNIC-EPAPER";
        unset resp.http.Via;
        unset resp.http.X-Varnish;
        unset resp.http.Age;
    }

    修改/etc/rc.conf檔
    # vi /etc/rc.conf
    varnishd_enable="YES"
    varnishd_listen="127.0.0.1:6086,PROXY"
    varnishd_extra_flags="-a 210.17.xxx.xxx:80 -p feature=+http2"
    varnishd_config="/usr/local/etc/varnish/default.vcl"
    varnishd_storage="malloc,2G"
    varnishlog_enable="YES"
    varnishncsa_enable="YES"
    備註:
    1. "127.0.0.1:6086,PROXY"中的「PROXY」一定要加,Varnish才會將客戶端的IP位址轉給後端的網頁伺服器
    2. "-a 210.17.xxx.xxx"請修改為反向代理伺服器的IP位址
    3. "-p feature=+http2"是設定啟用HTTP2
    4. "malloc,2G"是設定使用2G的記憶體當快取空間,可以根據自身的機器設備來做調整

    啟動Varnish相關程式(含Varnish Log)
    # /usr/local/etc/rc.d/varnishd start
    # /usr/local/etc/rc.d/varnishncsa start
    # /usr/local/etc/rc.d/varnishlog start

    設定錯誤的檢查方法
    # varnishd -C -f /usr/local/etc/varnish/default.vcl

  6. 申請SSL憑證(產生的相關檔案會存放在:/var/lib/acme)
    設定acmetool工具(根據指示操作)
    # /usr/local/bin/acmetool quickstart

    備註:
    1. 第一步驟:選「Let's Encrypt (Live) - I want live certificates」
    2. 第二步驟:選「PROXY - I'll proxy challenge requests to an HTTP server」
    3. 第三步驟:接受服務條款
    4. 第四步驟:填入管理人員的電子郵件信箱

    設定自動更新憑證的工作排程(根據實際狀況設定每日執行的時間)

    # crontab –e
    27 13 * * * /usr/local/bin/acmetool --batch reconcile

    產生管理的網域名稱所需的SSL憑證
    # openssl dhparam -rand - -out  /var/lib/acme/conf/dhparams 2048
    # /usr/local/bin/acmetool want epaper.idv.tw
    # /usr/local/bin/acmetool want talk.idv.tw

    檢查acmetool的狀況
    # /usr/local/bin/acmetool status

  7. 設定Hitch
    建立相關目錄
    # mkdir -p /var/lib/hitch
    # chown -R nobody:nobody /var/lib/hitch
    備註:
    1. Hitch會因為使用port(v1.4.4)或package(v1.1.1)安裝,而有一些設定上的差異,因為有些功能是後續的版本才有支援(例:OCSP、HTTP2)
    2. Hitch預設使用nobody來執行程式,因此,若要啟用OCSP機制,須建立一個nobody可以寫入的目錄,用來儲存OCSP相關的檔案

    修改hitch.conf檔案
    # vi /usr/local/etc/hitch.conf
    frontend           = "[210.17.xxx.xxx]:443"
    backend            = "[127.0.0.1]:6086"
    write-proxy-v2     = on
    ciphers            = "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH"
    pem-file           = "/var/lib/acme/live/epaper.idv.tw/haproxy"
    pem-file           = "/var/lib/acme/live/talk.idv.tw/haproxy"
    workers            = 4
    alpn-protos        = "h2,http/1.1"
    ocsp-verify-staple = on
    ocsp-dir           = "/var/lib/hitch"
    備註:
    1. "210.17.xxx.xxx"請修改為反向代理伺服器的IP位址
    2. Package安裝的版本,預設使用「TLSv1.0、TLSv1.1、TLSv1.2」三種格式;Port安裝的版本,預設只使用「TLSv1.1、TLSv1.2」
    3. 「alpn-protos」、「ocsp-verify-staple」、「ocsp-dir」三個參數,只有Port安裝的版本才有

    修改/etc/rc.conf檔
    # vi /etc/rc.conf
    hitch_enable="YES"

    啟動Hitch程式
    # /usr/local/etc/rc.d/hitch start

  8. 檢查Listen Port是否正確
    # sockstat -l

測試

  1. 使用curl指令
     # curl -I https://epaper.idv.tw
    # curl -I https://talk.idv.tw

    備註:使用curl指令來檢查連線是否正常、及HTTP Header是否有變更?

  2. 使用Browser測試是否能正常顯示「綠色鎖頭」




  3. 線上檢測網站憑證的分數(使用https://www.ssllabs.com/ssltest/)
財團法人台灣網路資訊中心,100臺北市羅斯福路二段9號4樓之2
TEL:886-2-23411313,FAX:886-2-2396-8832,版權聲明,禁止未經授權轉貼節錄