手头上原本是一个apache基础的wordpress网站,想着直接配置https,一顿操作之后出了各种问题。后面又想搭一个CTFd平台,绑定到域名下的子域名上,这就不能直接配置https了,因为443端口只有一个。后面发现了可以利用nginx进行统一管理,零零散散折腾了很多天才搞完,在这里记录一下过程。

0x00 给wordpress配置https

wordpress是基于apache的,所以事先需要准备好SSL证书,证书的获得方式有很多,在这里不赘述。我是通过腾讯云官方的下载的,下文也以此为基本。

首先对于单一容器,配置https需要追加443端口,我将下载的证书存储在/data/cert/apache/目录下,并在开启时配置挂载。创建好容器后,我们在容器内部操作如下

  • 加载Apache2的SSL模块
    a2enmod ssl
    第一次加载会提示你重启,此处重启或稍后重启均可

  • 重启指令
    service apache2 restart
    重启之后容器会退出,重进即可

  • 修改配置文件

    加载之后,在/etc/apache2/sites-available/下修改配置文件default-ssl.conf000-default.conf

    • default-ssl.conf
    SSLCertificateFile  /data/cert/ephemerally.top.crt
    SSLCertificateKeyFile /data/cert/ephemerally.top.key

    修改上述两行,后面为你容器中挂载的证书路径,此处作用为绑定你的证书

    • 000-default.conf

    <VirtualHost *:80> </VirtualHost> 标签中增加下面的配置

     
        RewriteEngine   on
        RewriteBase /
        # FORCE HTTPS
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
    

    修改效果

    
        # The ServerName directive sets the request scheme, hostname and port that
        # the server uses to identify itself. This is used when creating
        # redirection URLs. In the context of virtual hosts, the ServerName
        # specifies what hostname must appear in the request's Host: header to
        # match this virtual host. For the default virtual host (this file) this
        # value is not decisive as it is used as a last resort host regardless.
        # However, you must set it for any further virtual host explicitly.
        #ServerName www.example.com
    
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www/html
    
       
        RewriteEngine   on
        RewriteBase /
        # FORCE HTTPS
        RewriteCond %{HTTPS} !=on
        RewriteRule ^/?(.*) https://%{SERVER_NAME}/$1 [R,L]
      

    此处效果为http 请求跳转到 https

  • 文件链接

    从 apache 的配置文件 apache2.conf 可以看到,apache 只会读取 /etc/apache2/sites-enabled 目录的配置文件,所以需要把 /etc/apache2/sites-available 下的 default-ssl.conf 文件链接到 /etc/apache2/sites-enabled 目录下。

    ln -s /etc/apache2/sites-available/default-ssl.conf \
    /etc/apache2/sites-enabled/default-ssl.conf
  • 修改wordpress后台

    到此的效果应该是,httpshttp均能正常访问网站,但是在访问网站的时候,会提示你页面存在http元素,所以在后台的仪表盘里,修改对应的地址,http改成https即可。

    如果前面有操作失误导致无法访问后台,可以在数据库中进行修改

    mysql> use wordpress;
    mysql> desc wp_options;
    +--------------+---------------------+------+-----+---------+----------------+
    | Field        | Type                | Null | Key | Default | Extra          |
    +--------------+---------------------+------+-----+---------+----------------+
    | option_id    | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
    | option_name  | varchar(191)        | NO   | UNI |         |                |
    | option_value | longtext            | NO   |     | NULL    |                |
    | autoload     | varchar(20)         | NO   | MUL | yes     |                |
    +--------------+---------------------+------+-----+---------+----------------+
    4 rows in set (0.00 sec)
    mysql> select option_id , option_value from wp_options where option_id = 1 or option_id = 2;
    +-----------+-------------------------+
    | option_id | option_value            |
    +-----------+-------------------------+
    |         1 | https://ephemerally.top |
    |         2 | https://ephemerally.top |
    +-----------+-------------------------+
    2 rows in set (0.00 sec)
    mysql> update wp_options set option_value = 'https://ephemerally.top' where option_id = 1;
    mysql> update wp_options set option_value = 'https://ephemerally.top' where option_id = 2;

    如上操作即可。

  • 修改成功之后,如果遇到访问http未跳转到https的,可以修改/var/www/html下的.htaccess文件

    
    RewriteEngine On
    RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
    RewriteBase /
    RewriteRule ^index\.php$ - [L]
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteCond %{REQUEST_FILENAME} !-d
    RewriteRule . /index.php [L]
    RewriteCond %{SERVER_PORT} !^443$
    RewriteRule (.*) https://%{SERVER_NAME}/$1 [R=301,L]
    

    参考如上

  • 为了方便维护,我是利用docker-compose写的启动,但是正常启动之后,上述的操作如果不设置挂载,会需要重复操作,因此可参考restart.sh

    容器名称:wordpress

    #! /bin/bash
    docker exec -it wordpress bash -c 'a2enmod ssl'
    docker cp ./conf/default-ssl.conf wordpress:/etc/apache2/sites-available/default-ssl.conf
    docker cp ./conf/000-default.conf wordpress:/etc/apache2/sites-available/000-default.conf
    docker exec -it wordpress bash -c 'ln -s /etc/apache2/sites-available/default-ssl.conf /etc/apache2/sites-enabled/default-ssl.conf && service apache2 restart'

0x01 用nginx接管端口

在只有一个服务的情况下,如上方法部署会比较方便。但是当服务器上有多个服务的时候,就可以通过nginx将请求转发到各个服务。

原来的情况下,我的80和443端口要跑我的网站,这个时候我要让nginx接管80和443端口,一种做法就是比如让我的网站跑在8080端口,然后nginx将8080端口的流量通过443端口转发到外网,在这个过程中用网站内部用http协议,nginx与外网传输用https协议。同样,CTFd如果跑在8001端口,同样用nginx进行转发。

但是,如果我不想对外显示端口,因为我想将端口绑定到域名上,通过端口无法访问服务,那么我可以建立一个docker网络,让nginx与我的网站在同一个docker网络下,在nginx中通过容器名称访问服务。

docker-compose.yml中添加

nginx:
    container_name: proxy_nginx
    image: nginx:latest
    volumes: 
      - /root/nginx/log/:/var/log/nginx
      - /root/nginx/nginx.conf:/etc/nginx/nginx.conf
      - /root/nginx/dist/:/usr/share/nginx/html
      - /data/cert/nginx/:/data/cert/
    ports:
      - "443:443"
      - "80:80"
    restart: always

我用同一个yml启动,就可以让他们在同一个网络中。

同样,在nginx中配置https的步骤会更加好理解

    server {
        listen 80;
        server_name  ephemerally.top;    
        rewrite ^(.*)$ https://${server_name}$1 permanent;
        }

    server {
        listen       443 ssl;         
        server_name  ephemerally.top;    
        ssl_certificate   /data/cert/ephemerally.top_bundle.crt;
        ssl_certificate_key  /data/cert/ephemerally.top.key;
        location / {
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass https://wordpress:443;
        }
    }

第一个server的作用是配置接收到80端口HTTP请求转发至HTTPS
而第二个server的作用是利用SSL证书配置HTTPS访问,wordpress是我内部容器的名称,这样就将对443端口的请求转发到了内部容器的443端口,跟直接容器 -p 443:443等效。

这里其实还有一种解决方式,就是我不需要配置wordpresshttps,直接内部利用http通信,只需要nginx和外网https通信。我开始是这样配置的,但是因为wordpress内部的关系,配置的过程中出了很多问题,因此我在wordpress内部配置https之后直接将https流量转出。

0x02 添加CTFd进入nginx管理

关于CTFd的配置参见《SSSCTF平台部署指南》

因为CTFd本身是一个多容器的庞大网络,并不方便和我的nginx共属一个网络,但是我只需要将CTFd的流量转发即可,比如说我CTFd部署在内网的8001端口,那我只要在内部写内网的8001端口

    server {
        listen 80;
        server_name  ctf.ephemerally.top;    
        rewrite ^(.*)$ https://${server_name}$1 permanent;
        }

    server {
        listen 443 ssl;
        server_name  ctf.ephemerally.top;    
        ssl_certificate   /data/cert/ctf.ephemerally.top_bundle.crt;
        ssl_certificate_key  /data/cert/ctf.ephemerally.top.key;
        location / {
            proxy_redirect off;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_pass http://x.x.x.x:8001;
        }

    }

proxy_pass处填写内网地址即可

第一个server的作用是配置接收到80端口HTTP请求转发至HTTPS
第二个server直接将内网8001端口的流量转发到443https

这里有一点需要注意,nginx配置完并不是实时生效的,可以通过docker restart proxy_nginx或在容器内部nginx -s reload重载配置。

至此,完成配置。正如你所见,通过ephemerally.top访问我的博客,通过ctf.ephemerally.top访问CTFd平台

0x03 参考链接

配置过程中参考了不少零零碎碎的资料,如下。nginx的功能远远不止于此,这里只介绍了它的反向代理功能

https://www.cnblogs.com/zhenhunfan2/p/14031224.html
https://www.jianshu.com/p/3378d2eacb3d
https://www.jianshu.com/p/3e25b24d7247