2018年9月29日 星期六

Docker Compose Wordpress Stack with OpenVPN and Haproxy

# install docker

#!/bin/bash
# 移除舊版本docker並安裝新版
sudo yum remove -y docker \
     docker-client \
     docker-client-latest \
     docker-common \
     docker-latest \
     docker-latest-logrotate \
     docker-logrotate \
     docker-selinux \
     docker-engine-selinux \
     docker-engine
sudo yum install -y yum-utils \
     device-mapper-persistent-data \
     lvm2
sudo yum-config-manager \
     --add-repo \
     https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install -y docker-ce docker-compose
sudo systemctl enable docker.service
sudo systemctl start docker.service

建立工作資料夾與必要設定檔案

1. mkdir -p docker-wp-compose/{php.ini.d,dh-param,nginx}
2. cd docker-wp-compose

Config files

  • 在 docker-wp-compose 資料夾中建立相關設定檔
  • 由於 openvpn 與 nginx 需共用 443 port ,因此使用 haproxy 處理共用 443 > port並轉送真實ip至nginx log內
  • 雖然有 sslh 與新版的 nginx 可用,但前者在轉送上不盡理想,調整 iptable 相關設定後仍未生效後者則是似乎不支援 openvpn 轉發,僅支援 ssh 通道
  • 使用 haproxy 之 proxy_protocol參數,讓 nginx 取得使用者真實 ip
架構圖如下:


nginx 與 haproxy 分別使用 docker bridge mode 對外 80 與 443 ,其中 nginx 80 port 直接對外是為了減少 let's encrypt 驗證失敗的風險,只是預設仍會把 80 非驗證 let's encrypt 需求轉往 https

docker

.env
  • 指定時區與 ssl 相關資訊
  • 設定 wordpress 資料庫 root 密碼
  • 需先將下列 key 產生完畢後設定
    • private key
    • public key
    • dhparam

# nginx config
TIME_ZONE_INFO=/usr/share/zoneinfo/Asia/Taipei
NGINX_PUBLIC_KEY=/docker-volumes/etc/letsencrypt/live/ooo.com/fullchain.pem
NGINX_PRIVATE_KEY=/docker-volumes/etc/letsencrypt/live/ooo.com/privkey.pem
NGINX_DHPARAM=./dh-param/dhparam-2048.pem
NGINX_CERTBOT_USE=/docker-volumes/data/letsencrypt
# mariadb config
MARIADB_ROOT_PASSWORD=your_password

docker-compose.yml
  • 設定 docker 網段為 10.5.0.0/24 供 nginx 解析真實使用者 ip 之信任網段
  • 對外僅開放 443 與 80 ,其餘使用 docker 內部解析連線

version: "3.5"
services:
    openvpn:
        cap_add:
            - NET_ADMIN
        image: kylemanna/openvpn
        container_name: openvpn
        restart: always
        volumes:
            - ./openvpn-data/conf:/etc/openvpn
        networks:
            - wp-stack-net

    haproxy:
        container_name: wp-haproxy
        image: haproxy:alpine
        ports:
            - '443:443'
        volumes:
            - ./haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro
        networks:
            - wp-stack-net
        restart: always

    nginx:
        container_name: wp-nginx
        image: nginx:latest
        ports:
            - '80:80'
        volumes:
            - ./nginx/:/etc/nginx/conf.d
            - ./nginx.conf:/etc/nginx/nginx.conf
            - ./logs/nginx:/var/log/nginx
            - ${TIME_ZONE_INFO}:/etc/localtime:ro
            - ./www:/var/www/html
            - ${NGINX_PUBLIC_KEY}:/etc/letsencrypt/live/fullchain.pem
            - ${NGINX_PRIVATE_KEY}:/etc/letsencrypt/live/privkey.pem
            - ${NGINX_DHPARAM}:/etc/ssl/certs/dhparam-2048.pem
            #for certbot challenges
            - ${NGINX_CERTBOT_USE}:/data/letsencrypt
        networks:
            - wp-stack-net
        restart: always
    
    mysql:
        container_name: wp-mariadb
        image: mariadb
        volumes:
            - ./mariadb:/var/lib/mysql
        environment:
            - MYSQL_ROOT_PASSWORD=${MARIADB_ROOT_PASSWORD}
        networks:
            - wp-stack-net
        restart: always
    
    wordpress:
        container_name: wp-php-fpm
        image: wordpress:4.9.8-fpm
        volumes:
            - ./www:/var/www/html
            - ./php.ini.d/general.ini:/usr/local/etc/php/conf.d/general.ini
        environment:
            - WORDPRESS_DB_NAME=wpdb
            - WORDPRESS_TABLE_PREFIX=wp_
            - WORDPRESS_DB_HOST=mysql
            - WORDPRESS_DB_PASSWORD=${MARIADB_ROOT_PASSWORD}
        networks:
            - wp-stack-net
        restart: always

networks:
    wp-stack-net:
        driver: bridge
        ipam:
            config:
              - subnet: 10.5.0.0/24

haproxy.cfg
  • 確認 ssl_hello_type 則轉給 nginx,否則預設導向 openvpn container
  • 將 send-proxy 開啟,確保真實 ip 可被 nginx 解析

global
    maxconn     4000

frontend ssl
    mode tcp
    bind 0.0.0.0:443
    tcp-request inspect-delay 5s
    tcp-request content accept if HTTP
    use_backend main-ssl if { req.ssl_hello_type 1 }
    default_backend openvpn

backend main-ssl
mode tcp
# use the send-proxy option for transfering the real ip to nginx
server main-ssl nginx:8443 send-proxy

backend openvpn
mode tcp
timeout server 2h
server openvpn openvpn:1194

nginx

nginx.conf
  • 開啟 gzip 壓縮

user  nginx;
worker_processes  1;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    keepalive_timeout  65;

    gzip  on;
    gzip_comp_level    6;
    include /etc/nginx/conf.d/*.conf;
}

nginx/wp.conf
  • 80 port 僅允許 let's encrypt 驗證,其餘轉至 https
  • 設定ssl key相關資訊
  • 設定 set_real_ip_from, proxy_protocol 取得使用者真實 ip
  • 關閉 nginx 版本資訊
  • 禁止連線存取 xmlrpc.php 確保資安

server {
    listen      80;
    server_name _;
    server_tokens off;
    location / {
        rewrite ^ https://$host$request_uri? permanent;
    }

    #for certbot challenges (renewal process)
    location ~ /.well-known/acme-challenge {
        allow all;
        root /data/letsencrypt;
    }
}

server {
    # add proxy_protocol for getting the real ip from haproxy
    listen 8443 ssl http2 proxy_protocol;
    server_name ooo.com;
    real_ip_header proxy_protocol;
    set_real_ip_from 10.5.0.0/24;

    root /var/www/html;
    index index.php;

    access_log /var/log/nginx/wp-access.log;
    error_log /var/log/nginx/wp-error.log;
    server_tokens off;

    ssl_buffer_size 8k;
    ssl_dhparam /etc/ssl/certs/dhparam-2048.pem;

    ssl_protocols TLSv1.2 TLSv1.1 TLSv1;
    ssl_prefer_server_ciphers on;
    ssl_ciphers ECDH+AESGCM:ECDH+AES256:ECDH+AES128:DH+3DES:!ADH:!AECDH:!MD5;

    # OCSP stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    resolver 8.8.8.8 8.8.4.4;

    ssl_ecdh_curve secp384r1;
    ssl_session_tickets off;
    ssl_certificate /etc/letsencrypt/live/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/privkey.pem;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }
    
    location = /xmlrpc.php {
        deny all;
        access_log off;
        log_not_found off;
    }

    location ~ \.php$ {
        try_files $uri =404;
        fastcgi_split_path_info ^(.+\.php)(/.+)$;
        fastcgi_pass wordpress:9000;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        fastcgi_param PATH_INFO $fastcgi_path_info;
    }
}

php

php.ini.d/general.ini
  • 關閉版本資訊
  • 設定時區
expose_php = off
timezone = Asia/Taipei

openvpn

  • 產生 openvpn 專用憑證可參考此產生
  • 產生之設定檔可能會有不全,可參考加入如下:
    • auth SHA512
    • comp-lzo
    • Tuning through put (提高傳輸效能參數)
    • push "dhcp-option DNS open_dns_ip"
    • proto tcp
openvpn-data/conf/openvpn.conf


server 192.168.255.0 255.255.255.0
verb 3
key /etc/openvpn/pki/private/ooo.com.key
ca /etc/openvpn/pki/ca.crt
cert /etc/openvpn/pki/issued/ooo.com.crt
dh /etc/openvpn/pki/dh.pem
tls-auth /etc/openvpn/pki/ta.key
key-direction 0
keepalive 10 60
persist-key
persist-tun
cipher aes-256-cbc
auth SHA512

# Tuning through put
sndbuf 393216
rcvbuf 393216
push "sndbuf 393216"
push "rcvbuf 393216"

proto tcp
# Rely on Docker to do port mapping, internally always 1194
port 1194
dev tun0
status /tmp/openvpn-status.log

user nobody
group nogroup
comp-lzo

### Route Configurations Below
route 192.168.254.0 255.255.255.0

### Push Configurations Below
push "block-outside-dns"
push "dhcp-option DNS 208.67.222.222"
push "dhcp-option DNS 208.67.220.220"
#push "comp-lzo no"

client config
  • 加入auth SHA512
  • comp-lzo

client
dev tun
proto tcp
remote YOUR_VPN_IP_OR_DOMAIN 443
resolv-retry infinite
nobind
block-outside-dns
persist-key
persist-tun
cipher aes-256-cbc
verb 3
comp-lzo
key-direction 1
auth SHA512


-----BEGIN ENCRYPTED PRIVATE KEY-----
-----END ENCRYPTED PRIVATE KEY-----


-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----


-----BEGIN CERTIFICATE-----
-----END CERTIFICATE-----

key-direction 1

#
# 2048 bit OpenVPN static key
#
-----BEGIN OpenVPN Static key V1-----
-----END OpenVPN Static key V1-----

redirect-gateway def1


啟用

完成後使用 docker-compose up -d 開啟服務

沒有留言:

張貼留言

MariaDB Cluster on CentOS7.6

目標 於3臺 CentOS 7.6 minimal 上建置 MariaDB 10.3 Galera cluster