当新浪防盗链时,我在想些什么?续

工具 2020-02-29 8583 字 1285 浏览 点赞

起步

在去年五月份的时候,新浪免费图床做了防盗链机制,羊毛就不能褥了。个人博客不挂图片忍忍也就过去,但在某些时候还是不便。后来我采用 docker 镜像 + 开源图床系统 lychee-docker 的方式,在自己的阿里服务器上搭建了一个简易图床。时移近一年,使用起来没有什么不对劲的地方。但当时我知道的东西还是太少,做了许多不规范的操作,现在想弥补。

我基于前文(当新浪防盗链时,我在想些什么?)在虚拟机里还原了过往操作,并将在此基础上弥补之前的不足。其用意是:倘若你不幸参看我写的教程部署了图床系统,那么我带你入坑的,我负责把你带出来。

HTTP -> HTTPS

关于谷歌对 HTTP 协议越来越不友好的新闻屡见不鲜,而作为增加了加密层的 HTTPS 协议已经出来多年。不论是跟风还是为保数据安全,将 HTTP 更换至 HTTPS 都显得必要。

如果你的网站还不是 HTTPS,浏览器市场的霸主 chrome 就会负责任的告诉游客:当前您访问的网站不安全

所以第一步要做的就是,把 lychee-docker 原有的 HTTP 修改为 HTTPS。这其实很简单,因为图床系统使用 nginx 做代理,我们只需要修改 nginx 配置即可。

在动手之前,我们还需要准备 HTTPS 证书。譬如我的服务器与域名都是阿里那儿买的,于是证书索性也从阿里购买(SSL证书)。对于个人站点来说,所谓“购买”就是申请,不用花钱。当然,你也可以去其他机构获取证书,我既不曾尝试,便不再妄言,心中有疑惑可以向搜索引擎倾述。

我为方便本地演示,需要做一个伪证书,否则不能开启 HTTPS。

$ openssl req -x509 -newkey rsa:2048 -nodes -sha256 -keyout localhost-cert.key -out localhost-cert.pem

执行上述命令之后,在当前目录下就会生成 localhost-cert.key, localhost-cert.pem 两个文件。现在将其拷贝进图床所在的 docker 容器中。

$ docker cp localhost-cert.pem [container_id]:/etc/nginx
$ docker cp localhost-cert.key [container_id]:/etc/nginx

接着进入容器,修改 nginx 的配置。使其支持 HTTPS。

$ docker exec -it [container_id] bash  # 进入 docker
(docker) $ vi /etc/nginx/sites-enabled/lychee
  • 这里说明一下,所有在 docker 容器里面执行的命令,用 (docker) 前缀加以提示。

替换为以下内容。

server {
        listen 443 ssl;
        server_name localhost;

        ssl_certificate      localhost-cert.pem;  # 注意 1
        ssl_certificate_key  localhost-cert.key;  # 注意 2

        ssl_session_cache    shared:SSL:1m;
        ssl_session_timeout  5m;

        ssl_ciphers  HIGH:!aNULL:!MD5;
        ssl_prefer_server_ciphers  on;

        root /var/www/lychee;
        index index.php;
        client_max_body_size 20G;
        access_log /var/log/nginx/lychee.access.log;
        error_log /var/log/nginx/lychee.error.log;

        location / {
            index  index.php index.html index.htm;
        }

        location ~* \.(?:ico|css|js|gif|jpe?g|png)$ {
            expires max;
            add_header Pragma public;
            add_header Cache-Control "public, must-revalidate, proxy-revalidate";
        }

        location ~ \.php$ {
            fastcgi_split_path_info ^(.+\.php)(/.+)$;
            fastcgi_pass unix:/var/run/php5-fpm.sock;
            fastcgi_index index.php;
            include fastcgi_params;
        }
        
        include php.conf;
}

server {
        listen 80;
        server_name 192.168.111.136:5121;  # 注意 3
        rewrite ^(.*) https://$server_name$1 permanent;
}

你可以仔细对比上述内容和原来 /etc/nginx/sites-enabled/lychee 文件中的内容。其实就是把 server 中的 80 修改为 443 ssl,同时添加一系列 ssl 的设置。如果你有自己的 HTTPS 证书,你需要替换掉 localhost-cert.pem 和
localhost-cert.key,一定要确保文件路径正确。

最后在末尾添加监听 80 端口的 server,这样做是为了同时支持对 80 端口的访问,但并不能让用户真去访问 80 端口。rewrite ^(.*) https://$server_name$1 permanent 表示所有访问 80 端口的请求,都会自动跳转到 HTTPS 监听的端口。在 80 sever 中,需要留意 server_name 对应的值,这里写入的是容器外的 ip:port。域名也行,比方说我的域名是 youguanxinqing.xyz,所以我图床中 80 端口对应的 server_name 是:

server_name youguanxinqing.com:5121

在前文中我们用 5120 映射容器 80 端口,而 5121 映射容器中的 443 端口。

最后重启 nginx。

(docker) $ supervisorctl

为运行容器添加端口映射

到了这里,别以为就大功告成。用 docker ps 命令可以看到,容器与宿主机之间仅有一个端口映射:5120:80。我们还需要添加 5121:443,肯定不能去重新生成一个容器,所以涉及到修改 docker 配置。需要先把宿主机中的 docker 主进程停下来。

$ systemctl stop docker
$ cd /var/lib/docker/containers/[container_id]*
$ vim hostconfig.json
  • container_id 是你容器的 id 值,事实上这个值只是一串 hash 的前缀而已,为了进入对应的目录,需要在末尾添加通配符 *

hostconfig.json 文件中找到 PortBindings:"PortBindings":{"80/tcp":[{"HostIp":"","HostPort":"5120"}]}。我们根据 80 端口的映射格式,添加一个 443 端口(其实就是把内容复制一遍,修改一下端口值就好)。为方便你观看,我将关键内容缩进之后呈现如下。

"PortBindings":{
    "80/tcp":[{"HostIp":"","HostPort":"5120"}], 
    "443/tcp":[{"HostIp":"","HostPort":"5121"}]
}

保存退出,同级目录下打开 config.v2.json,找到"ExposedPorts":{"80/tcp":{}}。以同样的方式添加一个 443。

"ExposedPorts":{
    "80/tcp":{}, 
    "443/tcp":{}
}

启动 docker,启动容器:

$ systemctl start docker 
$ docker start [container_id]

如果操作无误,在执行 docker ps 命令后,你应该可以看到输出信息中有这样一段内容。有,则说明成功:

0.0.0.0:5120->80/tcp, 0.0.0.0:5121->443/tcp

现在访问图床就应该能看到协议是 HTTPS 了,即使你访问是 http://$your_host:5120 也会跳转到 https://$your_host:5121。

由于我使用的是本地测试环境,证书是未经认证的伪证书。所以访问的时候前面还是有“不安全”三个字。实际场景应该呈如下这般:

HTTPS -> HTTP2

这是一个可选项,但我建议你选择。

照理说更换为 HTTPS 已经很 OK 了,安全性也有了,“小锁”也是贼好看。但 HTTPS 其实只是 HTTP + SSL,在 HTTP 外面套了一层 SSL 而已,使用的还是 HTTP1.1 协议——这玩意儿很落伍了。如果你不怕麻烦,为什么不来试试 HTTP2 呢?相较于 HTTP1.1 来说,HTTP2 允许信道复用,分帧传输,头信息压缩,从传输效率上可以嘲讽 1.1 小弟弟。

要想容器中的 nginx 支持 HTTP2 需要源码编译一个版本高一点的 nginx。lychee-docker 中的 nginx 为 1.4.6,而如今最新稳定版本是 1.16.1。这是下载地址,毕竟是外网,如果你访问速度太慢可以从我的网盘下载(链接放在文末)。如果你太懒,不想编译也是可以的,我已将编译好的可执行文件放在网盘中。

注意了,nginx 有个蛋疼的地方,如果想无缝替换 nginx 可执行文件,就必须使用相同的参数,所以需要进入容器获取 nginx 配置时的参数。命令也很简单。

(docker) $ nginx -V

configure arguments: ” 后面就是我们要用到的配置参数,需要记录下来。回到宿主机,开始我们的源码编译之旅:

$ tar xf nginx-1.16.1.tar.gz
$ cd nginx-1.16.1
$ ./configure ...  # ... 表示之前记录的那串参数

回车之后大概率你会遇到这样一个错误:./configure: error: invalid option "--with-http_spdy_module",这是因为新版的 nginx 不支持该选项,需要将此参数替换为 --with-file-aio --with-http_v2_module

继续回车你还会遇到错误,类似于这样的一系列错误 ./configure: error: the HTTP XSLT module requires the libxml2/libxslt。这是因为你环境上缺少编译时用到的依赖。我暂无法告诉你需要安装哪些依赖,但你可以参考 Linux Nginx安装以及可能出现错误 这篇文章,几乎涵盖了所有需要安装的依赖。

当 configure 成功后你能看到输出信息如下。

开始编译 nginx。编译成功后可执行文件在 objs 目录下,你可以 ll 进行查看。

$ make
$ ll objs/nginx

我们需要先去备份容器中的 nginx。

(docker) $ mv /usr/sbin/nginx /usr/sbin/nginx.old

回到宿主机将刚刚编译好的 nginx 拷贝到容器中:

$ docker cp objs/nginx [container_id]:/usr/sbin/

进入容器重启 nginx。重启命令在上面贴过图,这里就不重复了。很遗憾,nginx 进程会起不来。这是因为 nginx 链接的动态库在容器中没找到。我整理了 supervisor 错误日志中的输出信息,一共需要六个动态库。

/usr/sbin/nginx: error while loading shared libraries: libpcre.so.1: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libssl.so.10: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libcrypto.so.10: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libgd.so.2: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libjpeg.so.62: cannot open shared object file: No such file or directory
/usr/sbin/nginx: error while loading shared libraries: libpng15.so.15: cannot open shared object file: No such file or directory

这里我说明一下,我的宿主机环境是 centos7,而 docker 容器中是 ubuntu 14,我直接将这些需要的动态库拷贝到容器的 /usr/lib 目下,发现是可行的。由于还涉及到动态库软链接,避免累述,我把这些动态库整理之后放在网盘里,请自行下载。将动态库拷贝到容器的 /usr/lib 目录下,修改 /etc/nginx/sites-enabled/lychee 文件,进入 supervisorctl 启动 nginx。

$ docker cp [动态库] [container_id]:/usr/lib

(docker) $ vi /etc/nginx/sites-enabled/lychee
# listen 443 ssl;
# 修改为:
# listen 443 ssl http2;

倘若一切成功,访问页面能看到请求的协议是 h2,也就是 http2 的缩写。

如何排查其他问题

在升级 nginx 过程中,我遇到的所有问题已经在前面一一说明了。但不同的人操作会遇到不同的问题,这在部署环境时屡见不鲜。一旦遇到上述之外的问题,请查看容器中 nginx 和 supervisor 的日志信息,根据错误内容到搜索引擎中寻找答案。

  • nginx 日志路径:/var/log/nginx。
  • supervisor 日志路径:/var/log/supervisor。

感谢

链接:https://pan.baidu.com/s/1rjW5ysRD_3EQinvkIFl7ng 提取码:yvt7



本文由 Guan 创作,采用 知识共享署名 3.0,可自由转载、引用,但需署名作者且注明文章出处。

还不快抢沙发

添加新评论