知识分享 – 玩转CVM:Web服务搭建

本教程将讲解如何依托腾讯云主机(CVM),搭建前沿的安全高性能Web服务。具体将包括:配置域名解析、SSL证书申请等Web服务的前置依赖,以及安装部署最新版Nginx Web服务器,并支持当前最新的TLSv1.3协议从而做到安全高效的访问支持。

0x00 环境准备

在开始前,我们已经做好了以下两类资源的准备。

  • 腾讯云实例一台:

腾讯云CVM产品主页中按需创建。本文采用机型为S4.SMALL2的云服务器,注意需要在选购时勾选上“免费分配公网IP”。我们选用操作系统是CentOS 7,当然对于其他系统如Fedora/Ubuntu等,本文的大部分步骤都是通用的。下文中所有命令均在该实例内部执行。

  • 腾讯云域名一个:

腾讯云域名注册页中选择注册。挑一个喜欢的域名吧~

下文中用my-awesome-domain.com指代。

0x01 设置域名解析

所谓添加域名解析记录,就是将一条域名记录和一台公有云上的云服务器关联。

腾讯云的云解析产品,可以方便管理我们的域名解析工作。

所有解析记录的添加都可在云解析控制台,进行设置。

添加域名解析记录

记录类型:选择“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工具可以很方便地完成。Certbot实质上属于ACME协议的客户端,专门用于开发者自动化地管理证书申请流程。

安装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 参考资料

正文完