MoreRSS

site iconLala | 荒岛修改

一个应用分享、教程类的博客,主要是那些需要自部署的。
请复制 RSS 到你的阅读器,或快速订阅到 :

Inoreader Feedly Follow Feedbin Local Reader

Lala | 荒岛的 RSS 预览

La Suite Drive:开源协作文件共享程序

2026-02-06 13:29:36

La Suite Drive是法国政府主导的又一个开源网盘程序。特点如下:

  • 🔐 将您的文件安全地存储在集中位置
  • 🌐 通过我们的网页界面,随时随地访问您的文件
  • 🔍 强大的搜索功能,可快速查找文件和文件夹
  • 📂 文件结构清晰有序,导航和筛选功能直观易用
  • 🤝 与团队成员共享文件和文件夹
  • 👥 精细化的访问控制,确保您的信息安全,且只与授权人员共享
  • 🏢 创建工作区以组织团队协作和管理共享资源
  • 🚀 易于安装、可扩展且安全的文件存储解决方案

为什么把易于安装给划掉?因为这个项目我认为就目前而言是非常难以安装的,官方没有提供生产部署的文档,且部署步骤太多太杂了。。我认为这与官方的宣传不符,所以给划掉了= =

这里我为了尝鲜体验一番,记录一下用docker compose部署的方式。如果后续官方写了部署文档,请以官方的文档为准。这个项目目前应该还是Beta状态,如果官方后续对项目做了比较大的改动,不保证这篇文章的部署方法一直有效。

准备工作:

1.一台Debian服务器,内存不低于4GB,放行80、443、9199、9299、9980端口。

2.添加域名A记录解析,本文示例域名:drive.example.com、onlyoffice.example.com、collabora.example.com。

3.部署RustFS对象存储服务,或Garage S3对象存储服务。推荐RustFS,因为Drive的文档编辑功能需要S3支持“版本控制”的功能,Garage S3缺少这个功能将导致文档无法保存。本文S3示例域名:rustfs.example.com

4.部署VoidAuth OIDC身份验证服务。本文示例域名:voidauth.example.com

在VoidAuth创建OIDC APP:

设置Redirect URLs:

https://drive.example.com/api/v1.0/callback/

设置PostLogout URL:

https://drive.example.com/api/v1.0/logout-callback/

在RustFS控制台创建存储桶,并启用版本控制:

做好上面的准备工作后,现在可以正式部署Drive了,在服务器内安装Docker:

apt -y update
apt -y install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

新建compose文件:

mkdir -p /opt/drive && cd /opt/drive && nano docker-compose.yml

写入如下内容,需要修改的地方写了注释:

x-common-env: &common-env
  BACKEND_HOST: backend
  FRONTEND_HOST: frontend
  # Django
  DJANGO_ALLOWED_HOSTS: "*"
  DJANGO_SECRET_KEY: 7cafccee9288097a2b3eb64305b0d07bff6682bc50a2b3f99a9696ef2e000fab # 使用openssl rand -hex 32生成
  DJANGO_SETTINGS_MODULE: drive.settings
  DJANGO_CONFIGURATION: Production
  # Logging
  LOGGING_LEVEL_HANDLERS_CONSOLE: ERROR
  LOGGING_LEVEL_LOGGERS_ROOT: INFO
  LOGGING_LEVEL_LOGGERS_APP: INFO
  # Python
  PYTHONPATH: /app
  # Mail
  DJANGO_EMAIL_HOST: mail.example.com
  DJANGO_EMAIL_HOST_USER: smtp
  DJANGO_EMAIL_HOST_PASSWORD: smtppassword
  DJANGO_EMAIL_PORT: 587
  DJANGO_EMAIL_FROM: [email protected]
  DJANGO_EMAIL_USE_TLS: true
  DJANGO_EMAIL_BRAND_NAME: "La Suite Numérique"
  DJANGO_EMAIL_LOGO_IMG: "https://drive.example.com/assets/logo-suite-numerique.png"
  # Media S3
  STORAGES_STATICFILES_BACKEND: django.contrib.staticfiles.storage.StaticFilesStorage
  AWS_S3_ACCESS_KEY_ID: imlala
  AWS_S3_SECRET_ACCESS_KEY: s3secretaccesskey
  AWS_S3_REGION_NAME: auto
  AWS_STORAGE_BUCKET_NAME: drive-media-storage
  AWS_S3_SIGNATURE_VERSION: s3v4
  AWS_S3_ENDPOINT_URL: https://rustfs.example.com
  MEDIA_BASE_URL: https://drive.example.com
  # OIDC
  OIDC_OP_JWKS_ENDPOINT: https://voidauth.example.com/oidc/jwks
  OIDC_OP_AUTHORIZATION_ENDPOINT: https://voidauth.example.com/oidc/auth
  OIDC_OP_TOKEN_ENDPOINT: https://voidauth.example.com/oidc/token
  OIDC_OP_USER_ENDPOINT: https://voidauth.example.com/oidc/me
  OIDC_OP_LOGOUT_ENDPOINT: https://voidauth.example.com/oidc/session/end
  OIDC_RP_CLIENT_ID: pcl1xbprEdcbi0xQ
  OIDC_RP_CLIENT_SECRET: youroidcsecret
  OIDC_RP_SIGN_ALGO: RS256
  OIDC_RP_SCOPES: "openid email"
  LOGIN_REDIRECT_URL: https://drive.example.com
  LOGIN_REDIRECT_URL_FAILURE: https://drive.example.com
  LOGOUT_REDIRECT_URL: https://drive.example.com
  OIDC_REDIRECT_ALLOWED_HOSTS: '["https://drive.example.com"]'
  # WOPI
  WOPI_CLIENTS: "collabora,onlyoffice"
  WOPI_COLLABORA_DISCOVERY_URL: https://collabora.example.com/hosting/discovery
  WOPI_ONLYOFFICE_DISCOVERY_URL: https://onlyoffice.example.com/hosting/discovery
  WOPI_SRC_BASE_URL: https://drive.example.com

x-postgres-env: &postgres-env
  # Postgresql db container configuration
  POSTGRES_DB: drive
  POSTGRES_USER: drive
  POSTGRES_PASSWORD: 023917148a35e7c9a62964caabe0334c # 设置数据库密码
  # App database configuration
  DB_HOST: postgresql
  DB_NAME: drive
  DB_USER: drive
  DB_PASSWORD: 023917148a35e7c9a62964caabe0334c # 设置数据库密码
  DB_PORT: 5432

services:
  postgresql:
    image: postgres:16
    restart: unless-stopped
    environment:
      

注意事项:

如果你只想使用一个办公套件,例如onlyoffice,那么请修改如下变量:

WOPI_CLIENTS: "onlyoffice"
#WOPI_COLLABORA_DISCOVERY_URL: https://collabora.example.com/hosting/discovery #注释掉这个变量

Drive的WOPI在默认情况下会使用onlyoffice,如果你不需要collabora code可以不部署,并且onlyoffice的使用体验比collabora code好的多。

新建前端容器需要用到的NGINX配置文件:

nano default.conf.template

写入如下内容:

upstream docs_backend {
    server ${BACKEND_HOST}:8000 fail_timeout=0;
}

upstream docs_frontend {
    server ${FRONTEND_HOST}:3000 fail_timeout=0;
}

server {
    listen 8083;
    server_name localhost;
    charset utf-8;
    # increase max upload size
    client_max_body_size 5120m;
    server_tokens off;
    proxy_ssl_server_name on;

    location @proxy_to_docs_backend {
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_redirect off;
        proxy_pass http://docs_backend;
    }

    location @proxy_to_docs_frontend {
        proxy_set_header Host $http_host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_redirect off;
        proxy_pass http://docs_frontend;
    }

    location / {
        try_files $uri @proxy_to_docs_frontend;
    }

    location /api {
        try_files $uri @proxy_to_docs_backend;
    }

    location /admin {
        try_files $uri @proxy_to_docs_backend;
    }

    location /media/ {
        # Auth request configuration
        auth_request /media-auth;
        auth_request_set $authHeader $upstream_http_authorization;
        auth_request_set $authDate $upstream_http_x_amz_date;
        auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256;
        # Pass specific headers from the auth response
        proxy_set_header Authorization $authHeader;
        proxy_set_header X-Amz-Date $authDate;
        proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
        proxy_pass https://rustfs.example.com/drive-media-storage/;
        proxy_set_header Host rustfs.example.com;
        proxy_ssl_name rustfs.example.com;
    }

    # Proxy auth for media-preview
    location /media/preview/ {
        # Auth request configuration
        auth_request /media-auth;
        auth_request_set $authHeader $upstream_http_authorization;
        auth_request_set $authDate $upstream_http_x_amz_date;
        auth_request_set $authContentSha256 $upstream_http_x_amz_content_sha256;
        # Pass specific headers from the auth response
        proxy_set_header Authorization $authHeader;
        proxy_set_header X-Amz-Date $authDate;
        proxy_set_header X-Amz-Content-SHA256 $authContentSha256;
        proxy_pass https://rustfs.example.com/drive-media-storage/;
        proxy_set_header Host rustfs.example.com;
        proxy_ssl_name rustfs.example.com;
    }

    location /media-auth {
        proxy_pass http://docs_backend/api/v1.0/items/media-auth/;
        proxy_set_header X-Forwarded-Proto https;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Original-URL $request_uri;
        # Prevent the body from being passed
        proxy_pass_request_body off;
        proxy_set_header Content-Length "";
        proxy_set_header X-Original-Method $request_method;
    }
}

注意将location /media/location /media/preview/内的域名改为自己的S3域名:

proxy_pass https://rustfs.example.com/drive-media-storage/;
proxy_set_header Host rustfs.example.com;
proxy_ssl_name rustfs.example.com;

启动全部容器:

docker compose up -d

运行数据库迁移、创建Django管理员账号:

docker compose run --rm backend python manage.py migrate
docker compose run --rm backend python manage.py createsuperuser --email [email protected] --password youradminpassword

运行如下命令启用WOPI客户端:

docker compose run --rm backend python manage.py trigger_wopi_configuration

这应该是一个BUG,按道理来说celery会自动执行,但是不知道为什么没生效。如果不执行这个命令将无法使用文档编辑功能。

配置Ferron反向代理:

nano /etc/ferron.kdl

写入如下内容:

drive.example.com {
   proxy "http://127.0.0.1:9199/"
   proxy_request_header_replace "Host" "{header:Host}"
}

onlyoffice.example.com {
   proxy "http://127.0.0.1:9299/"
   proxy_request_header_replace "Host" "{header:Host}"
}

collabora.example.com {
   proxy "http://127.0.0.1:9980/"
   proxy_request_header_replace "Host" "{header:Host}"
   disable_url_sanitizer #true
}

重载Ferron:

systemctl reload ferron

如遇到Ferron反向代理导致上传速度慢,可以参考这篇文章解决。

所有服务将在以下URL提供访问:

https://drive.example.com
https://onlyoffice.example.com/admin
https://collabora.example.com/browser/dist/admin/admin.html

效果:

总结一下部署过程中遇到的问题。

1.由于官方的环境变量配置文件不完整,导致部署非常困难,甚至有些变量还要翻源码才能知道是什么作用= =

2.前端容器必须配置入口点启动脚本,否则无法使用挂载的自定义NGINX配置文件。

3.官方提供的NGINX配置文件缺少对/media/preview/的路由,导致上传的文件无法预览。

4.Ferron反向代理collabora code遇到WebSocket连接失败。原因是Ferron默认会过滤URL防止路径遍历等安全漏洞,collabora code的WebSocket连接URL里面包含https://被Ferron过滤成https:/了,导致后端报错Bad URL。解决办法是关闭Ferron的URL过滤:disable_url_sanitizer #true

5.S3实现不支持版本控制,导致文档无法保存,这个之前已经提过了。

6.S3跨域问题,因为Drive是直接操作S3,没有经过后端,所以S3这边要设置跨域规则。

7.目前需要手动执行python manage.py trigger_wopi_configuration来集成onlyoffice等办公套件,这是一个BUG:见此issue

总之就是这个项目完成度还是非常高的,基本功能都有了,要我说就是还差个文件缩略图和密码分享的功能。但是有一说一部署确实太多坑了,建议再观望一下= =

Docker部署单节点RustFS对象存储

2026-02-05 21:50:50

由于Garage S3没有版本控制的功能,最近部署的几个程序都需要用到这个功能,所以部署一个RustFS当作备用。

还有一个重要原因是,RustFS可谓是历经千辛万苦,终于把这个BUG给修好了= =

RustFS和MinIO在部署和使用方面都非常相似,如果你用过MinIO,那么上手RustFS会很容易,比Garage简单的多~

准备工作:

  • 一个域名,添加A记录解析。本文示例:rustfs.example.com、rustfs-console.example.com
  • 如需要支持虚拟主机风格的存储桶,则还需要添加一个通配符域名解析记录:*.rustfs.example.com
  • 一台Debian服务器,开放80、443、9000、9001端口

安装Docker:

apt -y update
apt -y install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

新建compose文件:

mkdir -p /opt/rustfs && cd /opt/rustfs && nano docker-compose.yml

写入如下内容:

services:
  rustfs:
    image: rustfs/rustfs:latest
    container_name: rustfs-server
    restart: unless-stopped
    security_opt:
      - "no-new-privileges:true"
    ports:
      - "127.0.0.1:9000:9000" # S3 API 
      - "127.0.0.1:9001:9001" # 控制台
    environment:
      - RUSTFS_SERVER_DOMAINS=rustfs.example.com
      - RUSTFS_VOLUMES=/data/rustfs0
      - RUSTFS_ADDRESS=0.0.0.0:9000
      - RUSTFS_CONSOLE_ADDRESS=0.0.0.0:9001
      - RUSTFS_CONSOLE_ENABLE=true
      - RUSTFS_CORS_ALLOWED_ORIGINS=*
      - RUSTFS_CONSOLE_CORS_ALLOWED_ORIGINS=*
      - RUSTFS_ACCESS_KEY=imlala
      - RUSTFS_SECRET_KEY=setyoursecretkey
      - RUSTFS_REGION=auto
      - RUSTFS_OBS_LOGGER_LEVEL=info
      - RUSTFS_OBS_LOG_DIRECTORY=/app/logs
    volumes:
      - ./data:/data
      - ./logs:/app/logs
    healthcheck:
      test: ["CMD", "sh", "-c", "curl -f http://localhost:9000/health && curl -f http://localhost:9001/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

启动:

docker compose up -d

配置Ferron反向代理:

nano /etc/ferron.kdl

写入如下内容:

rustfs.example.com {
    proxy "http://127.0.0.1:9000/"
    proxy_request_header_replace "Host" "{header:Host}"
}

rustfs-console.example.com {
    proxy "http://127.0.0.1:9001/"
    proxy_request_header_replace "Host" "{header:Host}"
}

重载Ferron:

systemctl reload ferron

如遇到Ferron反向代理导致上传速度慢,可以参考这篇文章解决。

控制台URL:https://rustfs-console.example.com
登录账号:你设置的RUSTFS_ACCESS_KEY值
登录密钥:你设置的RUSTFS_SECRET_KEY值

简单说一下虚拟主机风格(Virtual Host Style)的存储桶。我们之前已经配置了RUSTFS_SERVER_DOMAINS环境变量,域名为:rustfs.example.com,那么虚拟主机风格存储桶的命名规则应该是这样的:

bucket0.rustfs.example.com
bucket1.rustfs.example.com
bucket2.rustfs.example.com
...

实战演示一下,在RustFS控制台创建一个桶,例如名字为:photo-rustfs

编辑Ferron配置文件:

nano /etc/ferron.kdl

将之前的配置修改为如下内容:

rustfs.example.com,*.rustfs.example.com {
    proxy "http://127.0.0.1:9000/"
    proxy_request_header_replace "Host" "{header:Host}"
}

另外因为需要用到通配符域名证书,务必将Ferron申请证书的方式改为DNS-01,这里我配置的DNS服务商是CloudFlare,将yourkey修改成你在CloudFlare申请的key:

* {
    ...
//    auto_tls_challenge "http-01"
    auto_tls_challenge "dns-01" provider="cloudflare" api_key="yourkey"
    ...
}

重载Ferron:

systemctl reload ferron

这个桶现在的URL应该是:

photo-rustfs.rustfs.example.com

将桶的访问策略设置为公有,然后上传文件进行测试,如果正常应该可以访问到上传的文件:

参考:

https://docs.rustfs.com/integration/virtual.html
https://github.com/rustfs/rustfs/blob/main/docker-compose.yml

La Suite Meet:基于LiveKit的开源视频会议程序

2026-01-29 17:06:56

La Suite Meet由LiveKit提供技术支持,拥有媲美Zoom的卓越性能和高质量音视频体验。

特点:

  • 针对大型会议的稳定性进行了优化(100+人)
  • 支持多屏幕共享
  • 非持久性、安全聊天
  • 端到端加密(即将推出)
  • 会议记录
  • 会议记录及摘要(目前处于测试阶段)
  • 电话系统集成
  • 通过强大的身份验证和访问控制机制确保安全参与
  • 可定制的前端样式

这个项目也是法国政府主导的,引用下面这段话:

On the 25th of January 2026, David Amiel, France’s Minister for Civil Service and State Reform, announced the full deployment of Visio—the French government’s dedicated Meet platform—to all public servants

这哥们打算让法国所有公务员都用这个开会= =这么牛b的程序(其实牛b的是LiveKit)必须部署逝一下。。。

本文根据官方的文档编写,主要记录并解决目前官方部署文档内的一些错误。

准备工作:

1.准备一个域名做好解析:meet.example.com(主程序),livekit.example.com(livekit服务)

2.部署VoidAuth OIDC身份验证服务,示例域名:voidauth.example.com

3.服务器的这些端口不能被其他程序占用:80、443、7880、7881、7882(UDP)、8086

在VoidAuth创建OIDC APP:

请将Redirect URLs配置为,注意URL最后的/,不要少了这个/:

https://meet.example.com/api/v1.0/callback/

请将PostLogout URL配置为,注意URL最后的/,不要少了这个/:

https://meet.example.com/api/v1.0/logout-callback/

做好上述准备工作后,现在就可以来部署Meet了,安装Docker:

apt -y update
apt -y install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

下载compose文件以及需要用到的环境变量配置文件、LiveKit配置文件、NGINX配置文件:

cd /opt
mkdir -p meet/env.d && cd meet
curl -o compose.yaml https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/docs/examples/compose/compose.yaml
curl -o .env https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/env.d/production.dist/hosts
curl -o env.d/common https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/env.d/production.dist/common
curl -o env.d/postgresql https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/env.d/production.dist/postgresql
curl -o livekit-server.yaml https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/docs/examples/livekit/server.yaml
curl -o default.conf.template https://raw.githubusercontent.com/suitenumerique/meet/refs/heads/main/docker/files/production/default.conf.template

为了后续部署工作的顺利进行,这里先解决一些目前官方文档里面的错误,编辑compose文件:

nano compose.yaml

后端容器设置的env配置文件是错的,根本没有backend这个文件,取而代之的是.env文件,所以这里注释掉不存在的backend文件,添加.env文件:

...
  backend:
    image: lasuite/meet-backend:latest
    ...
    restart: always
    env_file:
    - env.d/common
#    - env.d/backend
    - .env
    - env.d/postgresql
    ...

前端容器又缺少.env文件,这里加上,同时将前端容器的8083端口暴露出来(不是官方文档说的8086端口)方便后续配置反向代理:

...
  frontend:
    image: lasuite/meet-frontend:latest
    ...
    env_file:
    - .env
    - env.d/common
    ...
    ports:
    - "127.0.0.1:8086:8083"

将LiveKit容器的7880端口暴露出来,这是Likvkit的“信号”端口,是Meet与Livekit通信的关键:

...
  livekit:
    image: livekit/livekit-server:latest
    command: --config /config.yaml
    ports:
    - 127.0.0.1:7880:7880
    - 7881:7881/tcp
    - 7882:7882/udp
    ...

编辑供前端容器内部使用的NGINX配置文件:

nano default.conf.template

将这两个错误的环境变量:BACKEND_HOST/FRONTEND_HOST修改为如下内容:

upstream meet_backend {
    server ${BACKEND_INTERNAL_HOST}:8000 fail_timeout=0;
}
upstream meet_frontend {
    server ${FRONTEND_INTERNAL_HOST}:8080 fail_timeout=0;
}

编辑.env文件:

nano .env

这里只列出需要修改的内容:

MEET_HOST=meet.example.com
#KEYCLOAK_HOST=id.domain.tld # 注释掉这个配置,我们不使用KEYCLOAK
LIVEKIT_HOST=livekit.example.com
#REALM_NAME=meet # 注释掉这个配置,我们不使用KEYCLOAK

编辑common文件:

nano env.d/common

这里只列出需要修改的内容:

# Django
DJANGO_SECRET_KEY= # 使用openssl rand -hex 32生成

# Mail
DJANGO_EMAIL_HOST=mail.example.com
DJANGO_EMAIL_HOST_USER=smtp
DJANGO_EMAIL_HOST_PASSWORD=smtppassword
DJANGO_EMAIL_PORT=587
[email protected]
DJANGO_EMAIL_USE_TLS=true

# OIDC
OIDC_OP_JWKS_ENDPOINT=https://voidauth.example.com/oidc/jwks
OIDC_OP_AUTHORIZATION_ENDPOINT=https://voidauth.example.com/oidc/auth
OIDC_OP_TOKEN_ENDPOINT=https://voidauth.example.com/oidc/token
OIDC_OP_USER_ENDPOINT=https://voidauth.example.com/oidc/me
OIDC_OP_LOGOUT_ENDPOINT=https://voidauth.example.com/oidc/session/end
OIDC_RP_CLIENT_ID=
OIDC_RP_CLIENT_SECRET=

# Livekit Token settings
LIVEKIT_API_SECRET= # 使用openssl rand -hex 32生成

编辑postgresql文件,设置PostgreSQL数据库密码:

nano env.d/postgresql

这里只列出需要修改的内容:

DB_PASSWORD=setyourdbpassword # 设置你的数据库密码。

编辑livekit-server.yaml:

nano livekit-server.yaml

将keys的值设置成和LIVEKIT_API_SECRET相同的内容:

port: 7880
redis:
  address: redis:6379
keys:
  meet: # 把这里的值设置成和LIVEKIT_API_SECRET相同的内容
# WebRTC configuration
rtc:
  # # when set, LiveKit will attempt to use a UDP mux so all UDP traffic goes through
  # # listed port(s). To maximize system performance, we recommend using a range of ports
  # # greater or equal to the number of vCPUs on the machine.
  # # port_range_start & end must not be set for this config to take effect
  udp_port: 7882
  # when set, LiveKit enable WebRTC ICE over TCP when UDP isn't available
  # this port *cannot* be behind load balancer or TLS, and must be exposed on the node
  # WebRTC transports are encrypted and do not require additional encryption
  # only 80/443 on public IP are allowed if less than 1024
  tcp_port: 7881
  # use_external_ip should be set to true for most cloud environments where
  # the host has a public IP address, but is not exposed to the process.
  # LiveKit will attempt to use STUN to discover the true IP, and advertise
  # that IP with its clients
  use_external_ip: true

启动全部容器:

docker compos up -d

运行数据库迁移并创建Django管理员用户:

docker compose run --rm backend python manage.py migrate
docker compose run --rm backend python manage.py createsuperuser --email [email protected] --password adminpassword

稍后你可以使用这个URL访问Docs的Django后台:meet.example.com/admin。在这个后台你可以管理用户创建的房间。

请注意这里创建的用户只拥有Django管理员权限,最终实际可供用户使用的账号必须使用OIDC创建。

配置Ferron反向代理:

nano /etc/ferron.kdl

写入如下内容:

meet.example.com {
   proxy "http://127.0.0.1:8086/"
   proxy_request_header_replace "Host" "{header:Host}"
}

livekit.example.com {
   proxy "http://127.0.0.1:7880/"
   proxy_request_header_replace "Host" "{header:Host}"
}

如果你使用的是别的反向代理,则必须为LiveKit配置WebSocket和长连接支持。Ferron默认支持这些,所以不必写在配置文件内。

重载Ferron:

systemctl reload ferron

效果:

看着这个经典的蓝白配色UI,还有这几个按钮的造型,然后还是法国出品,瞬间就觉得好似一位故人:OVH!

简单测试,在电脑上启用OBS虚拟摄像头,在手机上加入房间,看看双方能不能正常视频通话:

功能还是很全面的,共享浏览器屏幕、发表情、拍手、甚至还有虚拟背景和聊天功能。唯独在国内使用的话可能要注意连通性问题,国内的网络嘛,懂的都懂,如果你按照这篇文章部署后连不上,则可能还需要在LiveKit配置TURN服务。

由法国政府开源的笔记软件:Docs

2026-01-28 19:12:14

Docs这个项目我一直都在关注,奈何之前的部署方式只支持k8s,如果只是为了部署一个笔记软件去搭一个k8s的话,那真的是有点拿大炮打蚊子那味了= =最近发现这个项目支持用docker compose部署了,遂记录下部署过程与遇到的一些问题。

不得不说国家队还是牛B!这个笔记软件第一次用就感觉挺顺手的,前端UI简洁大气,自带AI功能,可以帮你美化、改写、总结、修正错别字、翻译等。自托管的实例所有功能都能使用,与官方实例1:1还原,而不是像某些“开源笔记”那样吃相难看。

缺点也还是有的,没有十全十美的东西= =目前虽然支持docker compose部署了,但部署起来还是比较麻烦。另外我在使用中发现一些小bug,例如:上传的文件名不能包含中文,有中文就会导致上传失败。导出的PDF文件显示中文是乱码,且不支持代码块等。如果你不介意这些问题的话,可以尝试一下。

本文根据Docs官方的文档编写,进行了一些修改:

1.将Keycloak更换为VoidAuth,因为Keycloak部署和配置都过于复杂。

2.将MinIO更换为Garage,因为开源的MinIO已经名存实亡了。

3.不使用官方的NGINX反向代理配置,因为我的服务器已经运行Ferron Web Server了。

如果你完全遵循本文的步骤来部署,则在开始前需要做好以下准备工作:

1.一个域名做好解析,本文示例域名:docs.example.com

2.部署Garage S3对象存储。本文示例域名:s3-garage.example.com

注:如果你使用Garage S3则无法使用Docs的历史版本与回退功能,这是由于目前Garage的S3实现不支持“版本控制”功能导致的,并不是Docs的问题。

3.部署VoidAuth OIDC身份验证服务。本文示例域名:voidauth.example.com

4.创建一个OPENAI的API KEY。

在Garage S3创建存储桶和key并授权:

docker exec -ti garage /garage bucket create docs-media-storage
docker exec -ti garage /garage key create docs-media-storage-app-key
docker exec -ti garage /garage bucket allow --read --write --owner docs-media-storage --key docs-media-storage-app-key

在VoidAuth创建OIDC APP:

务必将Redirect URLs设置为:

https://docs.example.com/api/v1.0/callback/

务必将PostLogout URL设置为:

https://docs.example.com/api/v1.0/logout-callback/

做好上面的准备工作后,现在可以正式部署Docs了,先在服务器内安装Docker:

apt -y update
apt -y install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

下载compose文件以及需要用到的环境变量配置文件、NGINX配置文件:

cd /opt
mkdir -p docs/env.d
cd docs
curl -o compose.yaml https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docs/examples/compose/compose.yaml
curl -o env.d/common https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/common
curl -o env.d/backend https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/backend
curl -o env.d/yprovider https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/yprovider
curl -o env.d/postgresql https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/env.d/production.dist/postgresql
curl -o default.conf.template https://raw.githubusercontent.com/suitenumerique/docs/refs/heads/main/docker/files/production/etc/nginx/conf.d/default.conf.template

设置PostgreSQL数据库密码:

nano env.d/postgresql

这里只列出需要修改的内容:

DB_PASSWORD=setyourdbpassword # 设置你的数据库密码。

设置YPROVIDER API KEY/COLLABORATION SECRET:

nano env.d/yprovider

这里只列出需要修改的内容:

Y_PROVIDER_API_KEY=randomkey # 使用openssl rand -hex 32生成
COLLABORATION_SERVER_SECRET=randomkey # 使用openssl rand -hex 32生成

编辑common环境变量配置文件:

nano env.d/common

这里只列出需要修改的内容:

DOCS_HOST=docs.example.com # 设置你的Docs实例域名
#KEYCLOAK_HOST=id.domain.tld # 注释掉这个变量,因为我们不使用KEYCLOAK
S3_HOST=s3-garage.example.com # 设置你的Garage S3端点域名
BUCKET_NAME=docs-media-storage # 如果你的S3桶名不是docs-media-storage请修改
#REALM_NAME=docs # 注释掉这个变量,因为我们不使用KEYCLOAK

编辑backend环境变量配置文件:

nano env.d/backend

这里只列出需要修改和增加的内容:

## Django
DJANGO_SECRET_KEY= # 使用openssl rand -hex 32生成
DOCUMENT_IMAGE_MAX_SIZE=2048000000 # 增加这个配置,修改默认文件上传大小到2048MB

# Mail
DJANGO_EMAIL_HOST=mail.example.com
DJANGO_EMAIL_HOST_USER=smtp
DJANGO_EMAIL_HOST_PASSWORD=smtppassword
DJANGO_EMAIL_PORT=587
[email protected]
DJANGO_EMAIL_USE_TLS=true

# Media
AWS_S3_REGION_NAME=garage # 因Garage S3强制要求区域,增加这个配置以适配Garage S3
AWS_S3_ACCESS_KEY_ID=
AWS_S3_SECRET_ACCESS_KEY=

# OIDC
OIDC_OP_JWKS_ENDPOINT=https://voidauth.example.com/oidc/jwks
OIDC_OP_AUTHORIZATION_ENDPOINT=https://voidauth.example.com/oidc/auth
OIDC_OP_TOKEN_ENDPOINT=https://voidauth.example.com/oidc/token
OIDC_OP_USER_ENDPOINT=https://voidauth.example.com/oidc/me
OIDC_OP_LOGOUT_ENDPOINT=https://voidauth.example.com/oidc/session/end
OIDC_RP_CLIENT_ID=
OIDC_RP_CLIENT_SECRET=

# AI
AI_FEATURE_ENABLED=true # is false by default
AI_BASE_URL=https://api.openai.com/v1
AI_API_KEY=sk-proj-3aFFuEBT...
AI_MODEL=gpt-4o-mini

编辑NGINX配置文件:

nano default.conf.template

修改上传大小与之前的配置对应:

# increase max upload size
client_max_body_size 2048m;

为了避免混淆,请注意这里的NGINX配置文件是给前端容器在容器内部使用的,并不是给主机NGINX用的。无论你用不用NGINX反代,都必须要有这个文件。

编辑compose文件:

nano compose.yaml

将前端容器的8083端口暴露出来:

...
  frontend:
    image: lasuite/impress-frontend:latest
    ...
    volumes:
      - ./default.conf.template:/etc/nginx/templates/docs.conf.template
    ports:
      - 127.0.0.1:8083:8083
...

启动Docs:

docker compose up -d

运行数据库迁移并创建Django管理员用户:

docker compose run --rm backend python manage.py migrate
docker compose run --rm backend python manage.py createsuperuser --email [email protected] --password adminpassword

稍后你可以使用这个URL访问Docs的Django后台:docs.example.com/admin。请注意这里创建的用户仅作为Django管理员账号,与实际的用户账号不相关,用户账号始终使用OIDC登录。

配置Ferron反向代理:

nano /etc/ferron.kdl

写入如下内容:

docs.example.com {
   proxy "http://127.0.0.1:8083/"
   proxy_request_header_replace "Host" "{header:Host}"
}

重载Ferron:

systemctl reload ferron

看下使用效果,和AFFiNE一样,支持使用/命令呼出菜单:

选中一段内容可以使用AI功能:

AI改写:

用作提示:

gpt-4o-mini这排版感觉比我写的还好啊= =我艹了个DJ!把功能都试的差不多了,最后再补充点不太满意的地方。搜索功能弱了点,似乎不支持全文搜索。不支持密码分享文档。没有个人账号的信息页面,缺少上传头像之类的功能,这方面有点过于简洁了= =总的来说很牛B,给我一种很丝滑的体验。

由AdGuard开源的VPN协议:TrustTunnel

2026-01-25 21:32:12

TrustTunnel可以将任意网络流量(TCP/UDP/ICMP)通过加密的HTTP/2或HTTP/3连接进行隧道传输。该协议的设计目标如下:

隐蔽性:流量显示为标准的HTTPS,难以与常规网页浏览区分开来。
高性能:在单个传输会话上高效复用多个连接。
可靠性:内置会话恢复和健康检查机制。
灵活性:同时支持 HTTP/2(基于TLS)和HTTP/3(基于QUIC)传输协议。

TrustTunnel还有iOS/Android的GUI客户端,Windows和Linux目前只有CLI客户端,但是这个CLI也支持TUN模式。我部署试了一下,目前感觉分流不太行,要是官方能支持GeoIP之类的数据库就好了,现在只能用IPCIDR= =

开始部署前你需要先准备一个域名,做好解析记录,例如:giatunnel.example.com。一台Debian x64服务器(VPS)。

Debian服务器端安装:

cd /opt
wget https://github.com/TrustTunnel/TrustTunnel/releases/download/v0.9.115/trusttunnel-v0.9.115-linux-x86_64.tar.gz
tar -xzvf trusttunnel-v0.9.115-linux-x86_64.tar.gz
mv trusttunnel-v0.9.115-linux-x86_64 trusttunnel
cd trusttunnel/

直接运行这个二进制文件开始配置向导,这个向导会协助你配置服务端的内容:

./setup_wizard

1.服务端监听的地址和端口,默认:0.0.0.0:443,但是考虑到443端口可能会被Web Server等软件占用,本文配置为:0.0.0.0:8443。

2.设置用户账号和密码,这里的账号和密码用于客户端连接。TrustTunnel是支持多用户的,这可能为后续的机场服务铺路= =

3.创建一个规则配置文件(rules.toml)如果你是一个人使用,那么服务器端不需要配置这个,一路回车下一步即可。

4.创建一个核心配置文件(vpn.toml)所有与VPN相关的配置都在这个配置文件内。一路回车下一步即可!

5.协助你配置域名证书,可使用Let’s Encrypt自动申请证书,支持HTTP-01/DNS-01模式。如果服务器80端口被Web Server等软件占用,可以选择DNS-01模式。

如果选择DNS-01模式,则需要在DNS解析提供商处添加相应解析记录:

配置完成后,复制一份systemd服务配置文件:

cp trusttunnel.service.template /etc/systemd/system/trusttunnel.service

启动TrustTunnel服务并设置开机自启:

systemctl enable --now trusttunnel

检查运行状态,确保正常运行:

systemctl status --now trusttunnel

[可选]导出客户端配置文件,方便客户端配置,例如本文之前配置的用户账号是imlala,服务器IP是89.64.19.37,端口为8443,那么执行如下命令:

./trusttunnel_endpoint vpn.toml hosts.toml -c imlala -a 89.64.19.37:8443

会输出类似如下内容,这些是配置客户端连接需要的,可以复制保存下来:

# Endpoint host name, used for TLS session establishment
hostname = "giatunnel.exampel.com"
# Endpoint addresses.
addresses = ["89.64.19.37:8443"]
# Whether IPv6 traffic can be routed through the endpoint
has_ipv6 = true
# Username for authorization
username = "imlala"
# Password for authorization
password = "yourvpnpassword"
# Skip the endpoint certificate verification?
# That is, any certificate is accepted with this one set to true.
skip_verification = false
upstream_protocol = "http2"
upstream_fallback_protocol = ""
anti_dpi = false

服务器端的配置就全部完成了,接下来配置客户端。这里我以Linux的CLI客户端为例。

下载客户端压缩包解压进入到目录:

cd /home/imlala/桌面
wget https://github.com/TrustTunnel/TrustTunnelClient/releases/download/v0.99.93/trusttunnel_client-v0.99.93-linux-x86_64.tar.gz
tar -xzvf trusttunnel_client-v0.99.93-linux-x86_64.tar.gz
cd trusttunnel_client-v0.99.93-linux-x86_64

执行配置向导,协助你配置客户端:

./setup_wizard

1.配置VPN的运行模式,目前支持general和selective模式。general是指加入排除列表的都走直连,其他走代理。而selective是加入排除列表的走代理,其他走直连。本文使用的是general模式。

2.配置连接到VPN服务器的IP地址/端口/账号密码,对于本文而言端口号是8443。

3.配置证书,直接回车下一步。但是请不要启用跳过证书验证,这可能会导致安全问题,如果你是自签证书的话就无所谓了。

4.配置服务端的域名,本文示例:giatunnel.example.com

5.配置客户端监听类型,支持Socks5和TUN,本文使用的是TUN。

接下来使用文本编辑器打开向导生成的客户端配置文件:

nano trusttunnel_client.toml

配置一个DNS服务器:

dns_upstreams = ["tls://1.1.1.1"]

[可选]配置分流,下载中国的IP列表,然后新建一个Python脚本,内容如下:

with open("chnroute.txt", "r") as f:
    ips = [f'"{line.strip()}"' for line in f if line.strip()]

with open("config.toml", "w") as f:
    f.write("exclusions = [\n  ")
    f.write(",\n  ".join(ips))
    f.write("\n]")

print("处理完成,结果已保存至 config.toml")

运行脚本,把IP转换成TrustTunnel需要的格式:

python3 ipcidr.py

把转换好的内容全选复制粘贴到trusttunnel_client.toml:

exclusions = [
  "1.0.1.0/24",
  "1.0.2.0/23",
  "1.0.8.0/21",
  "1.0.32.0/19",
  "1.1.0.0/24",
  "1.1.2.0/23",
  "1.1.4.0/22",
  "1.1.8.0/24",
  "1.1.9.0/24",
  "1.1.10.0/23",
  "1.1.12.0/22",
  "1.1.16.0/20",
  "1.1.32.0/19",
  "1.2.0.0/23",
  "1.2.2.0/24",
  ...
]

下载中国域名列表,然后新建一个Python脚本,内容如下:

import json

def extract_domain_suffixes(input_file, output_file):
    try:
        # 1. 读取并解析 JSON 文件
        with open(input_file, 'r', encoding='utf-8') as f:
            data = json.load(f)
        
        all_suffixes = []
        
        # 2. 遍历 rules 提取所有的 domain_suffix
        rules = data.get("rules", [])
        for rule in rules:
            if "domain_suffix" in rule:
                all_suffixes.extend(rule["domain_suffix"])

        if not all_suffixes:
            print("未找到任何 domain_suffix 数据。")
            return

        # 3. 格式化处理
        formatted_list = []
        total_count = len(all_suffixes)
        
        for i, item in enumerate(all_suffixes):
            # 在最前面添加两个空格
            line = f'  "{item}"'
            
            # 如果不是最后一项,则在末尾加逗号
            if i 

运行脚本,把域名转换成TrustTunnel需要的格式:

python3 cndomain.py

把转换好的内容全选复制粘贴到trusttunnel_client.toml:

exclusions = [
  ...
  "zzzla.com",
  "zzzlsh.com",
  "zzznkq.com",
  "zzzppp.com",
  "zzzqqp.com",
  "zzzsxx.com",
  "zzzxwh.net",
  "zzzyb.com",
  "zzzymjg.com",
  "zzzyy.com",
  "zzzyyy.com",
  "zzzzaaaa.com",
  "zzzzzz.me"
]

启动TrustTunnel客户端:

sudo ./trusttunnel_client -c trusttunnel_client.toml

使用TUN模式请使用sudo提升权限,以允许TrustTunnel创建TUN0虚拟网卡及配置路由表。

简单说一下使用体验(分流效果)。。如果客户端使用:

upstream_protocol = "http2"

访问国外的站点很快,但是国内站点比mihomo/sing-box慢多了,分流是生效了的,不知道是不是DNS的问题。。如果客户端使用:

upstream_protocol = "http3"

则访问国外也慢的一匹,应该是奠信限制udp导致的= =,另外请不要把油管4k/8k视频当作测速工具,即便要测也请关闭浏览器的quic(血泪史):

chrome://flags/#enable-quic

Docker部署PowerDNS Authoritative Server

2026-01-19 17:12:01

我几年前写过一篇部署PowerDNS权威服务器的文章,最近需要搭建一个,然后翻出来看了下发现写的不够完善,而且纯手动部署太麻烦了,遂决定重新记录下使用Docker部署的步骤,另外主要补充一下:主从同步、DNSSEC的配置。

首先还是需要到域名注册商添加胶水记录(Glue Record),我使用的是spaceship,这家的用户面板不叫胶水记录,叫个人名称服务器,说实话有时候挺烦这种自己瞎起名字的行为。。害我在他们的这个面板里面找半天:

由于spaceship的限制,这里至少要添加2条胶水记录,如果你不打算部署2台服务器(主从同步),可以把两条记录的值都指向同一台服务器的IP,之后更改域名的NS服务器为刚设置的“个人名称服务器”:

本文示例:91.99.72.72为主(primary)服务器,49.13.168.202为从(secondary)服务器,在两台服务器内都安装Docker:

apt -y update
apt -y install curl
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

在主(primary)服务器创建目录新建compose文件:

mkdir /opt/pdns-mysql && cd /opt/pdns-mysql && nano docker-compose.yml

写入如下内容,需要修改的地方写了注释:

services:
  mariadb:
    image: mariadb:lts
    container_name: pdns-mariadb
    restart: unless-stopped
    networks:
      - pdns
    environment:
      - MARIADB_ROOT_PASSWORD=setyourpdnsmasterdbpassword # 设置数据库ROOT用户的密码
    volumes:
      - ./mariadb-data:/var/lib/mysql:Z
    healthcheck:
      test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
      timeout: 10s
      retries: 5

  phpmyadmin:
    image: phpmyadmin
    restart: unless-stopped
    networks:
      - pdns
    ports:
      - 8988:80
    environment:
      - PMA_HOST=mariadb

  pdns-master:
    image: pschiffe/pdns-mysql:latest
    container_name: pdns-master
    hostname: ns1.ohsb.cc # 设置PDNS主服务器的主机名,务必与你的胶水记录保持一致
    restart: unless-stopped
    networks:
      pdns:
        ipv4_address: 172.89.64.74
    depends_on:
      mariadb:
        condition: service_healthy
    environment:
      - PDNS_gmysql_host=mariadb
      - PDNS_gmysql_port=3306
      - PDNS_gmysql_user=root
      - PDNS_gmysql_password=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
      - PDNS_gmysql_dbname=powerdns
      - PDNS_gmysql_dnssec=yes
      - PDNS_primary=yes
      - PDNS_api=yes
      - PDNS_api_key=setyourpdnsapikey # 设置PDNS API KEY
      - PDNS_webserver=yes
      - PDNS_webserver_address=0.0.0.0
      - PDNS_webserver_allow_from=0.0.0.0/0,::/0
      - PDNS_webserver_password=setyourwebserverpassword # 设置PDNS WEB密码
      - PDNS_version_string=anonymous
      - PDNS_default_ttl=1500
      - PDNS_allow_axfr_ips=49.13.168.202 # 设置为从服务器的公网IP
      - PDNS_only_notify=49.13.168.202 # 设置为从服务器的公网IP
    ports:
      - '53:53'
      - '53:53/udp'
      - '8081:8081'
    volumes:
      - /etc/localtime:/etc/localtime:ro

  pdns-admin:
    image: pschiffe/pdns-admin
    container_name: pdns-admin
    restart: unless-stopped
    depends_on:
      mariadb:
        condition: service_healthy
    networks:
      - pdns
    ports:
      - '8989:8080'
    environment:
      - PDNS_ADMIN_SQLA_DB_HOST=mariadb
      - PDNS_ADMIN_SQLA_DB_PORT=3306
      - PDNS_ADMIN_SQLA_DB_USER=root
      - PDNS_ADMIN_SQLA_DB_PASSWORD=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
      - PDNS_ADMIN_SQLA_DB_NAME=powerdnsadmin
      - PDNS_API_URL="http://pdns-master:8081/"
      - PDNS_API_KEY=setyourpdnsapikey # 设置连接到PDNS Master的API KEY
      - PDNS_VERSION=5.0
    volumes:
      - /etc/localtime:/etc/localtime:ro

networks:
  pdns:
    ipam:
      config:
        - subnet: 172.89.0.0/16
          gateway: 172.89.0.1

在从(secondary)服务器创建目录新建compose文件:

mkdir /opt/pdns-slave && cd /opt/pdns-slave && nano docker-compose.yml

写入如下内容,需要修改的地方写了注释:

services:
  mariadb:
    image: mariadb:lts
    container_name: pdns-mariadb
    restart: unless-stopped
    networks:
      - pdns
    environment:
      - MARIADB_ROOT_PASSWORD=setyourpdnsmasterdbpassword # 设置数据库ROOT用户的密码
    volumes:
      - ./mariadb-data:/var/lib/mysql:Z
    healthcheck:
      test: ['CMD', 'healthcheck.sh', '--connect', '--innodb_initialized']
      timeout: 10s
      retries: 5

  phpmyadmin:
    image: phpmyadmin
    restart: unless-stopped
    networks:
      - pdns
    ports:
      - 8988:80
    environment:
      - PMA_HOST=mariadb

  pdns-slave:
    image: pschiffe/pdns-mysql:latest
    container_name: pdns-slave
    hostname: ns2.ohsb.cc # 设置PDNS从服务器的主机名,务必与你的胶水记录保持一致,从服务器必须设置,否则无法与主服务器同步!
    restart: unless-stopped
    networks:
      pdns:
        ipv4_address: 172.89.64.75
    depends_on:
      mariadb:
        condition: service_healthy
    environment:
      - PDNS_gmysql_host=mariadb
      - PDNS_gmysql_port=3306
      - PDNS_gmysql_user=root
      - PDNS_gmysql_password=setyourpdnsmasterdbpassword # 设置连接数据库ROOT用户的密码
      - PDNS_gmysql_dbname=powerdnsslave
      - PDNS_gmysql_dnssec=yes
      - PDNS_secondary=yes
      - PDNS_autosecondary=yes
      - PDNS_webserver=yes
      - PDNS_webserver_address=0.0.0.0
      - PDNS_webserver_allow_from=0.0.0.0/0,::/0
      - PDNS_webserver_password=setyourwebserverpassword # 设置PDNS WEB密码
      - PDNS_version_string=anonymous
      - PDNS_default_ttl=1500
      - PDNS_disable_axfr=yes
      - PDNS_allow_notify_from=91.99.72.72 # 设置为主服务器的公网IP
      - SUPERMASTER_IPS=91.99.72.72 # 设置为主服务器的公网IP
    ports:
      - '53:53'
      - '53:53/udp'
      - '8081:8081'
    volumes:
      - /etc/localtime:/etc/localtime:ro

networks:
  pdns:
    ipam:
      config:
        - subnet: 172.89.0.0/16
          gateway: 172.89.0.1

配置项太多,这里我也懒得详细说明了,打字太累= =为了方便理解,我没有给敏感内容(域名、服务器IP等信息)脱敏,这套配置是我目前从测试服务器1:1复制下来的。只要你按照注释来配置,肯定能跑起来的,并且功能都是正常的。

启动主、从服务器的所有服务:

docker compose up -d

配置DNSSEC,首先打开PowerDNS-Admin(91.99.72.72:8989)注册一个账号,第一个注册的账号自动成为管理员。

在PowerDNS-Admin添加Zone,Zone Name:你的域名,Zone Type选择:Primary,一定要选择Primary,Primary,Primary!

按如图所示添加2条NS记录以及2条A记录:

启用DNSSEC:

会回显类似如图的信息:

DNSKEY不用管,你可以简单理解为这是公钥。我们需要注意的是DS下面的内容,这实际上代表两条DS记录:

41411 13 2 48e9394892ee2da8...
41411 13 4 9a9382822735e648...

41411是“密钥标签”,13是“算法”,2和4是“摘要类型”,后面一长串是“摘要”。按照这个格式在spaceship内添加两条DS记录:

实际上只添加一条DS记录也是可以的,这个取决于你自己。检查DNSSEC是否生效,可以安装如下软件包:

apt install bind9-dnsutils

测试:

delv ns1.ohsb.cc
delv ns2.ohsb.cc

如果输出的内容有fully validated,则说明DNSSEC工作正常:

检查主从同步是否正常,可以使用compose内部署的phpmyadmin登录到服务器数据库,查看两个数据库内的数据是否一致:

总结下部署过程中遇到的问题。从服务器无法同步,报错:

Unable to find backend willing to host ohsb.cc for potential autoprimary 91.99.72.72. Remote nameservers

这是从服务器的Docker容器没有设置正确的hostname导致的,见此issue

在spaceship设置了DNSSEC的DS记录后,spaceship面板的DNS传播状态异常。这是由于PowerDNS内的NS记录配置错误导致的。我在spaceship配置了两个NS服务器,那么PowerDNS-Admin内也应该有两条NS记录,必须要保持一致。

参考资料:

https://hub.docker.com/r/pschiffe/pdns-mysql
https://github.com/pschiffe/docker-pdns/blob/master/docker-compose-mysql.yml
https://doc.powerdns.com/authoritative/settings.html
https://doc.powerdns.com/authoritative/backends/generic-mysql.html#gmysql-dnssec