如果你在自己的VPS上运行一个Node.js应用程序,你将需要一个解决方案来获得SSL证书。
今天,这样做的标准是使用Let's Encrypt和Certbot,这是一个来自EFF(又称电子前沿基金会)的工具,EFF是专注于数字世界中隐私、言论自由和一般公民自由的领先非营利组织。
这些是我们要遵循的步骤。
安装Certbot
这些说明假定你使用的是Ubuntu、Debian或任何其他使用apt-get 来管理软件包的Linux发行版。
sudo add-apt-repository ppa:certbot/certbot
sudo apt-get update
sudo apt-get install certbot
你也可以在Mac上安装Certbot,用于测试目的(需要Homebrew)。
但是,你需要将其与一个真实的域名联系起来,这样才会有用。
使用Certbot生成SSL证书
现在Certbot已经安装,你可以调用它来生成证书。你必须以root身份运行它。
certbot certonly --manual
...或者从非root用户调用sudo。
sudo certbot certonly --manual
这是整个过程的细节。
安装程序会要求你提供你网站的域名。
...然后会要求你提供电子邮件。
➜ sudo certbot certonly --manual
Password: XXXXXXXXXXXXXXXXXX
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Plugins selected: Authenticator manual, Installer None
Enter email address (used for urgent renewal and security notices) (Enter 'c' to
cancel): your@email.com
...并接受ToS。
Please read the Terms of Service at
https://letsencrypt.org/documents/LE-SA-v1.2-November-15-2017.pdf. You must
agree in order to register with the ACME server at
https://acme-v02.api.letsencrypt.org/directory
(A)gree/(C)ancel: A
...并要求允许分享你的电子邮件地址。
Would you be willing to share your email address with the Electronic Frontier
Foundation, a founding partner of the Let's Encrypt project and the non-profit
organization that develops Certbot? We'd like to send you email about our work
encrypting the web, EFF news, campaigns, and ways to support digital freedom.
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: Y
...最后,我们可以输入我们想使用SSL证书的域名。
Please enter in your domain name(s) (comma and/or space separated) (Enter 'c'
to cancel): copesflavio.com
...安装程序会询问是否可以记录你的IP地址。
Obtaining a new certificate
Performing the following challenges:
http-01 challenge for copesflavio.com
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
NOTE: The IP of this machine will be publicly logged as having requested this
certificate. If you're running certbot in manual mode on a machine that is not
your server, please ensure you're okay with that.
Are you OK with your IP being logged?
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
(Y)es/(N)o: y
...最后,我们进入了验证阶段
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Create a file containing just this data:
TS_oZ2-ji23jrio3j2irj3iroj_U51u1o0x7rrDY2E.1DzOo_voCOsrpddP_2kpoek2opeko2pke-UAPb21sW1c
And make it available on your web server at this URL:
http://copesflavio.com/.well-known/acme-challenge/TS_oZ2-ji23jrio3j2irj3iroj_U51u1o0x7rrDY2E
现在,让我们让Certbot单独呆几分钟。
我们需要通过在.well-known/acme-challenge/ 文件夹中创建一个名为TS_oZ2-ji23jrio3j2irj3iroj_U51u1o0x7rrDY2E 的文件来验证我们拥有这个域名。请注意!我刚才粘贴的那个奇怪的字符串会在你每次经历这个过程时发生变化。
你需要创建这个文件夹和文件,因为它们默认是不存在的。
在这个文件中,你需要把Certbot打印的内容放进去。
TS_oZ2-ji23jrio3j2irj3iroj_U51u1o0x7rrDY2E.1DzOo_voCOsrpddP_2kpoek2opeko2pke-UAPb21sW1c
至于文件名--这个字符串在你每次运行Certbot时都是唯一的。
允许Express为静态文件提供服务
为了从Express提供该文件,你需要启用服务静态文件。你可以创建一个static 文件夹,并在那里添加.well-known 子文件夹,然后像这样配置Express。
const express = require('express')
const app = express()
//...
app.use(express.static(__dirname + '/static', { dotfiles: 'allow' }))
//...
dotfiles 选项是强制性的,否则,.well-known ,这是一个点状文件(因为它以点开头),不会被显示。这是一项安全措施,因为点文件可能包含敏感信息,它们最好在默认情况下被保留下来。
确认域名
现在运行该应用程序,并确保该文件可以从公共互联网上到达。回到仍在运行的Certbot,按ENTER键继续编写脚本。
获得证书
就这样了!如果一切顺利,Certbot创建了证书和私钥,并把它们放在你电脑上的一个文件夹里(当然,它会告诉你是哪个文件夹)。
现在,只要把这些路径复制/粘贴到你的应用程序中,就可以开始使用它们来为你的请求提供服务。
const fs = require('fs')
const https = require('https')
const app = express()
app.get('/', (req, res) => {
res.send('Hello HTTPS!')
})
https
.createServer(
{
key: fs.readFileSync('/etc/letsencrypt/path/to/key.pem'),
cert: fs.readFileSync('/etc/letsencrypt/path/to/cert.pem'),
ca: fs.readFileSync('/etc/letsencrypt/path/to/chain.pem'),
},
app
)
.listen(443, () => {
console.log('Listening...')
})
注意,我让这个服务器听从443端口,所以它需要以root权限运行。
另外,该服务器只在HTTPS中运行,因为我使用了https.createServer() 。你也可以在这旁边部署一个HTTP服务器,通过运行。
http.createServer(app).listen(80, () => {
console.log('Listening...')
})
https
.createServer(
{
key: fs.readFileSync('/etc/letsencrypt/path/to/key.pem'),
cert: fs.readFileSync('/etc/letsencrypt/path/to/cert.pem'),
ca: fs.readFileSync('/etc/letsencrypt/path/to/chain.pem'),
},
app
)
.listen(443, () => {
console.log('Listening...')
})
设置更新
SSL证书的有效期只有90天,所以你需要设置一个自动系统来更新它。
如何设置?使用一个cron job。
cron job是一种在指定时间间隔内运行任务的方法。它可以是每星期、每分钟、每个月,等等。
在我们的例子中,我们将按照Certbot文档中的建议,每天运行两次更新脚本。
首先,找出你系统中certbot 的绝对路径。我在macOS上使用type certbot 来获取它,在我的情况下,它在/usr/local/bin/certbot 。
这里是我们需要运行的脚本。
certbot renew
这是cron job的条目。
0 */12 * * * root /usr/local/bin/certbot renew >/dev/null 2>&1
上面说 "每12小时运行一次,每天:00:00和12:00"。
提示:我使用crontab-generator.org/来生成这一行
用这个命令把你新创建的脚本添加到系统的crontab中。
env EDITOR=pico crontab -e
这将打开pico 编辑器(请随意用你喜欢的编辑器代替)。简单地输入新的脚本,保存,然后cron job就安装好了。
一旦完成,你就可以通过运行以下命令看到活动的cron job列表。