使用Docker部署SnapDrop,实现内网浏览器传输文件

1. SnapDrop介绍

SnapDrop是一个基于WebRTC技术的开源文件传输工具,允许用户通过浏览器在同一 WiFi网络下的设备间实现点对点(P2P)文件传输,无需安装软件或注册账号。

SnapDrop 体验站: https://share.lololowe.com/

2. 安装Docker

参考: https://mirrors.ustc.edu.cn/help/docker-ce.html

2.1. 自动安装

使用中科大镜像源进行自动安装:

1
2
curl -fsSL https://get.docker.com -o get-docker.sh
sudo DOWNLOAD_URL=https://mirrors.ustc.edu.cn/docker-ce sh get-docker.sh

2.2. 手动安装

如果自动安装失败,则建议手动从镜像站下载安装包进行安装,Debian11(bullseye)的下载及安装命令如下:

1
2
3
4
5
6
7
8
9
10
11
12
# 创建临时安装目录
mkdir /tmp/docker && cd /tmp/docker
# 下载
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/containerd.io_1.7.27-1_amd64.deb
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/docker-buildx-plugin_0.22.0-1~debian.12~bullseye_amd64.deb
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/docker-ce-cli_28.0.4-1~debian.12~bullseye_amd64.deb
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/docker-ce-rootless-extras_28.0.4-1~debian.12~bullseye_amd64.deb
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/docker-ce_28.0.4-1~debian.12~bullseye_amd64.deb
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/docker-compose-plugin_2.34.0-1~debian.12~bullseye_amd64.deb
wget https://mirrors.ustc.edu.cn/docker-ce/linux/debian/dists/bullseye/pool/stable/amd64/docker-scan-plugin_0.23.0~debian-bullseye_amd64.deb
# 安装
apt install -y ./*.deb

2.3. 验证安装

1
2
3
4
5
6
# 设置开机自启并立即启动docker服务
systemctl enable docker --now
# 查看docker运行状态
systemctl status docker
# 查看client和server版本
docker info

2.4. 更换镜像源

参考: https://www.coderjia.cn/archives/dba3f94c-a021-468a-8ac6-e840f85867ea

使用以下命令来更换Docker Hub的镜像源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 创建目录
sudo mkdir -p /etc/docker
# 写入配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://docker-0.unsee.tech",
"https://docker-cf.registry.cyou",
"https://docker.1panel.live"
]
}
EOF
# 重启docker服务
sudo systemctl daemon-reload && sudo systemctl restart docker

查看镜像源是否配置成功:

1
2
3
4
5
6
❯ docker info | tail -n 6
Registry Mirrors:
https://docker-0.unsee.tech/
https://docker-cf.registry.cyou/
https://docker.1panel.live/
Live Restore Enabled: false

3. 部署SnapDrop

3.1. 服务部署

克隆源码:

1
2
3
git clone https://github.com/SnapDrop/snapdrop.git
cd snapdrop
chown -R 1000:1000 server # 设置权限,防止容器无法读取挂载卷(1000是容器内的node用户)

我的 docker-compose.yml 配置如下:

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
version: "3"
services:
node:
image: "node:lts-alpine"
restart: unless-stopped # 除非手动停止,否则自动重启
user: "node"
working_dir: /home/node/app
volumes:
- ./server/:/home/node/app
command: ash -c "npm i && node index.js"
nginx:
build:
context: ./docker/
dockerfile: nginx-with-openssl.Dockerfile
image: "nginx-with-openssl"
restart: unless-stopped
volumes:
- ./client:/usr/share/nginx/html
- ./docker/nginx/default.conf:/etc/nginx/conf.d/default.conf
- ./docker/certs:/etc/ssl/certs
- ./docker/openssl:/mnt/openssl
ports:
- "127.0.0.1:8101:80" # 绑定物理机环回口的8101端口到容器80端口
- "127.0.0.1:8102:443"
env_file: ./docker/fqdn.env
entrypoint: /mnt/openssl/create.sh
command: ["nginx", "-g", "daemon off;"]

启动服务:

1
2
3
docker compose up -d  # 后台启动容器编排
curl -I http://127.0.0.1:8101 # 检查http是否可以访问
curl -I -k https://127.0.0.1:8102 # 检查https是否可以访问

浏览器访问127.0.0.1的8101或者8102端口即可看到SnapDrop的页面。

如果启动时出现以下报错,提示nginx镜像编译失败,原因是alpine软件源中没有找到openssl:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
❯ docker compose up -d
WARN[0000] /root/Application/snapdrop/docker-compose.yml: the attribute `version` is obsolete, it will be ignored, please remove it to avoid potential confusion
[+] Running 1/1
! nginx Warning Get "https://registry-1.docker.io/v2/": net/http: request canceled while waiting for connec... 24.4s
[+] Building 5.4s (5/5) FINISHED docker:default
=> [nginx internal] load build definition from nginx-with-openssl.Dockerfile 0.0s
=> => transferring dockerfile: 105B 0.0s
=> [nginx internal] load metadata for docker.io/library/nginx:alpine 2.7s
=> [nginx internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> CACHED [nginx 1/2] FROM docker.io/library/nginx:alpine@sha256:4ff102c5d78d254a6f0da062b3cf39eaf07f01eec0927fd21e219d0af8b 0.0s
=> ERROR [nginx 2/2] RUN apk add --no-cache openssl 2.6s
------
> [nginx 2/2] RUN apk add --no-cache openssl:
0.478 fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/main/x86_64/APKINDEX.tar.gz
1.479 WARNING: fetching https://dl-cdn.alpinelinux.org/alpine/v3.21/main: temporary error (try again later)
1.480 fetch https://dl-cdn.alpinelinux.org/alpine/v3.21/community/x86_64/APKINDEX.tar.gz
2.481 WARNING: fetching https://dl-cdn.alpinelinux.org/alpine/v3.21/community: temporary error (try again later)
2.483 ERROR: unable to select packages:
2.483 openssl (no such package):
2.483 required by: world[openssl]
------
failed to solve: process "/bin/sh -c apk add --no-cache openssl" did not complete successfully: exit code: 1

那么建议手动拉取nginx镜像并自行添加openssl模块:

1
2
3
4
5
6
7
# 拉取nginx镜像
docker pull nginx:alpine

# 自定义构建带openssl的镜像(使用Dockerfile)
echo "FROM nginx:alpine
RUN apk add --no-cache openssl" > Dockerfile
docker build -t nginx-with-openssl .

完成后再运行 docker compose up -d 即可。

注意: 这里提到的nginx是docker容器以及镜像,不是物理机上的nginx。

3.2. Nginx反向代理

在上一个步骤中,通过 docker-compose.yml 文件把nginx容器的80和443端口映射到了物理机的127.0.0.1:8101和127.0.0.1:8102,这样可以避免docker不遵守ufw防火墙规则而降低安全性,但是也会导致本机以外的用户无法访问SnapDrop服务,因此需要通过Nginx反向代理来解决这个问题。

我的nginx server块(/etc/nginx/conf.d/snapdrop.conf)配置如下:

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
server {
listen 80;
server_name share.lololowe.com;

if ($http_Host !~* ^(share.lololowe.com) ) {
return 444;
}

return 301 https://$host$request_uri;
}

server{
listen 443 ssl;
server_name share.lololowe.com;

if ($http_Host !~* ^(share.lololowe.com) ) {
return 444;
}

ssl_certificate <替换成公钥文件路径>;
ssl_certificate_key <替换成私钥文件路径>;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;

access_log /var/log/nginx/snapdrop/access.log main;
error_log /var/log/nginx/snapdrop/error.log warn;

location / {
proxy_pass http://127.0.0.1:8101; # 反向代理到SnapDrop服务
proxy_connect_timeout 300;
proxy_set_header Connection "upgrade";
proxy_set_header Upgrade $http_upgrade;
proxy_set_header X-Forwarded-for $remote_addr;
}
}

按实际情况修改域名、证书路径信息,然后重启nginx服务即可:

1
2
nginx -t  # 检查配置文件是否正确
nginx -s reload # 重启nginx服务

3.3. 汉化

在源码的client目录下执行以下命令,将SnapDrop页面汉化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
cp index.html index.html.bak
sed -i 's/Open Snapdrop on other devices to send files/请在局域网其他设备上打开 Snapdrop 以传输文件/g' index.html
sed -i 's/Click to send files or right click to send a message/点击发送文件或右键(长按)发送消息/g' index.html
sed -i 's/Tap to send files or long tap to send a message/点击发送文件或长按发送消息/g' index.html
sed -i 's/You can be discovered by everyone on this network/您可以被内网中的每个人发现/g' index.html
sed -i 's/Ask to save each file before downloading/下载每一个文件前请先询问我/g' index.html
sed -i 's/Save/保存/g' index.html
sed -i 's/Ignore/忽略/g' index.html
sed -i 's/Send a Message/发送消息/g' index.html
sed -i 's#Send</button>#发送</button>#g' index.html
sed -i 's/Cancel/取消/g' index.html
sed -i 's/You are known as /您的昵称是: /g' scripts/ui.js
sed -i 's/Message Received/收到消息/g' index.html
sed -i 's/Copy/复制/g' index.html
sed -i 's/Close/关闭/g' index.html
sed -i 's/File Transfer Completed/文件传输完成/g' index.html
sed -i 's#File transfer completed.#文件传输完成#g' scripts/network.js
sed -i 's/The easiest way to transfer files across devices/以最简单的方式在您的各个设备间传输数据/g' index.html
sed -i 's#<h3>Send a Message</h3>#发送一条消息#g' index.html
sed -i 's/placeholder="Send a message"/placeholder="发送一条消息"/g' index.html

alt text

3.4. 悬挂备案号

替换index.html文件中的Footer部分,将备案号挂载到页脚:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<!-- Footer -->
<footer class="column">
<svg class="icon logo">
<use xlink:href="#wifi-tethering" />
</svg>
<div id="displayName" placeholder="The easiest way to transfer data across devices"></div>
<div class="font-body2">您可以被内网中的每个人发现</div>

<div class="font-body2" style="margin-top: 15px;">
<a href="https://beian.miit.gov.cn/" target="_blank" rel="noopener noreferrer"
style="color: #666; text-decoration: none;">
湘ICP备XXXX号-1
</a>
</div>
</footer>