HTTPS 现状
早在 2016 年底, 我就写过 从 HTTP 到 HTTPS 系列 文章来讲解 HTTPS 当时结合本站的部署经验, 给大家详细介绍了 IIS 部署免费 HTTPS
这篇文章就跟大家介绍一下 Node.js 如何部署免费 HTTPS 以及简单的部署 HTTP/2
截止 2018 年 03 月 13 日, 由 Let's Encrypt 实时统计报告 显示, 在统计的 6930 多万活跃网站中, 已经有 5350 万 (约 77%) 的站点部署了 HTTPS 证书服务
同时 Google 透明度报告 - 网络上的 HTTPS 加密 中, 统计了使用 Chrome 浏览器, 访问的站点统计中, HTTPS 使用率的增长情况:
而在今年 2 月份, Chrome 团队也宣布, 将在 2018 年 7 月份发布的 Chrome 68 中, 将没有部署 HTTPS 的网站标记为 "不安全"
简而言之, HTTPS 大势所趋
Node.js 部署 HTTPS
早在 从 HTTP 到 HTTPS - IIS 部署免费 HTTPS 一文中, 我就指出了 Let's Encrypt 免费证书的优势:
由 ISRG(Internet Security Research Group, 互联网安全研究小组)提供服务, 免费访问速度快, 稳定等
所以这次部署的证书也是围绕 Let's Encrypt 展开
greenlock-express
由于 js 生态圈的繁华, 所以想找一个现有的包是件很轻松的事情, greenlock-express 这个包就帮助我们封装了 Let's Enctrypt 证书的部署, 只需要引入这个包并使用, 就可以:
自动注册 Let's Encrypt 证书
自动续订( 80 天左右), 且服务器无需重启
支持虚拟主机
并且 greenlock 相关的证书生态圈十分完善, 同样有支持 koa 的 greenlock-koa
安装和使用
通过 npm 安装 greenlock-express:
$ npm install --save greenlock-express@2.x
使用起来非常简单, 这是 greenlock-express 默认提供的 demo:
- const greenlock = require('greenlock-express')
- require('greenlock-express').create({
- // 测试
- server: 'staging',
- // 联系邮箱
- email: 'john.doe@example.com',
- // 是否同意 Let's Encrypt 条款... 这必须为 true 啊, 不然走不下去
- agreeTos: true,
- // 申请的域名列表, 不支持通配符
- approveDomains: [ 'tasaid.com', 'www.tasaid.com' ],
- // 绑定 express app
- app: require('express')().use('/', function (req, res) {
- res.end('Hello, World!');
- })
- }).listen(80, 443)
证书存在 ~/letsencrypt
当然上面代码只能用于测试 / 开发环境, 因为它并没有申请一个有效的证书, 而是生成了一个自签名的证书(跟以前的 12306 自签证书一样), 用于在开发环境中调试
API
greenlock-express 的 create(options) 函数参数签名如下:
- interface Options {
- /**
- * Express app
- */
- app: Express
- /*
- * 远程服务器
- * 测试环境中可用为 staging
- * 生产环境中为 https://acme-v01.api.letsencrypt.org/directory
- */
- server: string
- /**
- * 用于接收 let's encrypt 协议的邮箱
- */
- email: string
- /**
- * 是否同意协议
- */
- agreeTos: boolean
- /**
- * 在注册域名获取证书前, 会执行这个回调函数
- * string[]: 一组需要注册证书的域名
- * 函数: 第一个参数跟 Options 格式差不多, 第二个参数是当前自动获取的域名信息, 第三个参数是在处理完之后传递的回调函数
- */
- approveDomains: string[] | (opts, certs: cb) => any
- /**
- * 更新证书最大天数 (以毫秒为单位)
- */
- renewWithin: number
- /**
- * 更新证书的最小天数(以毫秒为单位)
- */
- renewBy: number
- }
经过测试, 在真实的生产环境中, approveDomains 必须为函数, 传数组的话不会生效
生产环境
生产环境中部署还需要做一些配置改动和引入一些包
更新包:
- $ npm i --save greenlock-express@2.x
- $ npm i --save le-challenge-fs
- $ npm i --save le-store-certbot
- $ npm i --save redirect-https
生产代码:
- const greenlock = require('greenlock-express')
- const express = require('express')
- const app = express()
- const lex = greenlock.create({
- // 注意这里要成这个固定地址
- server: 'https://acme-v01.api.letsencrypt.org/directory',
- challenges: {
- 'http-01': require('le-challenge-fs').create({ webrootPath: '~/letsencrypt/var/acme-challenges' })
- },
- store: require('le-store-certbot').create({
- webrootPath: '~/letsencrypt/srv/www/:hostname/.well-known/acme-challenge'
- }),
- approveDomains: (opts: any, certs: any, cb: any) => {
- appLog.info('approveDomains', { opts, certs })
- if (certs) {
- /*
* 注意这里如果是这样写的话, 一定要对域名做校验
* 否则其他人可以通过将域名指向你的服务器地址, 导致你注册了其他域名的证书
* 从而造成安全性问题
- *$/ opts.domains = certs.altnames
- opts.domains = [ 'tasaid.com', 'www.tasaid.com' ]
- } else {
- opts.email = '你的邮箱 @live.com'
- opts.agreeTos = true
- }
- cb(null, { options: opts, certs: certs })
- },
- })
- // 这里的 redirect-https 用于自动将 HTTP 请求跳到 HTTPS 上
- require('http').createServer(
- lex.middleware(
- require('redirect-https')()
- )
- ).listen(80, function () {
- console.log('Listening', `for ACME http-01 challenges on: ${JSON.stringify(this.address())}`)
- })
- // 绑定 HTTPS 端口
- require('https').createServer(
- lex.httpsOptions,
- lex.middleware(app)
- ).listen(443, function () {
- console.log(('App is running at http://localhost:%d in %s mode'), app.get('port'), app.get('env'))
- console.log('Press CTRL-C to stop\n')
- })
如果没有生效, 可以检查下 ~/letsencrypt 的证书信息, 和 443 端口是否打开
部署 HTTP/2
HTTP/2 是 HTTP/1.1 的升级版, 主要来说改进了这些地方:
二进制协议: 采用二进制流
多路复用: 一次请求多次复用管道
服务器推送: 解决 HTTP/1.x 时代最大的痛点
值的注意的是, HTTP/2 是支持 HTTP 协议的, 只不过浏览器厂商都不愿意支持 HTTP, 所以基本上可以认为, 用上 HTTP/2 的前置条件是必须部署 HTTPS
SPDY
早在 2009 年, Google 开发了一个实验性协议, 叫做 SPDY, 目的解决 HTTP/1.x 中的一些设计缺陷在 SPDY 发布几年后, 这个新的实验性协议得到了 ChromeFirefox 和 Opera 的支持, 应用越来越广泛然后 HTTP 工作组 (HTTP-WG) 在这个 SPDY 的基础上, 设计了 HTTP/2, 所以可以说 SPDY 是 HTTP/2 的前身
关于 HTTP/2 的详情可以参考 这篇文章
部署 HTTP/2
引入 HTTP/2 在 Node.js 中也十分简单, 只需要引入 spdy 包即可:
$ npm i --save spdy
然后我们把上一节的代码做一点修改即可支持 HTTP/2:
- const greenlock = require('greenlock-express')
- const express = require('express')
- // HTTP/2
- const spdy = require('spdy')
- const app = express()
- const lex = greenlock.create({
- // 注意这里要成这个固定地址
- server: 'https://acme-v01.api.letsencrypt.org/directory',
- challenges: {
- 'http-01': require('le-challenge-fs').create({ webrootPath: '~/letsencrypt/var/acme-challenges' })
- },
- store: require('le-store-certbot').create({
- webrootPath: '~/letsencrypt/srv/www/:hostname/.well-known/acme-challenge'
- }),
- approveDomains: (opts: any, certs: any, cb: any) => {
- appLog.info('approveDomains', { opts, certs })
- if (certs) {
- /*
* 注意这里如果是这样写的话, 一定要对域名做校验
* 否则其他人可以通过将域名指向你的服务器地址, 导致你注册了其他域名的证书
* 从而造成安全性问题
- *$/ opts.domains = certs.altnames
- opts.domains = [ 'tasaid.com', 'www.tasaid.com' ]
- } else {
- opts.email = '你的邮箱 @live.com'
- opts.agreeTos = true
- }
- cb(null, { options: opts, certs: certs })
- },
- })
- // 这里的 redirect-https 用于自动将 HTTP 请求跳到 HTTPS 上
- require('http').createServer(
- lex.middleware(
- require('redirect-https')()
- )
- ).listen(80, function () {
- console.log('Listening', `for ACME http-01 challenges on: ${JSON.stringify(this.address())}`)
- })
- // HTTP/2
- spdy.createServer(lex.httpsOptions, lex.middleware(app)).listen(443, function () {
- console.log('Listening https', `for ACME tls-sni-01 challenges and serve app on: ${JSON.stringify(this.address())}`)
- })
至于 HTTP/2 相关的技术应用, 会在后续篇幅中再为大家讲解
来源: https://juejin.im/post/5aa9e0906fb9a028bc2d7644