基于 docker 使用 nginx + phpfpm + typecho 搭建一个自己博客(下)

in Server with 0 comment

由于文章内容较长,在此分为三个部分:

上:基础工作和创建mysql容器
中:创建配置php-fpm和nginx容器
下:安装typecho及Lets Encrypt证书(本文)

安装typecho

我们删除掉 ~/docker-blog/website/blog 目录下的 index.phphello.html 文件, 然后下载 typecho 版本 1.1 (17.10.30) 到此目录下。执行解压操作,命令如下:

wget http://typecho.org/downloads/1.1-17.10.30-release.tar.gz
tar -xzf 1.1-17.10.30-release.tar.gz
rm -f 1.1-17.10.30-release.tar.gz
mv build/* ./
rm -rf build/

现在,访问网站 www.demo.com,surprise ! 出现了 typecho 的安装界面。

安装界面的配置很简单,有几个点注意一下:数据库地址 填写 mydb(我们在 docker-compose.ymlmysql 容器起的服务(别)名),数据库数据库密码数据库前缀 填写前面 创建配置mysql容器 中配置的信息 blogblog123.

点击 确认,开始安装 会弹出 安装程序无法自动创建 config.inc.php 文件 提示信息,我们直接在 ~/docker-blog/website/blog 目录下直接创建 config.inc.php 文件,文件内容安装页面已经给出,这里就是不展示了。点击 创建完毕,继续安装 按钮继续,大功告成!

php pathinfo模式

上面的 大功告成 有点假,你会发现除 index.php 页面,你几乎访问不了其它任何页面。仔细观察访问的页面, e.g. http://www.demo.com/index.php/archives/1/ 其中 archives/1/ 并不是真实存在的路径,这一部分仅仅是作为 index.php 文件的查询参数,为了优化url才做的,这种模式叫 pathinfo模式

nginx 开启 pathinfo模式 有多种方式,这里介绍两种,一种是通过 fastcgi_param PATH_INFO 传参,另一种是通过 rewrite 重写 url。

# fastcgi
location ~ [^/]\.php(/|$) {
    fastcgi_pass php-fpm:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    fastcgi_split_path_info ^(.+?\.php)(/.*)$;
    set $path_info $fastcgi_path_info;
    fastcgi_param PATH_INFO $path_info;
    try_files $fastcgi_script_name =404;
    fastcgi_param SCRIPT_FILENAME /var/www/html/blog/$fastcgi_script_name;
}

# url rewrite
location ~ [^/]\.php(/|$) {
    fastcgi_pass php-fpm:9000;
    fastcgi_index index.php;
    include fastcgi_params;
    if (!-e $request_filename) {
        rewrite ^(.*)$ /index.php?$1 last;
    }
    fastcgi_param SCRIPT_FILENAME /var/www/html/blog/$fastcgi_script_name;
}

这里就采用了 fastcgi_param 传参方式。为了简化和复用配置信息,我们把配置信息写到单独的一个文件,名字叫:pathinfo_config。这里没有添加 .conf 文件后缀,原因是前面 nginx 配置中有一行 include /etc/nginx/conf.d/*.conf; 会把所有以 .conf 结尾的文件包含进去,对于不使用 php 编写的程序,这一点视为 不必要的浪费

现在,把配置修改后,重新启动 nginx 服务,就可以访问其它页面了。

cd ~/docker-blog
docker-compose restart nginx

安装配置证书

证书的的安装方式

Lets Encrypt

一个免费、自动化、开放的证书签发服务。它由 ISRG(Internet Security Research Group,互联网安全研究小组)提供服务,而 ISRG 是来自于美国加利福尼亚州的一个公益组织。Let's Encrypt 得到了 Mozilla、Cisco、Akamai、Electronic Frontier Foundation 和 Chrome 等众多公司和机构的支持,发展十分迅猛。

特点:免费、安装简单,但只有90天的有效期(这个可以通过脚本定期更新,做到永久使用)。

安装Lets Encrypt证书

本次使用 acme.sh 工具来生成 Let's Encrypt 证书,由于使用了 Docker,并且 web 服务已经运行了,这里就选用 Webroot mode。在执行命令之前,我们需要对 nginx 做一些配置,让其能正常访问站点目录下的 well-known 目录,编辑 ~/docker-blog/nginx/conf/vhost/blog.conf 文件:

Webroot mode 支持证书自动刷新,为每60天刷新一次

关于 acme.sh 工具的安装及使用,请参考:https://github.com/Neilpang/acme.sh 页面

server {
    listen  80;
    server_name www.demo.com;
    index index.html index.htm index.php;
    root /usr/share/nginx/html/blog;

    # 添加这下面的配置
    location ~ /.well-known {
      allow all;
    }

    location ~ [^/]\.php(/|$) {
        # ...
    }
    # 省略其余部分
}

再执行以下命令:

cd ~/docker-blog
docker-compose restart nginx
acme.sh --issue -d www.demo.com -w /root/docker-blog/website/blog

命令执行成功后,可以看到证书生成后存放的路径(由于我这边宿主机器还安装的 lnmp 软件,只是停止了 nginx,证书直接生成到了 /usr/local/nginx/conf/ssl/www.demo.com 目录下,生成的目录可能不同,这里要以实际路径为准。)

nginx配置证书

上面把证书生成成功后,我们先修改 ~/docker-blog/docker-compose.yml 文件,挂载证书目录到 nginx 服务容器中(这里只展示了 nginx 配置服务节点):

version: '3.7'

services:

  nginx:
    image: nginx:1.15.9
    restart: on-failure
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf/vhost:/etc/nginx/conf.d:ro
      - ./nginx/log:/var/log/nginx
      - ./website:/usr/share/nginx/html
      # 添加下面这一行
      - /usr/local/nginx/conf/ssl:/etc/ssl/certs
    environment:
      - "TZ=Asia/Shanghai"
    depends_on:
      - php-fpm
    networks:
      - blog-net

接下来配置 nginx,修改 ~/docker-blog/nginx/conf/vhost/blog.conf 文件,文件整体内容如下:

server {
    listen  80;
    server_name www.demo.com;
    index index.html index.htm index.php;
    root /usr/share/nginx/html/blog;

    location ~ /.well-known {
        allow all;
    }

    location / {
        rewrite ^/(.*)$ https://www.demo.com/$1 permanent;
    }

    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf)$ {
        expires 1h;
    }

    location ~ .*\.(js|css)?$ {
        expires 12h;
    }

    location ~ /\. {
        deny all;
    }
}

server {
    listen 443 ssl http2;
    server_name www.demo.com;
    index index.html index.htm index.php;
    root /usr/share/nginx/html/blog;

    ssl_certificate /etc/ssl/certs/www.demo.com/fullchain.cer;
    ssl_certificate_key /etc/ssl/certs/www.demo.com/doc.vkarz.com.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
    ssl_prefer_server_ciphers on;
    ssl_ciphers "EECDH+CHACHA20:EECDH+CHACHA20-draft:EECDH+AES128:RSA+AES128:EECDH+AES256:RSA+AES256:EECDH+3DES:RSA+3DES:!MD5";
    # openssl dhparam -out /etc/nginx/ssl/dhparam.pem 2048
    ssl_dhparam /etc/ssl/certs/dhparam.pem;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;

    location ~ [^/]\.php(/|$) {
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        include /etc/nginx/conf.d/pathinfo_config;
        fastcgi_param SCRIPT_FILENAME /var/www/html/blog/$fastcgi_script_name;
    }
}

注: ssl on; 这一行指令在 nginx 1.15.0 版本后已经被废弃。

注意 location ~ /.well-knownloction /, 这两个 location 的配置位置,nginx 做url匹配是从上到下依次执行的,acme.sh 再次刷新证书时需要用到这些配置。

下面重启一下服务,就可以查看效果了:

cd ~/docker-blog
docker-compose down
docker-compose up -d

增加网站的安全性

网站的安全

在配置容器时,应该注意避免暴露不必要的端口到外面,给居心叵测之人提供攻击的便利,应充分利用 docker network 的功能,使各个容器处于同一局域(内)网内,之间能正常访问。

下面介绍一些常用的 http 安全响应头

我们可以在 nginx server配置节中加入上面的介绍的响应头,来防止一些问题的发生。但也要注意,如果加入上面,可能会发生的问题,e.g. 使用富文本编辑器,页面包含 <iframe>等,下面我们强化一下 ~/docker-blog/nginx/conf/vhost/blog.conf 文件(这将是最终配置):

# 省略了80端口的配置信息
server {
    listen 443 ssl http2;
    server_name www.demo.com;
    index index.html index.htm index.php;
    root /usr/share/nginx/html/blog;

    # certs sent to the client in SERVER HELLO are concatenated in ssl_certificate
    ssl_certificate /etc/ssl/certs/www.demo.com/fullchain.cer;
    ssl_certificate_key /etc/ssl/certs/www.demo.com/www.demo.com.key;
    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/ssl/certs/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;

    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;

    # verify chain of trust of OCSP response using Root CA and Intermediate certs
    ssl_trusted_certificate /etc/ssl/certs/www.demo.com/ca.cer;

    # HSTS (ngx_http_headers_module is required) (31536000 seconds = 1 year)
    add_header Strict-Transport-Security "max-age=31536000; preload";

    location ~ [^/]\.php(/|$) {
        fastcgi_pass php-fpm:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        include /etc/nginx/conf.d/pathinfo_config;
        fastcgi_param SCRIPT_FILENAME /var/www/html/blog/$fastcgi_script_name;
    }
}
可以使用 Mozilla SSL Configuration Generator 来生成相应的配置。

配置完成后,我们可以在 Qualys SSL Labs 上来评测一下我们的网站。

宿主主机的安全

平时,对于我们来说,服务器常用的端口就是那么几个,其它端口我们可以选择关掉。

可以使用 netstat -ntpl 查看开放的端口,及占用的程序。

如果考虑服务器集群之类的,我们只开放相应的局域网IP段就可以了。现在大部分购买的主机都自还安全组设置,即使我们没有关闭服务器的端口,外网也是不能轻易访问的。

关于问题的排查

查看容器错误

查看容器的错误,通常都使用 docker logs <container id> 命令,输出详细的日志信息。

查看程序错误

通过配置 docker-compose.yml 文件来开启 nginx 调试模式:

web:
  image: nginx
  volumes:
    - ./nginx.conf:/etc/nginx/nginx.conf:ro
  command: [nginx-debug, '-g', 'daemon off;']

nginx 配置输出调试信息:

user  www-data;
worker_processes  1;

error_log  /var/log/nginx/debug.log debug;

# 省略其余配置

附一些遇到的问题及解散方案:

  1. 上传图片失败 请参考:Typecho - 上传图片失败解决办法并修改图片大小限制

最后 Ending

文章最后的部分写的比较粗,因为里面涵盖了太多的知识。小人才疏学浅,不敢妄谈。

关于 typecho 的更新信息,还请自行搜索解决,现在贴几个比较有用的地址:

如若文章中有不当之处,还请看官指出,小弟不胜感激!

Comments are closed.