[refactor] use fixed paths for data storage and add automatic certificate generation

This commit is contained in:
dijunkun
2025-12-08 13:57:49 +08:00
parent dcb07daadf
commit 799d1a31d5
7 changed files with 200 additions and 24 deletions

1
.gitignore vendored
View File

@@ -8,4 +8,5 @@ cert/
# VSCode cache
.vscode
.vs
continuous-desk-server.code-workspace

View File

@@ -43,4 +43,44 @@ xmake b -vy crossdesk_server
cd docker
sudo docker build -t image-name .
```
```
## 运行容器
### 基础启动(数据存储在容器内)
启动命令示例:
```bash
docker run -d \
--name crossdesk_server \
--network host \
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \
-e INTERNAL_IP=xxx.xxx.xxx.xxx \
-e CROSSDESK_SERVER_PORT=xxxx \
-e COTURN_PORT=xxxx \
-e MIN_PORT=xxxxx \
-e MAX_PORT=xxxxx \
-v /var/lib/crossdesk:/var/lib/crossdesk \
-v /var/log/crossdesk:/var/log/crossdesk \
crossdesk/crossdesk-server:v1.1.2
```
**说明**
- 证书文件会在首次启动时自动生成在容器内的 `/var/lib/crossdesk/certs`
- 数据库文件会自动创建在容器内的 `/var/lib/crossdesk/db/crossdesk-server.db`
- 日志文件会自动创建在容器内的 `/var/log/crossdesk/`
- **注意**:如果不挂载 volume容器删除后数据会丢失
- `-v /var/lib/crossdesk:/var/lib/crossdesk`:持久化数据库和证书文件到宿主机
- `-v /var/log/crossdesk:/var/log/crossdesk`:持久化日志文件到宿主机
- 容器删除后,数据仍保留在宿主机上
- **目录自动创建**如果宿主机没有这些目录Docker 会自动创建,容器内的代码也会自动创建子目录
- **权限注意(重要)**:如果 Docker 自动创建的目录权限不足(属于 root容器内用户无法写入会导致
- 证书生成失败,容器启动脚本会报错退出
- 数据库目录创建失败,程序会抛出异常并崩溃
- 日志目录创建失败,日志文件无法写入(但程序可能继续运行)
解决方案:在启动容器前手动设置权限:
```bash
sudo mkdir -p /var/lib/crossdesk /var/log/crossdesk
sudo chown -R $(id -u):$(id -g) /var/lib/crossdesk /var/log/crossdesk
```

View File

@@ -41,4 +41,44 @@ For more information, please refer to the [official Xmake documentation](https:/
cd docker
sudo docker build -t image-name .
```
```
## Run Container
### Basic Startup (Data stored in container)
Example startup command:
```bash
docker run -d \
--name crossdesk_server \
--network host \
-e EXTERNAL_IP=xxx.xxx.xxx.xxx \
-e INTERNAL_IP=xxx.xxx.xxx.xxx \
-e CROSSDESK_SERVER_PORT=xxxx \
-e COTURN_PORT=xxxx \
-e MIN_PORT=xxxxx \
-e MAX_PORT=xxxxx \
-v /var/lib/crossdesk:/var/lib/crossdesk \
-v /var/log/crossdesk:/var/log/crossdesk \
crossdesk/crossdesk-server:v1.1.2
```
**Notes**:
- Certificate files will be automatically generated on first startup in `/var/lib/crossdesk/certs` (inside container)
- Database file will be automatically created at `/var/lib/crossdesk/db/crossdesk-server.db` (inside container)
- Log files will be automatically created in `/var/log/crossdesk/` (inside container)
- **Note**: Data will be lost if container is removed without volume mounts
- `-v /var/lib/crossdesk:/var/lib/crossdesk`: Persist database and certificate files to host
- `-v /var/log/crossdesk:/var/log/crossdesk`: Persist log files to host
- Data will remain on host even if container is removed
- **Auto directory creation**: If these directories don't exist on host, Docker will create them automatically, and the container code will also create subdirectories
- **Permission note (Important)**: If Docker auto-created directories have insufficient permissions (owned by root), the container user cannot write, which will cause:
- Certificate generation failure, container startup script will exit with error
- Database directory creation failure, program will throw exception and crash
- Log directory creation failure, log files cannot be written (but program may continue running)
Solution: Set permissions manually before starting container:
```bash
sudo mkdir -p /var/lib/crossdesk /var/log/crossdesk
sudo chown -R $(id -u):$(id -g) /var/lib/crossdesk /var/log/crossdesk
```

View File

@@ -29,8 +29,9 @@ RUN mkdir -p /opt/turnserver && \
COPY docker/start.sh /start.sh
COPY docker/generate_certs.sh /docker/generate_certs.sh
COPY --from=builder /output/crossdesk_server /crossdesk-server/crossdesk_server
RUN chmod +x /start.sh /crossdesk-server/crossdesk_server
RUN chmod +x /start.sh /docker/generate_certs.sh /crossdesk-server/crossdesk_server
ENTRYPOINT ["/start.sh"]

82
docker/generate_certs.sh Normal file
View File

@@ -0,0 +1,82 @@
#!/bin/bash
set -e
# 检查参数
if [ "$#" -lt 1 ] || [ "$#" -gt 2 ]; then
echo "Usage: $0 <SERVER_IP> [OUTPUT_DIR]"
echo " SERVER_IP: IP address for the certificate"
echo " OUTPUT_DIR: Directory to save certificates (default: current directory)"
exit 1
fi
SERVER_IP="$1"
OUTPUT_DIR="${2:-$(pwd)}"
# 确保输出目录存在
mkdir -p "$OUTPUT_DIR"
# 切换到输出目录
cd "$OUTPUT_DIR"
# 文件名(使用完整路径)
ROOT_KEY="$OUTPUT_DIR/crossdesk.cn_root.key"
ROOT_CERT="$OUTPUT_DIR/crossdesk.cn_root.crt"
SERVER_KEY="$OUTPUT_DIR/crossdesk.cn.key"
SERVER_CSR="$OUTPUT_DIR/crossdesk.cn.csr"
SERVER_CERT="$OUTPUT_DIR/crossdesk.cn_bundle.crt"
FULLCHAIN_CERT="$OUTPUT_DIR/crossdesk.cn_fullchain.crt"
SAN_CONF="$OUTPUT_DIR/san.cnf"
# 证书主题
SUBJ="/C=CN/ST=Zhejiang/L=Hangzhou/O=CrossDesk/OU=CrossDesk/CN=$SERVER_IP"
# 1. 生成根证书
echo "Generating root private key..."
openssl genrsa -out "$ROOT_KEY" 4096
echo "Generating self-signed root certificate..."
openssl req -x509 -new -nodes -key "$ROOT_KEY" -sha256 -days 3650 -out "$ROOT_CERT" -subj "$SUBJ"
# 2. 生成服务器私钥
echo "Generating server private key..."
openssl genrsa -out "$SERVER_KEY" 2048
# 3. 生成服务器 CSR
echo "Generating server CSR..."
openssl req -new -key "$SERVER_KEY" -out "$SERVER_CSR" -subj "$SUBJ"
# 4. 生成临时 OpenSSL 配置文件,加入 SAN
cat > "$SAN_CONF" <<EOL
[ req ]
default_bits = 2048
distinguished_name = req_distinguished_name
req_extensions = req_ext
prompt = no
[ req_distinguished_name ]
C = CN
ST = Zhejiang
L = Hangzhou
O = CrossDesk
OU = CrossDesk
CN = $SERVER_IP
[ req_ext ]
subjectAltName = IP:$SERVER_IP
EOL
# 5. 用根证书签发服务器证书(包含 SAN
echo "Signing server certificate with root certificate..."
openssl x509 -req -in "$SERVER_CSR" -CA "$ROOT_CERT" -CAkey "$ROOT_KEY" -CAcreateserial \
-out "$SERVER_CERT" -days 3650 -sha256 -extfile "$SAN_CONF" -extensions req_ext
# 6. 生成完整链证书
cat "$SERVER_CERT" "$ROOT_CERT" > "$FULLCHAIN_CERT"
# 7. 清理中间文件
rm -f "$ROOT_CERT.srl" "$SAN_CONF" "$ROOT_KEY" "$SERVER_CSR" "$FULLCHAIN_CERT"
echo "Generation complete. Certificates saved to: $OUTPUT_DIR"
echo " Client root certificate: $ROOT_CERT"
echo " Server private key: $SERVER_KEY"
echo " Server certificate: $SERVER_CERT"

View File

@@ -28,6 +28,30 @@ if [ -z "$MIN_PORT" ] || [ -z "$MAX_PORT" ]; then
exit 1
fi
# check and generate certificates if needed
CERT_DIR="/var/lib/crossdesk/certs"
CERT_KEY="$CERT_DIR/crossdesk.cn.key"
CERT_BUNDLE="$CERT_DIR/crossdesk.cn_bundle.crt"
CERT_ROOT="$CERT_DIR/crossdesk.cn_root.crt"
if [ ! -f "$CERT_KEY" ] || [ ! -f "$CERT_BUNDLE" ] || [ ! -f "$CERT_ROOT" ]; then
echo "Certificate files not found, generating certificates..."
mkdir -p "$CERT_DIR"
# Run generate_certs.sh with EXTERNAL_IP and output directory
bash /docker/generate_certs.sh "$EXTERNAL_IP" "$CERT_DIR"
# Verify certificates were generated
if [ ! -f "$CERT_KEY" ] || [ ! -f "$CERT_BUNDLE" ] || [ ! -f "$CERT_ROOT" ]; then
echo "Error: Failed to generate certificate files"
exit 1
fi
echo "Certificates generated successfully"
else
echo "Certificate files found, skipping generation"
fi
# generate coturn configuration file
mkdir -p /etc/coturn
cat > "$CONF_FILE" <<EOF
@@ -44,7 +68,7 @@ user=crossdesk:crossdeskpw
realm=crossdesk
cert=${CERT_FILE}
pkey=${PKEY_FILE}
log-file=/crossdesk-server/logs/turn.log
log-file=/var/log/crossdesk/turn.log
no-cli
EOF
@@ -56,8 +80,8 @@ exec turnserver -c "$CONF_FILE" &
# start crossdesk-server as main foreground process
echo "Starting crossdesk-server..."
./crossdesk-server/crossdesk_server \
${CROSSDESK_SERVER_PORT} \
/crossdesk-server/certs/ \
/crossdesk-server/db/crossdesk_server.db \
/crossdesk-server/logs
# 程序现在使用固定目录:
# - 数据库和配置文件:/var/lib/crossdesk
# - 日志文件:/var/log/crossdesk
# 只需传递端口参数
./crossdesk-server/crossdesk_server ${CROSSDESK_SERVER_PORT}

View File

@@ -14,26 +14,14 @@
int main(int argc, char* argv[]) {
std::string port = "9090";
std::string log_dir = "./logs";
std::string certs_dir = "./cert";
std::string db_path = "devices.db";
std::string log_dir = "/var/log/crossdesk";
std::string certs_dir = "/var/lib/crossdesk/certs";
std::string db_path = "/var/lib/crossdesk/db/crossdesk-server.db";
if (argc > 1) {
port = argv[1];
}
if (argc > 2) {
certs_dir = argv[2];
}
if (argc > 3) {
db_path = argv[3];
}
if (argc > 4) {
log_dir = argv[4];
}
InitLogger(log_dir);
SignalServer s(std::stoi(port), certs_dir, db_path);