为Blog启用HTTPS
我的Blog主要是记录并总结一些遇到的问题及解决方法,或是自己的技术积累,其实并不需要HTTPS,但每次打开网站总能看到标题栏提示不安全,这也不是很爽,所以一直想启用HTTPS,但一直没有实施。最近正好有些时间,所以就把这个搞定了,但其实也是遇到了很多问题,在此记录下来。
HTTPS认证过程
首先看下HTTPS的认证过程,我使用电脑通过4G联网访问我的网站,把报文抓了下来,来简单展示认证的几个关键过程,以了解其工作原理(不做详细介绍):
- 客户端与服务端的三次握手;
- 三次握手成功后,客户端发送Client Hello,其包含客户端随机数、支持的TLS版本、客户端支持的密码套件(Cipher Suite)和客户端共享Key;
- 这里有5个包,第1个是ACK,第2个是Server Hello,其包服务器随机数,含支持的TLS版本,服务器支持的密码套件(Cipher Suite)和服务器共享Key,并标记Change Cipher Spec,代表3、4、5包都是加密的,后面3个包中包含了加密扩展,证书和Finished。能解密看里面的内容吗,当然可以,具体可以参考下一小节;
- 客户端收到后,用服务端证书中的公钥加密接下来用于通信的秘钥,密文发给服务端(这点我在抓包中没看到,暂时不理解);
- 服务端收到后用自己的私钥解密,拿到通信秘钥,后面的通信使用这个秘钥进行加密数据;
如何Decode TLS报文
这里只介绍MacOS,其他系统类似,可以自行查阅。根据相关文档,只有Firefox和Chome支持此种方法把交互的key保持下来,以供Wireshark进行解码。
设置环境变量,游览器会自动调用这个参数:
~ % export SSLKEYLOGFILE=~/Documents/sslkeylog.log
~ % echo $SSLKEYLOGFILE
/Users/xxxxx/Documents/sslkeylog.log
在Wireshark上关联这个文件:Preferences -> Protocols -> TLS -> (Pre)-Master-Secret log filename
然后打开游览器,游览抓包,在报文中就可以看到Decrypted TLS的相关信息了
~ % /Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome -incognito
申请SSL/TLS证书
证书可以通过很多途径获得,但各有优缺点:
- 比如参考我之前的文章,可以进行自签证书,虽然数据也是加密的,但游览器是不认的,因为你是个人,不是权威机构,换句话说,没有给游览器交过保护费,此时查看证书会有个红叉;
- 可以依托Let’s Encrypt免费签SSL/TLS R3证书,这个游览器是认可的,关键是免费的,不过时间比较短,只有90天,详细可以查看这里;
- 使用域名服务商提供的免费SSL/TLS证书,缺点是需要让互访流量经过域名服务商,换句话就是要用域名提供商代理你的网站流量(也就是做CDN),这样可以做到端到端的加密;
我最近刚把域名从Godaddy transfer到Cloudflare上了, 原因主要是域名注册续费,Godaddy每年都提高续费价钱,个人Blog很难承受的住,果断切到了Cloudflare,这里的续费费用是透明的,每年是一样的。正好Cloudflare提供免费的HTTPS的证书,所以直接通过Cloudflare来实现HTTPS就好了。
Cloudflare相关配置
设置加密规则,我这里使用的的严格加密方式,也就是服务器上需要使用Cloudflare的证书
然后在Origin Server中创建证书,我这里没法截图,因为证书已经生成了,直接文字描述:
- 使用Cloudflare生成私钥和CSR,2048就好
- 选择要保护的域名
- 年限默认15年
生成好后,下载源证书和私钥(注:要自行保留好私钥,Cloudflare不保存私钥信息)到服务器上。
切换博客到Nginx
开始以为直接用Nginx代理下就可以了,后来发现想简单了。原来的博客是通过Apacha建立的,如果用Nginx,就需要替换掉Apacha。但Apacha是支持对PHP代码的解析,而Nginx则不支持,因此想用Nginx,就需要一个中间件,来替代Apacha对PHP代码支持的工作,这个组件就是Nginx的FastCGI模块,Nginx只是将结果反馈给客户端。
优化docker-compose文件
原始文件可以参考我之前的文章:Blog顺利完成搬家之Bandwagonhost VPS 中的 docker-compose.yml文件,下面是适配Nginx的配置:
- 给每个容器都增加了一个app-network的网络,并bridge所有关联docker;
- 指定Wordpress FPM版本,在这个版本里,PHP也从之前的7升级到了8,这也为后面的问题埋下了伏笔;
- 限制Wordpress的CPU利用率,此容器只能使用50%的CPU资源(至于原因可以看这里:https://www.zhaocs.info/blog-revision-record#ftoc-heading-25),注意用了这部分配置,需要在启动时加上兼容flag,如:docker-compose –compatibility up -d;
- FPM版本的默认端口是9000;
- 增加Nginx容器的配置,其中挂载两个目录,一个是Wordpress,另一个是Nginx相关的配置文件目录,并放通80和443端口;
- 增加redis,来为Wordpress开启持久对象缓存;
- 注意不需要暴露到internet的端口不要设置ports参数,如wordpress和redis,都是内部通信,完全没必要设置ports,如果设置了,这些端口被暴露到internet上,有被攻击的风险,我就被攻击了,折腾了好久才彻底解决,具体可以看博客修订记录 23年8月和11月的记录;
[root@xxx my_wordpress]# more docker-compose.yml
version: '6'
services:
db:
image: mysql:5.7
volumes:
- ./mysql_data:/var/lib/mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: xxxx
MYSQL_DATABASE: wordpress
MYSQL_USER: wordpress
MYSQL_PASSWORD: wordpress
networks:
- app-network
redis:
image: redis:alpine
restart: always
# ports:
# - 6379:6379
networks:
- app-network
wordpress:
depends_on:
- db
image: wordpress:6.2.1-php8.0-fpm
deploy:
resources:
limits:
cpus: '0.50'
# ports:
# - "9000:9000"
logging:
options:
max-size: "1m"
volumes:
- ./wordpress_data:/var/www/html
restart: always
environment:
WORDPRESS_DB_NAME: wordpress
WORDPRESS_DB_HOST: db:3306
WORDPRESS_DB_USER: wordpress
WORDPRESS_DB_PASSWORD: wordpress
networks:
- app-network
nginx:
depends_on:
- wordpress
image: nginx:latest
volumes:
- ./wordpress_data:/var/www/html
- ./nginx-conf:/etc/nginx/conf.d
restart: always
ports:
- 80:80
- 443:443
links:
- wordpress
networks:
- app-network
# phpmyadmin:
# depends_on:
# - db
# image: phpmyadmin/phpmyadmin
# ports:
# - "8080:80"
# restart: always
# environment:
# PMA_HOST: db
# PMA_USER: wordpress
# PMA_PASSWORD: wordpress
# networks:
# - app-network
volumes:
db_data:
networks:
app-network:
driver: bridge
Nginx配置文件
[root@xxx my_wordpress]# more nginx-conf/default.conf
# redirect http -> https
server {
listen 80;
listen [::]:80;
server_name www.zhaocs.info zhaocs.info;
return 301 https://www.zhaocs.info$request_uri;
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name www.zhaocs.info;
root /var/www/html;
index index.php;
client_max_body_size 64m; # if not add, attach file will have issue.
ssl_certificate /etc/nginx/conf.d/xxx.pem;
ssl_certificate_key /etc/nginx/conf.d/xxx.key;
## This block will handle .htaccess files since Nginx won’t serve them.
## The deny_all directive ensures that .htaccess files will never be served to users.
location ~ /\.ht {
deny all;
}
location = /favicon.ico {
log_not_found off;
access_log off;
}
location = /robots.txt {
log_not_found off;
access_log off;
allow all;
}
location / {
## Try_files directive is used to check for files that match individual URI requests.
## Instead of returning a 404 Not Found status as a default, however, you’ll pass control
## to WordPress’s index.php file with the request arguments.
try_files $uri $uri/ /index.php?$args;
}
location ~ \.php$ {
try_files $uri =404;
fastcgi_split_path_info ^(.+\.php)(/.+)$;
fastcgi_pass wordpress:9000;
fastcgi_index index.php;
include fastcgi_params;
#fastcgi_intercept_errors on;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
}
location ~* \.(css|gif|ico|jpeg|jpg|js|png)$ {
expires max;
log_not_found off;
}
location = /xmlrpc.php { #因为这个导致了cpu高,关闭此无用接口
deny all;
}
}
优化文件目录
为了方便后续的维护,通过软链接的方式统一进行维护,如下:
ln -s /opt/wp-backup/wpbackup.sh /opt/wpbackup.sh
ln -s /opt/wp-backup/tscpupeak.sh /opt/tscpupeak.sh
ln -s /opt/wp-backup/nginx-conf/ /opt/my_wordpress/nginx-conf
ln -s /opt/wp-backup/docker-compose.yml /opt/my_wordpress/docker-compose.yml
my_wordpress是工作目录,wp-backup是备份目录,nginx-conf是nginx的配置文件,wp-backup.sh脚本定期备份mysql数据库和wordpress_data文件并打包存到wp-backup目录,群辉NAS定期对这个目录进行备份同步:
[root@xxxxx]# tree -L 2 /opt/
/opt/
├── my_wordpress
│ ├── docker-compose.yml -> /opt/wp-backup/docker-compose.yml
│ ├── mysql_data
│ ├── nginx-conf -> /opt/wp-backup/nginx-conf/
│ └── wordpress_data
├── tscpupeak.sh -> /opt/wp-backup/tscpupeak.sh
├── wp-backup
│ ├── docker-compose.yml
│ ├── nginx-conf
│ ├── tscpupeak.sh
│ └── wpbackup.sh
└── wpbackup.sh -> /opt/wp-backup/wpbackup.sh
veth端口映射和交互流量
大部分流量都在container间交互了,特别是redis和wordpress两个container,如下:
问题及解决
不兼容的插件导致Wordpress崩溃
恢复Mysql和Wordpress数据后,https解析正常,但发现Wordpress无法正常显示,只显示遇到了致命错误:
此时可以使用下面命令开启Wordpress的debug模式,在wp-config.php配置中:
// 开启WP_DEBUG模式
define( 'WP_DEBUG', true);
// 开启DEBUG日志,一定要记得关闭这个日志功能并清理这个日志文件哦,产生的日志文件在: /wp-content/debug.log
define( 'WP_DEBUG_LOG', true);
// 显示errors and warnings
define( 'WP_DEBUG_DISPLAY', true);
@ini_set( 'display_errors', 'On');
开启后再次打开网站,可以看到如下错误,经过分析,就是因为升级到PHP8以后,部分插件和主题不兼容导致的:
Fatal error: Uncaught Error: Call to undefined function create_function() in /var/www/html/wp-content/plugins/auto-syntaxhighlighter/auto-syntaxhighlighter.php:119
Stack trace: #0 /var/www/html/wp-settings.php(453): include_once() #1 /var/www/html/wp-config.php(84): require_once('/var/www/html/w...') #2 /var/www/html/wp-load.php(50): require_once('/var/www/html/w...') #3 /var/www/html/wp-blog-header.php(13): require_once('/var/www/html/w...') #4 /var/www/html/index.php(17): require('/var/www/html/w...') #5 {main} thrown in /var/www/html/wp-content/plugins/auto-syntaxhighlighter/auto-syntaxhighlighter.php on line 119
打开phpMyAdmin,连接到MySQL的数据库中,输入下面SQL命令:
SELECT * FROM wp_options WHERE option_name ='active_plugins';
a:13{i:0;s:19:"akismet/akismet.php";i:1;s:49:"auto-syntaxhighlighter/auto-syntaxhighlighter.php";i:3;s:75:"delete-all-comments-of-website/delete-all-comments-of-wordpress-website.php";i:4;s:46:"feed-reading-blogroll/feedreading_blogroll.php";i:5;s:23:"fixed-toc/fixed-toc.php";i:6;s:19:"jetpack/jetpack.php";i:7;s:15:"mulberrykit.php";i:8;s:9:"og/og.php";i:9;s:47:"simple-yearly-archive/simple-yearly-archive.php";i:10;s:37:"tinymce-advanced/tinymce-advanced.php";i:11;s:25:"wp-cumulus/wp-cumulus.php";i:12;s:39:"wp-recentcomments/wp-recentcomments.php";i:13;s:21:"wp-ulike/wp-ulike.php";}
把value改成 a:0:{} 后,关闭所有插件,如果想把网站名称改了,也可以使用下面这些SQL语句找到后直接改数据库的内容:
SELECT * FROM wp_options WHERE option_name ='siteurl';
SELECT * FROM wp_options WHERE option_name ='home';
不兼容的主题导致Wordpress崩溃
使用类似的SQL语句可以找到当前使用的主题,并更改成默认主题,这样就可以恢复了,如下:
但我的主题是经过自己一点一点魔改的,里面加了一些定制的内容。我尝试换成其他主题,但想要适配我的需求,仍然需要时间折腾,所以暂时还是先想办法把问题解决了,下面是开启Debug模式后的告警:
这里有两个问题,一个是”rigister_sidebar调用方法不正确“,另一个是”call_user_func_array() expects“,对于第一个问题,可以直接去主题目录,查找函数,并增加一行指令:
[root@xxx page-shippou]# grep -rn 'register_sidebar' *
functions.php:284:if( function_exists('register_sidebar') ) {
functions.php:285: register_sidebar(array(
[root@xxx page-shippou]# more functions.php |grep register_sideba -A 5
if( function_exists('register_sidebar') ) {
register_sidebar(array(
'name' => 'bottom-sidebar',
'id' => 'sidebar-1', //添加这行
'before_widget' => '<li id="%1$s" class="widget %2$s">',
'after_widget' => '</li>',
'before_title' => '<h3>',
对于第二个问题,经查找,注释掉非法语句就可以了,详细可以看这里,我这里只有add_action:
[root@xxx page-shippou]# more functions.php |grep add_action -A 3 -B 3
};
// register functions
//add_action('admin_menu', array('pageOptions', 'add'));
/** page options */
class pageOptions {
说白了还是因为主题太老了,很多新功能没有适配,老语法也不再支持了-_-…