开源网页邮箱客户端rainloop实践
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 | ## 下载源码 |
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
43root@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
6xyl@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
4root@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加密正常访问

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 xyl的blog!
评论


