诡异的问题:和朋友共用一台服务器,在访问我的网站时, nginx 同时发送了他的证书

2017-02-05 22:54:54 +08:00
 fourstring

我们的服务器用 Docker 建立 nginx 环境,所以 ssl 直接丢在了 root 下,也没有太大关系。

服务端软件为: nginx-1.11.8 , openssl-1.1.0c (已排除软件版本因素)

我的域名为 a.com ,他的域名为 b.com

以下是我能提供的所有信息,症状如标题,请大神赐教,谢谢!

Dockerfile 如下:

FROM ubuntu:latest
COPY crontabs /root
RUN apt update && \
	apt-get install -y -q libpcre3 libpcre3-dev zlib1g zlib1g-dev git wget gcc make cmake cpp autoconf automake cron && \
	cd /usr/local/src && \
	wget http://nginx.org/download/nginx-1.11.8.tar.gz && \
	tar xzf nginx-1.11.8.tar.gz && \
	wget https://www.openssl.org/source/openssl-1.1.0d.tar.gz && \
	tar xzf openssl-1.1.0d.tar.gz && \
	groupadd www && \
	useradd www -g www -s /sbin/nologin && \
	git clone https://github.com/grahamedgecombe/nginx-ct && \
	cd /usr/local/src/nginx-1.11.8 && \
	./configure --user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-http_v2_module --with-openssl=../openssl-1.1.0d --add-module=../nginx-ct && \
	make -j2 && make install && \
	ln -s /usr/local/nginx/sbin/nginx /usr/local/bin/nginx && \
	rm -rf /usr/local/src/* && \
	apt remove -y -q git wget gcc make cmake cpp autoconf automake && \
	apt autoremove -y && \
	apt purge -y -q git wget gcc make cmake cpp autoconf automake && \
	mkdir -p /usr/local/nginx/conf/vhost && \
	mkdir -p /home/wwwroot && \
	crontab /root/crontabs
COPY ["nginx.conf","fastcgi.conf","/usr/local/nginx/conf/"]
COPY ["a.com.conf","b.com.conf","/usr/local/nginx/conf/vhost/"]
ADD ssl.tar.gz /root
EXPOSE 80
EXPOSE 443
CMD ["nginx","-g","daemon off;"]

a.com.conf:

server {
    server_name a.com www.a.com;
    listen 80;
    location ^~ /.well-known/acme-challenge/ {
        alias /home/wwwroot/challenges/;
        try_files $uri =404;
    }

    location / {
        rewrite ^/(.*)$ https://a.com/$1 permanent;
    }
}
server {
    server_name a.com www.a.com;
    listen               443 ssl http2 reuseport fastopen=3;
    root /home/wwwroot/hexo;
    server_tokens        off;
    ssl_ct on;
    ssl_certificate      /root/ssl/a/a.com.rsa.pem;
    ssl_certificate_key  /root/ssl/a/a.com.rsa.key;
    ssl_ct_static_scts   /root/ssl/a/scts/rsa;

    ssl_certificate      /root/ssl/a/a.ecc.pem;
    ssl_certificate_key  /root/ssl/a/a.ecc.key;
    ssl_ct_static_scts   /root/ssl/a/scts/ecc;
    ssl_dhparam          /root/ssl/a/dhparams.pem;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers  on;
    ssl_ecdh_curve secp384r1;
    ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache          shared:SSL:50m;
    ssl_session_timeout        1d;
    ssl_session_tickets        on;
    ssl_stapling               on;
    ssl_stapling_verify        on;
    resolver                   8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout           10s;
    add_header    Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
    add_header    Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=";pin-sha256="Fbs+o+IxVNTHBpjNQYfX/TBnxPC+OWLYxQLEtqkrAfM=";max-age=2592000; includeSubDomains';
    index index.html;
    location / {
    	expires 120s;
    }
    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|ico)$ {
    expires 30d;
    access_log off;
    }
location ~ .*\.(js|css)?$ {
    expires 7d;
    access_log off;
    }
}

b.conf:

server {
    server_name b.com,www.b.com;
    location ^~ /.well-known/acme-challenge/ {
        alias /home/wwwroot/challenges/;
        try_files $uri =404;
    }

    location / {
        rewrite ^/(.*)$ https://b.com/$1 permanent;
    }
}
server {
    server_name b.com,www.b.com;
    listen               443 ssl http2;
    index index.php;
    root  /home/wwwroot/b;

    if (!-e $request_filename) {
        rewrite ^(.*)$ /index.php$1 last;
    }

    location ~ .*\.php(\/.*)*$ {
        include fastcgi.conf;
        fastcgi_pass  cgi:9001;
    }

    server_tokens        off;
    ssl_ct on;
    ssl_certificate      /root/ssl/b/b.com.rsa.pem;
    ssl_certificate_key  /root/ssl/b/b.com.rsa.key;
    ssl_ct_static_scts   /root/ssl/b/scts/rsa;

    ssl_certificate      /root/ssl/b/b.com.ecc.pem;
    ssl_certificate_key  /root/ssl/b/b.com.ecc.key;
    ssl_ct_static_scts   /root/ssl/b/scts/ecc;
    ssl_dhparam          /root/ssl/b/dhparams.pem;
    ssl_ciphers EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+ECDSA+AES128:EECDH+aRSA+AES128:RSA+AES128:EECDH+ECDSA+AES256:EECDH+aRSA+AES256:RSA+AES256:EECDH+ECDSA+3DES:EECDH+aRSA+3DES:RSA+3DES:!MD5;
    ssl_prefer_server_ciphers  on;
    ssl_ecdh_curve secp384r1;
    ssl_protocols              TLSv1 TLSv1.1 TLSv1.2;
    ssl_session_cache          shared:SSL:50m;
    ssl_session_timeout        1d;
    ssl_session_tickets        on;
    ssl_stapling               on;
    ssl_stapling_verify        on;
    resolver                   8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout           10s;
    add_header    Strict-Transport-Security 'max-age=31536000; includeSubDomains; preload';
    add_header    Public-Key-Pins 'pin-sha256="YLh1dUR9y6Kja30RrAn7JKnbQG/uEtLMkBgFF2Fuihg=";pin-sha256="Fbs+o+IxVNTHBpjNQYfX/TBnxPC+OWLYxQLEtqkrAfM=";max-age=2592000; includeSubDomains';
}

error_log  /root/b_error.log  crit;

/root/ssl 目录的结构图:

/root/ssl
|-- b.com.ecc.pem
|-- a
|   |-- account.key
|   |-- acme_tiny.py
|   |-- ct-submit-1.0.0
|   |   |-- LICENSE
|   |   |-- README.markdown
|   |   |-- ct-submit
|   |   `-- ct-submit.go
|   |-- ct-submit.zip
|   |-- dhparams.pem
|   |-- intermediate.pem
|   |-- renew_ecc.sh
|   |-- renew_rsa.sh
|   |-- root.pem
|   |-- scts
|   |   |-- ecc
|   |   |   |-- b.com.ecc.sct
|   |   |   `-- a.com.ecc.sct
|   |   `-- rsa
|   |       `-- a.com.rsa.sct
|   |-- signed_ecc.crt
|   |-- signed_rsa.crt
|   |-- a.com.ecc.csr
|   |-- a.com.ecc.key
|   |-- a.com.ecc.pem
|   |-- a.com.rsa.csr
|   |-- a.com.rsa.key
|   `-- a.com.rsa.pem
|-- intermediate.pem
|-- root.pem
|-- signed_ecc.crt
`-- b
    |-- account.key
    |-- acme_tiny.py
    |-- b.com.ecc.csr
    |-- b.com.ecc.key
    |-- b.com.ecc.pem
    |-- b.com.rsa.csr
    |-- b.com.rsa.key
    |-- b.com.rsa.pem
    |-- ct-submit-1.0.0
    |   |-- LICENSE
    |   |-- README.markdown
    |   |-- ct-submit
    |   `-- ct-submit.go
    |-- dhparams.pem
    |-- intermediate.pem
    |-- renew_ecc.sh
    |-- renew_rsa.sh
    |-- root.pem
    |-- scts
    |   |-- ecc
    |   |   `-- b.com.ecc.sct
    |   `-- rsa
    |       `-- b.com.rsa.sct
    |-- signed_ecc.crt
    `-- signed_rsa.crt

*.ecc.pem表示该域名的 ECC 证书,*.rsa.pem表示该域名的 RSA 证书,signed_*.crt表示 Let ‘ s encrypt 签发但还未进行证书链合并的证书;*.ecc.key表示对应域名 ECC 证书私钥,*.rsa.key表示对应域名 RSA 证书私钥;*.ecc.sct表示该域名 ECC 证书的 SCT 文件,*.rsa.sct表示该域名 RSA 证书的 SCT 文件。account.key为用户密钥。

renew_*.sh为对应证书更新脚本。 renew_ecc.sh:

#!/bin/bash
cd /root/ssl/a
python acme_tiny.py --account-key ./account.key --csr ./a.com.ecc.csr --acme-dir /home/wwwroot/challenges/ > ./signed_ecc.crt
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
wget -O - https://letsencrypt.org/certs/isrgrootx1.pem > root.pem
cat signed_ecc.crt intermediate.pem > a.com.ecc.pem
./ct-submit-1.0.0/ct-submit ct.googleapis.com/pilot </root/ssl/a/a.com.ecc.pem >/root/ssl/a/scts/ecc/a.com.ecc.sct
kill -HUP `cat /usr/local/nginx/logs/nginx.pid`

renew_rsa.sh:

#!/bin/bash
cd /root/ssl/a
python acme_tiny.py --account-key ./account.key --csr ./a.com.rsa.csr --acme-dir /home/wwwroot/challenges/ > ./signed_rsa.crt
wget -O - https://letsencrypt.org/certs/lets-encrypt-x3-cross-signed.pem > intermediate.pem
wget -O - https://letsencrypt.org/certs/isrgrootx1.pem > root.pem
cat signed_rsa.crt intermediate.pem > a.com.rsa.pem
./ct-submit-1.0.0/ct-submit ct.googleapis.com/pilot </root/ssl/a/a.com.rsa.pem >/root/ssl/a/scts/rsa/a.com.rsa.sct
kill -HUP `cat /usr/local/nginx/logs/nginx.pid`
5925 次点击
所在节点    NGINX
40 条回复
fourstring
2017-02-06 11:10:09 +08:00
@wql #20
那就是说不能双站点共存?
wql
2017-02-06 12:08:45 +08:00
@fourstring 我目前部署下来只发现两个解决方案:
1.放弃给每个站点单独设双证书
2.统一使用 SAN 来部署双证书
fourstring
2017-02-06 13:20:43 +08:00
@wql #22 统一使用 SAN ?具体怎么操作呢?
我对 SAN 的了解仅限于指定备用名称???
wql
2017-02-06 13:42:03 +08:00
@fourstring 我可能表达不清楚,就是分别签发 ECDSA 和 RSA 这两张证书,这两张证书要把四个域名都填上。就是备用名称。
fourstring
2017-02-06 13:44:42 +08:00
@wql #24 啊, SAN 还能填非本域名的域名?
lhbc
2017-02-06 13:49:18 +08:00
@fourstring SAN 可以包含多个顶级域名
你看下淘宝和天猫的证书
fourstring
2017-02-06 13:54:31 +08:00
@lhbc #26 涨姿势了,谢谢!
fourstring
2017-02-06 18:35:36 +08:00
@lhbc #26
@wql #24 这个问题已经解决,但是 CT 策略失效了,是因为 SAN 有多个顶级域名的原因吗?
wql
2017-02-06 18:38:19 +08:00
@fourstring 你是否知道,每签发一次证书,必须重新获取一次 SCT?
fourstring
2017-02-06 18:39:03 +08:00
@wql #29 我的脚本里都是这么做的,签发一次就获取一次
lightening
2017-02-06 18:43:22 +08:00
你还有一个选择,试试我的 https://github.com/SteveLTN/https-portal
wql
2017-02-06 18:46:57 +08:00
@fourstring 证书已经同一,那么 sct 必然是同一组啊。
fourstring
2017-02-06 18:47:32 +08:00
@wql #32 是的,然并卵……
chromee
2017-02-06 19:05:39 +08:00
@fourstring 看这个 issue https://github.com/grahamedgecombe/nginx-ct/issues/13
nginx-ct 用 OpenSSL 1.1.0 并用 SNI 的话不会对非默认的 vhost 返回 ct 信息
我之前也遇到了这个问题 我觉得用 OpenSSL 1.0.2 的最新版就行 没必要上 1.1.0
fourstring
2017-02-06 19:17:14 +08:00
@chromee #34 这样啊,谢谢!
bobylive
2017-02-06 19:45:59 +08:00
之前試過這個情況,臨時用 default_server 解決,暫時也沒研究出什麼好方法,因為以前就有一個 IP 一個證書的說法。
fourstring
2017-02-06 19:48:29 +08:00
@bobylive #36 已经解决。方案如下:用
openssl req -new -sha256 -key double.ecc.key -subj "/" -reqexts SAN -config <(cat /etc/ssl/openssl.cnf <(printf "[SAN]\nsubjectAltName=DNS:a.com,DNS:www.a.com,DNS:b.com,DNS:www.b.com")) > double.ecc.csr
生成带 SAN 的 CSR ,然后提交给 let ‘ s encrypt (如果是别家那得支持签发 SAN 证书)即可。 SAN 上限 100 个域名,这样一个证书可以给多个顶级域名使用。
如果用 nginx-ct 开启 CT ,不要使用 openssl1.1.0
evlos
2017-02-06 20:21:23 +08:00
另外我会单独加一个 default ,这样就算写错配置也不会很尴尬地进到其他 host 里
bobylive
2017-02-06 22:47:17 +08:00
@fourstring 感謝,等我試試
Showfom
2017-02-07 07:11:59 +08:00
@bobylive 同意 楼主没有加 default_server 的锅

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/338308

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX