使用 Docker 替代 OneinStack/LNMP

使用 Docker 替代 OneinStack/LNMP

持续更新中……

最近 OneinStack 和 LNMP 都被收购恶意挂马,此类一键安装脚本的安全已经不受控,建议还是手搓环境为好。

OneinStack被收购恶意挂马

记录一下使用 Docker 搭建 Nginx、PHP、Mysql 集成环境。

必备的功能

目录结构

├── mysql
         └── data
├── nginx
         ├── Dockerfile
         ├── conf.d
                  ├── default.conf
                  └── example.conf
         ├── nginx.conf
         └── ssl
├── php82
         ├── Dockerfile
         └── etc
             ├── php-fpm.conf
             ├── php-fpm.d
                      ├── docker.conf
                      ├── www.conf
                      └── zz-docker.conf
             ├── conf.d
             └── php.ini
├── www
         ├── html
         └── example.com
├── docker-compose.yml

在官方 PHP 镜像基础上安装必要的拓展

FROM php:8.2-fpm

LABEL maintainer="[email protected]"

RUN apt-get update \
  && apt-get install -y --no-install-recommends \
    libicu-dev \
    libz-dev \
    libpq-dev \
    libzip-dev \
    libonig-dev \
    libjpeg62-turbo-dev \
    libjpeg-dev \
    libpng-dev \
    libwebp-dev \
    libfreetype6-dev \
    libssl-dev \
    libmcrypt-dev \
    zlib1g-dev \
    libxml2-dev \
    libbz2-dev \
  && rm -rf /var/lib/apt/lists/*

RUN docker-php-ext-configure bcmath --enable-bcmath \
    && docker-php-ext-configure pcntl --enable-pcntl \
    && docker-php-ext-configure pdo_mysql --with-pdo-mysql \
    && docker-php-ext-configure mbstring --enable-mbstring \
    && docker-php-ext-configure soap --enable-soap \
    && docker-php-ext-configure exif --enable-exif \
    && docker-php-ext-install \
        bcmath \
        intl \
        mbstring \
        mysqli \
        pcntl \
        pdo_mysql \
        sockets \
        zip \
        soap \
        exif \
  && docker-php-ext-install opcache \
  && docker-php-ext-enable opcache


RUN docker-php-ext-configure gd --with-jpeg --with-webp --with-freetype \
            && docker-php-ext-install gd; 

RUN pecl install -o -f redis && docker-php-ext-enable redis;

RUN pecl install -o -f protobuf  && docker-php-ext-enable protobuf;

RUN pecl install -o -f swoole && docker-php-ext-enable swoole;

RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer

WORKDIR /var/www

使用 docker-compose.yml 运行多容器

Docker 的设计理念就是一个容器一个进程,把 LNMP 打包到一个容器不推荐,推荐使用 docker-compose 配置多容器运行。

修改一下 Mysql Root 密码。

version: '3.8'

services:
  nginx:
    image: nginx:latest
    ports:
      - 80:80
      - 443:443
    restart: always
    volumes:
      # nginx配置文件
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf:ro
      - ./nginx/conf.d:/etc/nginx/conf.d:ro
      - ./nginx/ssl:/etc/nginx/ssl
      - ./www:/var/www/
    networks:
      - backend
    environment:
      - TZ=Asia/Shanghai
    links: 
      - php82

  certbot:
    image: certbot/certbot
    container_name: certbot
    volumes:
      - ./www/certbot/:/var/www/certbot/:rw
      - ./nginx/ssl/:/etc/letsencrypt/:rw
    networks:
      - backend
    environment:
      - TZ=Asia/Shanghai

  php82:
    build:
      context: ./php82
    container_name: php82
    restart: always
    volumes:
      # php 配置文件
      - ./php82/etc/php.ini:/usr/local/etc/php/php.ini:ro
      - ./php82/etc/conf.d/docker-php-ext-opcache.ini:/usr/local/etc/php/conf.d/docker-php-ext-opcache.ini:ro
      - ./php82/etc/php-fpm.d:/usr/local/etc/php-fpm.d:ro
      # 项目代码
      - ./www:/var/www/
    networks:
      - backend
    environment:
      - TZ=Asia/Shanghai
    links: 
      - mysql
      - redis

  mysql:
    image: mysql:latest
    environment:
      - MYSQL_ROOT_PASSWORD=root_password
      - TZ=Asia/Shanghai
    volumes:
      # 数据文件
      - ./mysql/data:/var/lib/mysql
    command: --default-authentication-plugin=mysql_native_password
    restart: always   
    networks:
      - backend

  redis:
    image: redis:latest
    restart: always
    networks:
      - backend
    environment:
      - TZ=Asia/Shanghai


networks:
  backend:

Nginx、PHP、FPM 配置优化

...
events {
    worker_connections 65535;
}
...

http {
    charset                utf-8;
    sendfile               on;
    tcp_nopush             on;
    tcp_nodelay            on;
    server_tokens          off;
    log_not_found          off;
    types_hash_max_size    2048;
    types_hash_bucket_size 64;
    client_max_body_size   50M;
    gzip  on;
...
...
;post body 体积大小
post_max_size = 50M
...
;上传文件限制
upload_max_filesize = 50M
...
;禁用不安全的函数
disable_functions = passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,ini_alter,ini_restore,dl,readlink,symlink,popepassthru,stream_socket_server,fsocket,poket,popen
...

conf.d/docker-php-ext-opcache.ini

zend_extension=opcache.so

; 开启 opcache jit
opcache.enable=1
opcache.enable_cli=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=8
opcache.max_accelerated_files=100000
opcache.max_wasted_percentage=5
opcache.use_cwd=1
opcache.validate_timestamps=1
opcache.revalidate_freq=60
;opcache.save_comments=0
opcache.fast_shutdown=1
opcache.consistency_checks=0
;opcache.optimization_level=0
opcache.jit=1255
opcache.jit_buffer_size=100M
pm.max_children = 16
pm.start_servers = 10
pm.min_spare_servers = 8
pm.max_spare_servers = 16
pm.max_requests = 2048
pm.process_idle_timeout = 10s
request_terminate_timeout = 120
request_slowlog_timeout = 0

使用

1.更新系统 (Debian/Ubuntu)

apt update
apt upgrade
apt install screen wget

2.安装 Docker

3.下载 docker compose 文件

wget https://wordupr.com/public/wordupr-docker.zip
unzip wordupr-docker.zip

4.构建镜像

screen -S build #如果断线执行 screen -r build 可以恢复
cd wordupr-docker
docker compose build

5.修改 docker-compose.xml 中 Mysql 的 root 密码

6.启动容器

docker compose up -d

7.安装 phpMyadmin

为了管理数据方便安装 phpMyadmin,最好 Nginx 限制一下 ip 访问。

在 VPS shell 执行一下命令(非容器内)

  1. 打开默认目录
cd www/html
  1. 下载最新 phpMyadmin 源码,解压文件
wget https://files.phpmyadmin.net/phpMyAdmin/5.2.1/phpMyAdmin-5.2.1-all-languages.zip 
unzip phpMyAdmin-5.2.1-all-languages.zip
mv phpMyAdmin-5.2.1-all-languages phpMyAdmin
  1. 修改配置
cd phpMyAdmin
cp config.sample.inc.php config.inc.php
nano config.inc.php

在 config.inc.php 修改 host 配置为 mysql(docker-compose 里面定义的服务名,docker 会自动解析为 mysql 容器的 IP)

$cfg['Servers'][$i]['host'] = 'mysql';

修改 cookie 密钥,需要填写32位字符串,推荐使用 密码生成器

$cfg['blowfish_secret'] = '32位字符串';

8.新增站点

  1. 新建目录
mkdir www/example.com
  1. 配置临时的 HTTP 用于申请 SSL 证书
nano nginx/conf.d/example.com.conf

内容如下

server {
    listen 80;
    listen [::]:80;

    server_name example.com;

    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }

    location / {
        charset utf-8;
        default_type text/html;
        return 200 'Hello';
    }
}
  1. 更新 Nginx 配置
docker compose exec nginx nginx -s reload
  1. 申请证书
docker compose run --rm  certbot certonly --webroot --webroot-path /var/www/certbot/ -d example.com
  1. 删除临时 example.com.conf,配置完整的站点配置
server {
  listen 80;
  listen [::]:80;
  listen 443 ssl;
  listen [::]:443 ssl;
  ssl_certificate /etc/nginx/ssl/live/example.com/fullchain.pem;
  ssl_certificate_key /etc/nginx/ssl/live/example.com/privkey.pem;
  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_ecdh_curve X25519:prime256v1:secp384r1:secp521r1;
  ssl_ciphers ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256;
  ssl_conf_command Ciphersuites TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
  ssl_conf_command Options PrioritizeChaCha;
  ssl_prefer_server_ciphers on;
  ssl_session_timeout 10m;
  ssl_session_cache shared:SSL:10m;
  ssl_buffer_size 2k;
  add_header Strict-Transport-Security max-age=15768000;
  ssl_stapling on;
  ssl_stapling_verify on;
  server_name example.com;

  index index.html index.htm index.php;
  root /var/www/example.com;

  # PHP engine
  location ~ \.php$ {
      fastcgi_pass php82:9000;
      # 404
      try_files                     $fastcgi_script_name =404;
      # default fastcgi_params
      include                       fastcgi_params;
      # fastcgi settings
      fastcgi_index                 index.php;
      fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
  }

  location /.well-known/acme-challenge/ {
      root /var/www/certbot;
  }

  location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
    expires 30d;
    access_log off;
  }
  location ~ .*\.(js|css)?$ {
    expires 7d;
    access_log off;
  }
  location ~ /(\.user\.ini|\.ht|\.git|\.svn|\.project|LICENSE|README\.md) {
    deny all;
  }
}
  1. 再次更新 Nginx 配置
docker compose exec nginx nginx -s reload
  1. 配置 crontab 计划任务,每个月月初自动刷新
#更新https证书
1 1 1 * * cd /{目录} && docker compose run --rm certbot renew && docker compose exec nginx nginx -s reload
  1. 重置网站目录权限
docker compose exec php82 chown -R www-data:www-data /var/www
docker compose exec php82 find /var/www -type d -exec chmod 755 {} \;
docker compose exec php82 find /var/www -type f -exec chmod 644 {} \;