背景
一直想有一个个人博客空间,之前使用过自建的 WordPress,但是后来没有坚持下来。后来学会了 Node.js,想用 Node.js 开发的个人博客系统,之前查到过 Ghost,因为使用的是 Mysql 数据库,所以没有想使用。由于最近要重做管理平台,数据库计划由 MongoDB 迁移到 Mysql,所以有了使用 Ghost 的充分理由。
目标
- 正常安装 Ghost;
- 正常使用 Ghost;
- 学习 Ghost 的代码;
- Mysql 的连接和使用部分;
- 权限管理部分;
- 业务部分;
- 配置文件;
- 启动文件;
安装过程
安装 Ghost
第一次使用 Ghost-CLI 安装,提示一下内容:
⚠ Linux version is not Ubuntu 16,⚠ Missing package: systemd,⚠ Missing package: nginx
看来只有在 Ubuntu 16 上才可以使用 Ghost-CLI,我的 Centos 7 自然是不行了。因此采用最传统的安装方式。
-
下载最新的 Ghost 版本
curl -L https://ghost.org/zip/ghost-latest.zip -o ghost.zip
-
解压到指定文件夹
unzip ghost.zip -d ./ghost/
-
安装运行所需要的依赖包
npm install --production
-
测试运行
npm start --production
-
建立 PM2 配置文件,使用 PM2 运行 Ghost
pm2 ecosystem
pm2 start ecosystem.config.js 配置 Nginx
将数据库从 SQLite3 更换为 MariaDB,修改配置文件 config.js
database: {
client: 'mysql',
connection: {
host : '127.0.0.1',
user : 'your_database_user',
password : 'your_database_password',
database : 'ghost_db',
charset : 'utf8'
}
}
配置 HTTPS
- 安装 certbot;
- 修改原有 Nginx 配置文件;
- 下载证书;
- 再次编辑 Nginx 配置文件,启用 HTTP2 和 HTTPS;
- 定期自动更新证书
安装 certbot
sudo yum install epel-release
sudo yum install certbot
修改原有 Nginx 配置文件
Let's encrypt 为了鉴定域名的所有权,会在域名下的 /.well-known/acme-challenge
路径下放置一个文件,并做相应的鉴权,因此需要保证相关路径能够正确的解析,最简单的办法就是直接修改 Nginx 配置文件。
location ^~ /.well-known/acme-challenge/ {
root /usr/share/nginx/html;
}
location = /.well-known/acme-challenge/ {
return 404;
}
下载证书
certbot certonly --webroot -w /usr/share/nginx/html/ -d domain1.com -d domain2.com
然后就会进入安装界面,提示输入邮箱地址等信息,然后就安装完毕了。证书文件被保存在 /etc/letsencrypt/live/domain.com/fullchain.pem
下。
再次编辑 Nginx 配置文件,启用 HTTP2 和 HTTPS
upstream my_nodejs_upstream {
server 127.0.0.1:2368;
keepalive 64;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name domain.com;
# Redirect all HTTP requests to HTTPS with a 301 Moved
Permanently response.
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name domain.com;
# certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
ssl_certificate /etc/letsencrypt/live/domain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/domain.com/privkey.pem;
ssl_session_timeout 1d;
ssl_session_cache shared:SSL:50m;
ssl_session_tickets off;
# Diffie-Hellman parameter for DHE ciphersuites, recommended 2048 bits
ssl_dhparam /etc/nginx/ssl/dhparam.pem;
# intermediate configuration. tweak to your needs.
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
ssl_prefer_server_ciphers on;
# HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)
add_header Strict-Transport-Security max-age=15768000;
# OCSP Stapling ---
# fetch OCSP records from URL in ssl_certificate and cache them
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_max_temp_file_size 0;
proxy_pass http://my_nodejs_upstream/;
proxy_redirect off;
proxy_read_timeout 240s;
}
location ^~ /.well-known/acme-challenge/ {
root /usr/share/nginx/html;
}
location = /.well-known/acme-challenge/ {
return 404;
}
}
其中,ssl_certificate
开头的和 ssl_certificate_key
开头的这句分别对应刚刚下载好的证书。
另外,ssl_dhparam
开头的这句指向的证书需要通过以下代码来生成。
sudo mkdir /etc/nginx/ssl
sudo openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
定期自动更新证书
因为 Let's encrypt 证书的有效期限是 90 天,因此在 90 天必须要更新证书以避免证书失效。根据 Certbot 官网所述,只有在临近到期时才会真正更换证书,因此最好的每天执行更新命令,确保证书及时更新。
测试更新命令
使用以下命令测试更新证书,但并不会真的更新。
sudo certbot renew --dry-run
设置更新证书定时任务
首先创建定制任务
sudo crontabe -e
然后在文件中加入以下语句,表示在每天的 3 点 43 分尝试更新证书,如果证书更新成功,则重启 Nginx 服务以便读取新证书
43 3 * * * certbot renew --quiet --post-hook "systemctl reload nginx"
测试定时更新语句
为确保定时更新语句有效,最好单独测试一下,直接输入以下语句看执行结果
certbot renew --quiet --post-hook "systemctl reload nginx"
应该会提示还不需要更新,不过不要紧,只要测试语句有效就可以了。至此,HTTP2 和 HTTPS 的配置全部完毕。
测试配置成果
测试服务器 SSL 的安全性
Qualys SSL Labs 提供了全面的 SSL 安全性测试,填写你的网站域名,给自己的 HTTPS 配置打个分。
如果安装本教程来配置,正常情况下应该会得到 A+ 的得分。
测试 HTTP2
Chrome 下有个插件叫做 HTTP/2 and SPDY indicator。可以利用它来查看访问的站点是否支持 HTTP2。
HTTP2 的问题
话说 HTTP2 之前有 2 种实现的协商协议,分别是 NPN 和 ALPN,后来 Chrome 就只支持 ALPN 了。但当前大部分服务器的 OpenSSL 都是1.01e 版本,只支持 NPN。因此按照本教程部署完毕后,使用 Chrome 刚刚配置好的站点的时候发现还是不能使用 HTTP2,还需要在服务器上做一些调整才可以。
参考资料
- Ghost 官网
- Ghost 安装文档
- Ghost 配置文档
- PM2 文档
- Certbot 官网
- CentOS7 下使用 Certbot
- 使用 Certbot
- Node.js 下使用 Certbot
- crontab 定时任务