本教程将讲解如何依托腾讯云主机(CVM), 搭建前沿的安全高性能 web 服务. 具体将包括: 配置域名解析, SSL 证书申请等 Web 服务的前置依赖, 以及安装部署最新版 Nginx Web 服务器, 并支持当前最新的 TLSv1.3 协议从而做到安全高效的访问支持.
0x00 环境准备
在开始前, 我们已经做好了以下两类资源的准备.
腾讯云实例一台:
腾讯云 CVM 产品主页 https://cloud.tencent.com/product/cvm?from=10680 中按需创建. 本文采用机型为 S4.SMALL2 的云服务器, 注意需要在选购时勾选上 "免费分配公网 IP". 我们选用操作系统是 CentOS 7, 当然对于其他系统如 Fedora/Ubuntu 等, 本文的大部分步骤都是通用的. 下文中所有命令均在该实例内部执行.
腾讯云域名一个:
腾讯云域名注册页 https://dnspod.cloud.tencent.com/ 中选择注册. 挑一个喜欢的域名吧~
下文中用 my-awesome-domain.com 指代.
0x01 设置域名解析
所谓添加域名解析记录, 就是将一条域名记录和一台公有云上的云服务器关联.
腾讯云的云解析 https://cloud.tencent.com/product/cns?from=10680 产品, 可以方便管理我们的域名解析工作.
所有解析记录的添加都可在云解析控制台 https://console.cloud.tencent.com/cns , 进行设置.
添加域名解析记录
记录类型: 选择 "A", 这类解析记录可以关联 IP 和域名;
记录值: CVM 云主机的公网 IP;
主机记录: 为我们需要的三级以上域名, 如填写 Web, 就是将域名 "web.my-awesome-domain.com" 关联到指定 IP.
腾讯云的解析生效时间是极快的, 这样我们就可以通过域名记录来登陆 CVM 了, 如:
SSH root@Web.my-awesome-domain.com
0x02 申请 SSL 证书
下面我们来申请 Let's Encrypt 证书. 通过官方提供的 Certbot https://certbot.eff.org/ 工具可以很方便地完成. Certbot 实质上属于 ACME 协议 https://tools.ietf.org/html/rfc8555 的客户端, 专门用于开发者自动化地管理证书申请流程.
安装 Certbot
yum install certbot
同时会安装相关依赖库, 如 openssl 等. Debian/Ubuntu 下换用 apt install 即可.
证书申请
certbot certonly --standalone -n -m my-email-address@example.com --agree-tos -d Web.my-awesome-domain.com
申请执行过程大约十秒左右, 如下图:
证书申请结果
成功后会在 / etc/letsencrypt/live/Web.my-awesome-domain.com / 目录下生成证书相关的文件: 证书文件 fullchain.pem 和证书私钥文件 privkey.pem, 后面在 Nginx 配置中将用到它们.
设置自动定期更新证书
申请的证书 90 天后会过期, 不过 Certbot 自带了定时重新申请颁发 (renew) 证书的工具: certbot-renew. 我们通过 systemctl 命令启动这个定时任务就不用担心证书过期的问题了.
systemctl start certbot-renew.timer
0x03 安装 Nginx
安装 Nginx 常见的两种方式: 通过发行版包管理管理工具, 或通过源码编译安装. 如果采用前者仅需:
- yum install nginx
- # Debian/Ubuntu 下: apt install nginx
然后跳过本节, 开始下一节的配置过程即可.
但是就当前的主流发行版 (如 Centos7/Ubuntu18 等) 中, 由于 nginx/openssl 等软件包的版本相对不高, 将无法支持 TLSv1.3 等特性, 所以请根据需求进行特性间的取舍.
那么, 接下来我们来详细讲解下通过源码安装最新版 Nginx, 当前最新的稳定版本是 1.16.0. 注意尽量安装最新的稳定版本, 过于久远的版本不支持很多特性, 如 HTTP/2(1.10 后支持)和以及 TLSv1.3(1.15 后支持)等.
软件的最新版本通常是不会在发行版的包管理工具 (如 Yum, APT) 的软件库中的, 而是需要我们源码编译安装. 不过对于我们 CVM 玩家来说这根本不是问题, 下面跟我一起体验更大的自由与灵活吧~
我们选择在 / opt 目录下完成 Nginx 的安装, 这通常是个合适的选择, 当然你习惯工作的任何目录都可以.
cd /opt
安装相关的依赖软件包
主要是编译器, PCRE 包和 zlib 包
- yum install gcc pcre-devel zlib-devel
- (Debian/Ubuntu 系统下需要用 apt install 完成, 对应的包名是 libpcre3-dev 和 zlib1g-dev)
下载 openssl 源码
下载最新版本的 openssl 库, 版本 1.1.1b. 这是因为 Nginx 中的 TLS 协议以及加密解密等工作是由外部的库 (如 libssl/libcrypto 等) 来完成的, 而它们都在 openssl 项目中实现. 系统默认的 openssl 是比较老旧的, 无法支持最新的 HTTP/2 和 TLS 特性.
只需要两步: 下载和解压即可. 无需编译安装.
- wget https://www.openssl.org/source/openssl-1.1.1b.tar.gz
- tar -zxvf openssl-1.1.1b.tar.gz
源码编译 Nginx
下载编译安装 Nginx, 版本 1.16.0.
- wget http://nginx.org/download/nginx-1.16.0.tar.gz
- tar -zxvf nginx-1.16.0.tar.gz
- cd nginx-1.16.0
配置编译选项, 注意这里我们需要指定 openssl 的代码目录, Nginx 编译时会顺便完成编译 openssl 中所需要的部分. 其选项我们这里重点关注 http/2 和 ssl 模块的启用. 对于其他的选项, 如果后续想改动只需重新配置和编译即可, 源码安装就是这么方便又任性.
- ./configure \
- --pid-path=/run/nginx.pid \
- --with-http_v2_module \
- --with-http_ssl_module \
- --with-openssl=/opt/openssl-1.1.1b
编译安装
make && make install
Nginx 会被默认安装在 / usr/local/nginx 目录下(也可由 prefix 编译选项指定).
到这里, 我们已完成了 Nginx 的安装. 其实执行
/usr/local/nginx/sbin/nginx
即可启动 Nginx 服务了. 不过且慢, 让我们把工作完成地更优雅一些.
配置 Nginx 服务为 systemd 系统服务
编辑文件:/lib/systemd/system/nginx.service, 加入如下内容
- [Unit]
- Description=The nginx HTTP and reverse proxy server
- After=network.target remote-fs.target nss-lookup.target
- [Service]
- Type=forking
- PIDFile=/run/nginx.pid
- ExecStartPre=/usr/bin/rm -f /run/nginx.pid
- ExecStartPre=/usr/local/nginx/sbin/nginx -t
- ExecStart=/usr/local/nginx/sbin/nginx
- ExecReload=/bin/kill -s HUP $MAINPID
- KillSignal=SIGQUIT
- TimeoutStopSec=5
- KillMode=process
- PrivateTmp=true
- [Install]
- WantedBy=multi-user.target
然后执行
- systemctl daemon-reload
- systemctl enable nginx.service
我们后续就可以通过 systemctl 命令来管理 Nginx 服务了, 如重启 (restart), 加载配置(reload) 等.
systemctl restart nginx.service
0x04 配置 Nginx
编辑 nginx.conf(或类似配置文件)中的 server 段, 设置证书 / 密钥等 ssl 相关参数, 并将 80 端口的 HTTP 服务重定向至 HTTPS 的 443 端口. 具体如下:
- server {
- listen 443 ssl http2;
- server_name Web.my-awesome-domain.com;
- ssl_certificate "/etc/letsencrypt/live/web.my-awesome-domain.com/fullchain.pem";
- ssl_certificate_key "/etc/letsencrypt/live/web.my-awesome-domain.com/privkey.pem";
- ssl_session_cache shared:SSL:1m;
- ssl_session_timeout 10m;
- ssl_protocols TLSv1.2 TLSv1.3;
- ssl_ciphers ECDHE-RSA-AES256-GCM-SHA384:!aNULL:!MD5:!RC4:!DHE;
- ssl_prefer_server_ciphers on;
- add_header Strict-Transport-Security "max-age=31536000";
- location / {
- root HTML;
- index index.HTML;
- }
- }
- server {
- listen 80;
- server_name Web.my-awesome-domain.com;
- if ($host = Web.my-awesome-domain.com) {
- return 301 https://$host$request_uri;
- }
- }
注意: 我们支持了 http2, 并且对于 SSL 协议, 我们同时支持了当前稳定的 TLSv1.2 和最新的 TLSv1.3.
然后重启服务, 完成!
systemctl restart nginx.service
0x05 验证访问
浏览器验证
现在, 让我们一起试试通过浏览器访问 Nginx 的测试主页吧: https://web.my-awesome-domain.com/
"Welcome to nginx!", 我们的 Web 服务基本搭建完成.
通过 Chrome 或 Firefox 的开发者工具, 可以查看验证证书细节和 TLS 协议的版本.
查看请求是否通过 HTTP/2 协议:
Firefox 查看请求头部
查看相关的 TLS 连接信息, 如协议版本, 证书以及 cipher suite:
Chrome 查看 TLS 连接信息
Firefox 查看 TLS 连接信息
我们可以看到, 主流的浏览器, 如 Chrome70/Firefox63, 均已经在 2018 年支持(即默认优先采用)TLSv1.3.
其他的浏览器 (如微信或 QQ 浏览器) 也相信会在不久的未来予以支持, 但目前对于服务器端的配置, 还应如上文所示尽量设置为 TLSv1.2 和 TLSv1.3 更加保险和兼容.
curl 验证
针对高端玩家, 非必要操作, 如引起不适直接跳过.
用当前最新 (版本 7.64.1) 的 curl 工具,(注意同样需要结合新版的 openssl 进行编译, 过程略过), 则可以通过指定 tls 版本来详细查看 TLS 握手过程的细节. 对应的命令参数和握手细节如下
- TLSv1.2
- curl https://web.my-awesome-domain.com -v --tlsv1.3 --ciphers ECDHE-RSA-AES256-GCM-SHA384
结果
- ...
- * TLSv1.3 (OUT), TLS handshake, Client hello (1):
- * TLSv1.3 (IN), TLS handshake, Server hello (2):
- * TLSv1.2 (IN), TLS handshake, Certificate (11):
- * TLSv1.2 (IN), TLS handshake, Server key exchange (12):
- * TLSv1.2 (IN), TLS handshake, Server finished (14):
- * TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
- * TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
- * TLSv1.2 (OUT), TLS handshake, Finished (20):
- * TLSv1.2 (IN), TLS handshake, Finished (20):
- * SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
- ...
- TLSv1.3
- curl https://web.my-awesome-domain.com -v --tlsv1.3
结果
- ...
- * TLSv1.3 (OUT), TLS handshake, Client hello (1):
- * TLSv1.3 (IN), TLS handshake, Server hello (2):
- * TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
- * TLSv1.3 (IN), TLS handshake, Certificate (11):
- * TLSv1.3 (IN), TLS handshake, CERT verify (15):
- * TLSv1.3 (IN), TLS handshake, Finished (20):
- * TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
- * TLSv1.3 (OUT), TLS handshake, Finished (20):
- * SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384
- ...
- * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
- * TLSv1.3 (IN), TLS handshake, Newsession Ticket (4):
- * old SSL session ID is stale, removing
- ...
以上验证, 对于 TLSv1.3 以及 v1.2 我们配置的服务器都能正常支持.
0x06 Bonus:HTTP/2,TLSv1.3 简介
HTTP/2
HTTP/2 是新一代的应用层协议, 标准发布于 2015 年. 相比 HTTP/1.1(上世纪末),HTTP/2 可以做到速度更快, 更加节省资源. 这主要是因为 HTTP/2 中, 请求 / 返回可以完全地多路复用传输 (Fully Multiplexed), 即一个 TCP 连接内可以真正同时完成多个请求, 而非简单流水线化. 其二进制的协议内容(Binary Frame) 以及压缩的请求头部 (HPACK) 也是效率提升的关键. HTTP/2 还支持服务端推送等特性. 另外重要一点, HTTP/2 事实上必须结合 TLS 使用(各大浏览器厂商的要求, 至少 TSLv1.2), 因此也更加安全.
HTTP/2 协议示意
HTTP/2 多路传输示意
TLSv1.3
TLSv1.3, 即安全传输层协议 ( Transport Layer Security) 的最新版. TLS 可以认为是 SSL 协议 (已经废弃) 的升级版, 通过对数据对称加密等方式保证客户端和服务器间通信的安全, 可靠和完整. 2008 年到 2018 年是 TLSv1.2 版本, 2018 年 8 月 TLSv1.3 正式发布. 其相比 TLSv1.2 有很多重要的变化, 比如优化了密码组件, 废除不安全的加密算法, 简化密钥交换为 PSK 模式, 简化的握手流程(甚至通过 early-data 实现 0-RTT), 会话保存等.
总结起来就是 TLSv1.3 更加安全, 更加快速的新一代标准安全协议.
TLS 握手流程示意
0x07 One More Thing
以上本教程就全部完成. 相信到这里, 你已经明白如何为 CVM 关联域名解析以及搭建基于 Nginx 的 Web 服务了, 那么就快去动手亲自实践下吧!
觉得以上步骤略显繁琐? CVM 全新的产品 (PAI 实例) 可以帮你一键完成大部分任务, 有兴趣可以尝试下.
一起来享受玩转 CVM 的乐趣吧~
0x08 参考资料
- https://letsencrypt.org/
- https://tools.ietf.org/html/rfc8555
- https://curl.haxx.se/
- https://www.openssl.org/
来源: https://www.qcloud.com/developer/article/1429314