需求

在nginx配置OCSP结果缓存后,通过openssl查询可以看到OCSP的状态,示例如下:

[root@localhost ~]# openssl s_client -connect www.seafog.cn:443 -servername www.seafog.cn -status -tlsextdebug  2> /dev/null | grep -A 16 'OCSP response'
OCSP response: 
======================================
OCSP Response Data:
    OCSP Response Status: successful (0x0)
    Response Type: Basic OCSP Response
    Version: 1 (0x0)
    Responder Id: 55744FB2724FF560BA50D1D7E6515C9A018*****
    Produced At: Jun 30 08:06:33 2022 GMT
    Responses:
    Certificate ID:
      Hash Algorithm: sha1
      Issuer Name Hash: 978B4716E5B0F658BAE69DAB1689B8363AE*****
      Issuer Key Hash: 55744FB2724FF560BA50D1D7E6515C9A018*****
      Serial Number: 0254BD766BD41E41B86F71602E4*****
    Cert Status: good
    This Update: Jun 30 07:51:01 2022 GMT
    Next Update: Jul  7 07:06:01 2022 GMT

可以看到OCSP的状态有7天有效时间,如果超过Next Update没更新的话,在https://www.ssllabs.com/ssltest中检查网站SSL的时候,会提示有以下提示:

OCSP STAPLING ERROR: OCSP response expired on Tue Jun 30 05:30:01 UTC 2022

所以还是有必要定时去更新OCSP缓存文件。因此,使用脚本可以定时更新Nginx的OCSP stapling缓存文件,以及session ticket文件。

实现

Bash脚本实现如下:

#!/bin/bash
USER_ID=501001
GROUP_ID=501001
TRY_COUNT=10
SLEEP_TIME=3
TEMP_FILE='ocsp.resp'

function log(){
    echo "$(date +"%Y-%M-%d %H:%m:%S") $1" 
}

function get_OCSP(){
    CERT_KEY=$1
    CA_KEY=$2
    OCSP_FILENAME=$3

    log "start for $CERT_KEY get_OCSP"
    log "CERT_KEY=$CERT_KEY, CA_KEY=$CA_KEY, OCSP_FILENAME=$OCSP_FILENAME"
    OCSP_URI=$(openssl x509 -in $CERT_KEY -noout -ocsp_uri)
    HOST=$(echo $OCSP_URI | awk -F '/' '{print $3}')

    for (( num=1;num<=$TRY_COUNT;num++ ))
    do
        log "try $num"
        openssl ocsp -issuer $CA_KEY \
            -cert $CERT_KEY \
            -no_nonce \
            -text \
            -url "$OCSP_URI" \
            -header "HOST" "$HOST" \
            -text \
            -verify_other $CERT_KEY \
            -respout $OCSP_FILENAME > $TEMP_FILE 2>&1
        grep -q 'OK' $TEMP_FILE
        if [ $? -eq 0 ];then
            rm -f $TEMP_FILE
            log "get OCSP success!"
            break
        else
            log "get OCSP failure!"
            log "time sleep $SLEEP_TIME seconds"
            sleep $SLEEP_TIME
        fi
    done

    chown $USER_ID:$GROUP_ID $OCSP_FILENAME
}

function refresh_session_ticket(){
    SESSION_TICKET=$1

    log "start for refresh session_ticket"
    openssl rand 80 >$SESSION_TICKET
    log "refresh session_ticket success!"
    chown $USER_ID:$GROUP_ID $OCSP_FILENAME
}

function nginx_reload(){
    NGINX_CONTAINER_NAME=$1
    
    docker exec $NGINX_CONTAINER_NAME nginx -s reload
    log "$NGINX_CONTAINER_NAME reload success!"
}

get_OCSP 'www.seafog.cn.pem' 'ca.pem' 'ocsp.pem'
refresh_session_ticket 'session_ticket.key'
nginx_reload 'nginx'

通过执行get_OCSPrefresh_session_ticketnginx_reload这3个方法来实现获取OCSP缓存、刷新session ticket,以及nginx的reload重载生效。

其中,get_OCSP方法里生成OCSP的openssl命令里,添加了-verify_other,也就是指定我们的完整证书链,也就是 域名证书+中间证书的合并文件,也就是 nginx 里的 ssl_certificate 配置文件。不然生成OCSP文件的时候会报以下错误:

140063168673680:error:27069076:OCSP routines:OCSP_basic_verify:signer certificate not found:ocsp_vfy.c:92:

脚本参数解释及用法如下:

  1. USER_IDGROUP_ID用于更改生成结果文件的权限。
  2. TRY_COUNT是用于在get_OCSP方法中用来设定错误重试次数,因为由于网络原因,生成OCSP的时候,会经常认证失败,所以需要进行重试。
  3. SLEEP_TIME是用于在get_OCSP方法中错误重试时的间隔时间。
  4. TEMP_FILE是用于在get_OCSP方法中生成的临时文件。
  5. get_OCSP方法需要带入3个参数,依次是:
    • ssl_certificate域名证书
    • ssl_trusted_certificate的CA证书
    • 生成的ocsp文件名
  6. refresh_session_ticket方法需要带入1个参数:
    • 生成的session_ticket.key文件名
  7. nginx_reload方法需要带入1个参数:
    • nginx容器的容器名。

另推荐几个可以检查SSL的状态的网站:

  1. SSL Server Test (Powered by Qualys SSL Labs)
  2. SSL/TLS安全评估报告