RainLoop是一个开源的基于Web的邮件客户端,可以让你通过浏览器轻松地访问和管理你的电子邮件,项目官网:https://www.rainloop.net/

部署环境

  • 本地kvm虚拟机:debian12.5
  • 虚拟机内存:2G
  • cpu核心:2核

docker方式部署MySQL8和php8环境

  • 安装docker环境,基于官方软件源
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    ## 查看脚本
    root@env:~# cat docker_debian_install.sh
    #!/bin/bash
    echo -e '\e[33m清理旧版本。。。\e[0m'
    systemctl stop docker
    docker image prune -a -f
    docker container prune -f
    docker system prune
    docker network prune
    docker volume prune
    apt purge docker docker-engine docker.io containerd runc
    rm -rf /var/lib/docker
    rm -rf /var/lib/containerd
    echo -e '\e[33m设置存储库。。。\e[0m'
    apt update && apt upgrade
    apt -y install ca-certificates curl gnupg
    install -m 0755 -d /etc/apt/keyrings
    curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg
    chmod a+r /etc/apt/keyrings/docker.gpg
    echo "deb [arch="$(dpkg --print-architecture)" signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian "$(. /etc/os-release && echo "$VERSION_CODENAME")" stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null
    apt update && apt -y upgrade
    apt -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    systemctl enable docker
    systemctl restart docker
    if [ $? -eq 0 ];then
    echo -e '\e[36mok\e[0m'
    else
    echo -e '\e[31mfalse\e[0m'
    fi

    ## 执行并检查,由于dash和bash的些许不同,echo输出文本颜色会异常,不影响脚本正常运行
    root@env:~# sh docker_debian_install.sh
    root@env:~# docker -v
    Docker version 26.0.0, build 2ae903e
    root@env:~# docker compose version
    Docker Compose version v2.25.0
    root@env:~# systemctl status docker
    ● docker.service - Docker Application Container Engine
    Loaded: loaded (/lib/systemd/system/docker.service; enabled; preset: enabled)
    Active: active (running) since Thu 2024-04-04 12:46:36 CST; 1min 23s ago
  • 创建相关目录文件,下载rainloop项目源码包
    root@env:~# mkdir -p /app/{html,mysql_php}
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    ## rainloop-latest.zip官网下载后上传至服务器目录
    root@env:/app/html# ls
    rainloop-latest.zip

    ## 将压缩包解压至rainloop目录
    root@env:/app/html# unzip -d rainloop rainloop-latest.zip
    root@env:/app/html# ls
    rainloop rainloop-latest.zip

    ## 查看
    root@env:/app/html# ls rainloop
    data index.php rainloop
  • docker compose.yaml文件,mysql配置文件my.cnf
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    ## compose.yaml
    root@env:/app/mysql_php# cat compose.yaml
    services:
    rainloop_mysql:
    image: mysql:8.0.32
    container_name: mysql8
    restart: unless-stopped
    networks:
    - rainloop
    volumes:
    - rainloop_mysql:/var/lib/mysql
    - /app/mysql_php/my.cnf:/etc/my.cnf
    environment:
    MYSQL_ROOT_PASSWORD: T4GbWJ73hx
    MYSQL_DATABASE: rainloop
    MYSQL_USER: rainloop
    MYSQL_PASSWORD: N>A3zP43&b
    php_8.1:
    image: php:8.1-fpm-bullseye
    restart: unless-stopped
    container_name: php8
    networks:
    - rainloop
    ports:
    - "9000:9000"
    volumes:
    - /app/html/rainloop:/var/www/html
    - /etc/localtime:/etc/localtime
    networks:
    rainloop:
    driver: bridge
    volumes:
    rainloop_mysql:

    ## my.cnf
    [mysqld]
    skip-host-cache
    skip-name-resolve
    datadir=/var/lib/mysql
    socket=/var/run/mysqld/mysqld.sock
    secure-file-priv=/var/lib/mysql-files
    user=mysql
    pid-file=/var/run/mysqld/mysqld.pid
    port=3306
    local_infile=OFF
    default-time_zone='+8:00'
    max_connections=3000
    max_connect_errors=100
    default_authentication_plugin=mysql_native_password
    max_allowed_packet=100M
    open_files_limit=1048576
    log-error=error
    log_bin=mysql-bin
    binlog_format=row
    sync_binlog=1
    expire_logs_days=7
    max_binlog_size=100M

    [client]
    socket=/var/run/mysqld/mysqld.sock
  • 启动容器
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    ## 检查配置文件
    root@env:/app/mysql_php# docker compose config

    ## 启动并查看
    root@env:/app/mysql_php# docker compose up -d
    root@env:/app/mysql_php# docker compose ps
    NAME IMAGE COMMAND SERVICE CREATED STATUS PORTS
    mysql8 mysql:8.0.32 "docker-entrypoint.s…" rainloop_mysql 2 minutes ago Up 2 minutes 3306/tcp, 33060/tcp
    php8 php:8.1-fpm-bullseye "docker-php-entrypoi…" php_8.1 2 minutes ago Up 2 minutes 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp

    ## 启动日志信息
    root@env:/app/mysql_php# docker compose logs -n 3 rainloop_mysql
    mysql8 |
    mysql8 | 2024-04-04 07:14:57+00:00 [Note] [Entrypoint]: MySQL init process done. Ready for start up.
    mysql8 |
    root@env:/app/mysql_php# docker compose logs -n 3 php_8.1
    php8 | [04-Apr-2024 15:14:38] NOTICE: fpm is running, pid 1
    php8 | [04-Apr-2024 15:14:38] NOTICE: ready to handle connections

    ## 验证数据库,连接正常
    root@env:/app/mysql_php# docker exec -it mysql8 bash
    bash-4.4# mysql -urainloop -p'N>A3zP43&b'
    mysql: [Warning] Using a password on the command line interface can be insecure.
    Welcome to the MySQL monitor. Commands end with ; or \g.
    Your MySQL connection id is 8
    Server version: 8.0.32 MySQL Community Server - GPL

    Copyright (c) 2000, 2023, Oracle and/or its affiliates.

    Oracle is a registered trademark of Oracle Corporation and/or its
    affiliates. Other names may be trademarks of their respective
    owners.

    Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

    mysql> show databases;
    +--------------------+
    | Database |
    +--------------------+
    | information_schema |
    | performance_schema |
    | rainloop |
    +--------------------+
    3 rows in set (0.00 sec)

nginx由于频繁变更配置,采用源码安装,后续操作更为方便

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
## 下载源码
root@env:~# wget -c https://nginx.org/download/nginx-1.24.0.tar.gz

## 解压至当前目录
root@env:~# tar xf nginx-1.24.0.tar.gz

## 安装编译依赖
root@env:~# apt -y install build-essential libpcre3-dev zlib1g-dev libssl-dev

## 编译配置
root@env:~/nginx-1.24.0# ./configure --prefix=/usr/local/nginx --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module

## 编译并安装
root@env:~/nginx-1.24.0# make -j`nproc` && make install

## 创建nginx用户
root@env:~# useradd -M -s /sbin/nologin nginx

## 配置系统环境变量
root@env:/usr/local/nginx# echo "export PATH=\$PATH:/usr/local/nginx/sbin" >> /etc/profile
root@env:/usr/local/nginx# tail -1 /etc/profile
export PATH=$PATH:/usr/local/nginx/sbin
root@env:/usr/local/nginx# . /etc/profile

## 查看nginx
root@env:/usr/local/nginx# nginx -V
nginx version: nginx/1.24.0
built by gcc 12.2.0 (Debian 12.2.0-14)
built with OpenSSL 3.0.11 19 Sep 2023
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module

## 创建目录
root@env:/usr/local/nginx/conf# mkdir conf.d ssl
root@env:/usr/local/nginx/conf# ls
conf.d fastcgi_params.default mime.types.default scgi_params.default win-utf
fastcgi.conf koi-utf nginx.conf ssl
fastcgi.conf.default koi-win nginx.conf.default uwsgi_params
fastcgi_params mime.types scgi_params uwsgi_params.default

## 主配置文件修改
root@env:/usr/local/nginx/conf# cat nginx.conf
user nginx nginx;

worker_processes auto;
worker_cpu_affinity auto;
worker_rlimit_nofile 65535;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log /dev/null emerg;

pid logs/nginx.pid;

events {
worker_connections 5000;
use epoll;
accept_mutex on;
accept_mutex_delay 500;
multi_accept on;
}

stream {
include conf.d/*.stream;
}

http {
include 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 off;
error_log /dev/null emerg;

sendfile on;
tcp_nopush on;

keepalive_timeout 65;

proxy_buffering on;

gzip on;
include conf.d/*.conf;
}

## 添加为系统服务
root@env:/usr/local/nginx# cat /lib/systemd/system/nginx.service
[Unit]
Description=nginx daemon
After=network.target
After=network-online.target
Wants=network-online.target

[Service]
Type=forking
ExecStartPre=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf -p /usr/local/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf -p /usr/local/nginx
ExecReload=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf -p /usr/local/nginx -s reload
PrivateTmp=true
Restart=always
RestartSec=5
StartLimitInterval=0
LimitNOFILE=65536

[Install]
WantedBy=multi-user.target

## 启动
root@env:/usr/local/nginx/conf# systemctl daemon-reload
root@env:/usr/local/nginx/conf# systemctl start nginx
root@env:/usr/local/nginx/conf# systemctl enable nginx
Created symlink /etc/systemd/system/multi-user.target.wants/nginx.service → /lib/systemd/system/nginx.service.
root@env:/usr/local/nginx/conf# systemctl status nginx
● nginx.service - nginx daemon
Loaded: loaded (/lib/systemd/system/nginx.service; enabled; preset: enabled)
Active: active (running) since Thu 2024-04-04 15:58:10 CST; 14s ago

rainloop配置

  • rainloop参考nginx配置
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    root@env:/usr/local/nginx/conf/conf.d# cat rainloop.conf
    server {
    listen 80;
    server_name rainloop.domain.xyl;
    client_max_body_size 1024m;
    access_log /usr/local/nginx/logs/rainloop_access.log;
    error_log /usr/local/nginx/logs/rainloop_error.log;
    charset utf-8;

    if ($http_user_agent ~* "python|java|curl") {
    return 503;
    }

    root /app/html/rainloop;

    location / {
    index index.php index.html index.htm;
    }

    location ^~ /data {
    deny all;
    }

    location ~ \.php$ {
    root /var/www/html;
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi.conf;
    include fastcgi_params;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }


    }

    ## nginx重载
    root@env:~# nginx -t
    nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
    nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
    root@env:~# nginx -s reload
    root@env:~# netstat -lnpt|grep 80
    tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN 12917/nginx: master

访问http://rainloop.domain.xyl/?admin

  • 客户端添加私有域名解析

    1
    2
    3
    4
    5
    6
    xyl@laptop:~$ nslookup rainloop.domain.xyl
    Server: 192.168.250.3
    Address: 192.168.250.3#53

    Name: rainloop.domain.xyl
    Address: 192.168.250.197
  • 遇到问题:[202] Data folder permissions error [is_writable]
    该问题是文件权限问题,参考官方提示解决https://www.rainloop.net/docs/permissions/

    1
    2
    3
    4
    root@env:/app/html/rainloop# find . -type d -exec chmod 755 {} \;
    root@env:/app/html/rainloop# find . -type f -exec chmod 644 {} \;
    ## 修改为php用户www-data
    root@env:/app/html/rainloop# chown -R www-data:www-data .
  • 默认管理员账户密码:admin/12345,首次登录后更改

  • 登录界面展示

管理界面配置邮箱服务商,所需信息在邮箱服务商网页版查找或操作

  • 添加服务商收发服务器

  • 开启所需服务,会获得授权码,第三方客户端登录时用

使用界面

使用openssl创建域名自签名证书

  • 生成证书
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    ## 创建证书生成目录
    root@env:~# mkdir openssl
    root@env:~# ls
    docker_debian_install.sh nginx-1.24.0.tar.gz
    nginx-1.24.0 openssl
    root@env:~# cd openssl/

    ## ca.key
    root@env:~/openssl# openssl genrsa -out ca.key 2048
    root@env:~/openssl# ls
    ca.key

    ## ca.crt
    root@env:~/openssl# openssl req -new -x509 -sha256 -days 36500 -key ca.key -out ca.crt -subj "/C=CN/ST=ZJ/L=HZ/O=XYL/OU=YW/CN=HF/emailAddress=admin@admin.com"
    root@env:~/openssl# ls
    ca.crt ca.key

    ## server.key和server.csr
    root@env:~/openssl# openssl genrsa -out server.key 2048
    root@env:~/openssl# openssl req -new -key server.key -out server.csr -subj "/C=CN/ST=ZJ/L=HZ/O=XYL/OU=YW/CN=HF/emailAddress=admin@admin.com"

    ## ext.conf
    root@env:~/openssl# cat ext.conf
    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName = @alt_names

    [alt_names]
    DNS.1 = rainloop.domain.xyl

    ## server.crt
    root@env:~/openssl# openssl x509 -req -sha256 -days 3650 -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -extfile ext.conf
    Certificate request self-signature ok
    subject=C = CN, ST = ZJ, L = HZ, O = XYL, OU = YW, CN = HF, emailAddress = admin@admin.com
    root@env:~/openssl# ls
    ca.crt ca.srl server.crt server.key
    ca.key ext.conf server.csr

    ## client类似
    root@env:~/openssl# openssl genrsa -out client.key 2048
    root@env:~/openssl# openssl req -new -out client.csr -key client.key -subj "/C=CN/ST=ZJ/L=HZ/O=XYL/OU=YW/CN=rainloop.domain.xyl/emailAddress=admin@admin.com"
    root@env:~/openssl# openssl x509 -req -in client.csr -out client.crt -CA ca.crt -CAkey ca.key -CAcreateserial -days 3650
    Certificate request self-signature ok
    subject=C = CN, ST = ZJ, L = HZ, O = XYL, OU = YW, CN = rainloop.domain.xyl, emailAddress = admin@admin.com

    ## 验证
    root@env:~/openssl# openssl verify -CAfile ca.crt client.crt
    client.crt: OK
    root@env:~/openssl# openssl verify -CAfile ca.crt server.crt
    server.crt: OK

    ## 将客户端转为浏览器支持的格式
    root@env:~/openssl# openssl pkcs12 -export -clcerts -in client.crt -inkey client.key -out rainloop_client.p12
    Warning: -clcerts option ignored with -export
    Enter Export Password:
    Verifying - Enter Export Password:
    root@env:~/openssl#
    root@env:~/openssl# ls
    ca.crt ca.srl client.csr ext.conf server.crt server.key
    ca.key client.crt client.key rainloop_client.p12 server.csr
  • nginx服务端配置证书
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    ## 证书复制到nginx证书目录
    root@env:~/openssl# cp -a server.key /usr/local/nginx/conf/ssl/
    root@env:~/openssl# cp -a server.crt /usr/local/nginx/conf/ssl/
    root@env:~/openssl# cp -a ca.crt /usr/local/nginx/conf/ssl/
    root@env:~/openssl# cd /usr/local/nginx/conf/ssl
    root@env:/usr/local/nginx/conf/ssl# ls
    ca.crt server.crt server.key
    ## nginx.conf
    root@env:/usr/local/nginx/conf/conf.d# cat rainloop.conf
    server {
    listen 80;
    server_name rainloop.domain.xyl;
    return 301 https://$server_name$request_uri;
    }

    server {
    listen 443 ssl;
    server_name rainloop.domain.xyl;
    client_max_body_size 1024m;
    access_log /usr/local/nginx/logs/rainloop_access.log;
    error_log /usr/local/nginx/logs/rainloop_error.log;

    ##证书
    ssl_certificate ssl/server.crt;
    ssl_certificate_key ssl/server.key;

    ##相关参数
    ssl_session_timeout 10m;
    ssl_session_cache none;
    ssl_protocols TLSv1.2 TLSv1.3;

    ssl_prefer_server_ciphers on;
    ssl_ciphers 'TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES256-GCM-SHA512:DHE-RSA-AES256-GCM-SHA512:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES256-GCM-SHA384';


    ## 客户端认证
    ssl_client_certificate ssl/ca.crt;
    ssl_verify_client on;

    charset utf-8;


    if ($http_user_agent ~* "python|java|curl") {
    return 503;
    }

    root /app/html/rainloop;

    location / {
    index index.php index.html index.htm;
    }

    location ^~ /data {
    deny all;
    }

    location ~ \.php$ {
    fastcgi_pass 127.0.0.1:9000;
    fastcgi_index index.php;
    include fastcgi.conf;
    include fastcgi_params;
    root /var/www/html;
    fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }


    }
  • 重启后浏览器未导入客户端证书访问失败
  • 导入证书和可信颁发机构可https加密正常访问