Signature PDF 1.11.0 配置参数详解

逐项实测讲解 Signature PDF 1.11.0 的全部配置面:Docker 环境变量层、config.ini 的 [globals] 键与 [signature] retention 段、NSS3 数字签名与管理页授权,含重启覆写、路由遮蔽等踩坑实录与生产配置模板。

Signature PDFSignature PDFPDF电子签名PDF 处理2026/11/02

Signature PDF 1.11.0 配置参数详解

目标:把 Signature PDF 1.11.0 的全部配置面——Docker 环境变量层、config.ini 的 [globals] 键与 [signature] retention 段、数字签名 NSS3 证书、管理页授权——逐项讲清「默认值、改法、生效机制、实测行为」,并沉淀重启覆写、路由遮蔽、错误段等踩坑实录。已在 Ubuntu 26.04.1 LTS(x86_64,Docker 29.9.2 / Compose v5.6.1)运行中的 signaturepdf-1.11.0-stack 环境逐项实测(2026-11-02),源码结论以 v1.11.0 tag 实拉核对。

一、环境信息

  • 操作系统:Ubuntu 26.04.1 LTS(x86_64,内核 7.0.0-38-generic)
  • 应用版本:Signature PDF v1.11.0,官方镜像 xgaia/signaturepdf:1.11.0(镜像 ID e4e417983384,基础镜像 php:8.2-apache)
  • 运行栈:《Signature PDF 1.11.0 Docker Compose 部署指南》 所建 Compose 栈——服务名 signaturepdf、容器 signaturepdf-1.11.0-web、端口 8140:80、网络 10.89.60.0/24(网关 10.89.60.1)、数据目录 ./data:/data
  • 本部署注入的环境变量:SERVERNAME=localhost:8140、UPLOAD_MAX_FILESIZE=48M、POST_MAX_SIZE=48M、MAX_FILE_UPLOADS=401、PDF_STORAGE_PATH=/data、DEFAULT_LANGUAGE=en_US.UTF-8(其余未设置,取模板默认)
  • 应用内关键路径:配置文件 /usr/local/signaturepdf/config/config.ini、PHP 限额 /usr/local/etc/php/conf.d/uploads.ini、Apache 虚拟主机 /etc/apache2/sites-available/signaturepdf.conf、共享存储 /data
  • 取证方式:宿主侧 curl http://localhost:8140/... 观察行为,docker exec 读写配置;源码结论来自 v1.11.0 源码包(Config.class.php、MainController.class.php、entrypoint.sh、config/config.ini.example)
  • 测试素材:contract-test.pdf(2 页 A4、1061 字节)、overlay-b.svg(签名图层)、hello.png(位图转矢量输入)

💡 说明:本篇与部署篇共用同一运行栈,所有「实测」都是对活服务的观察实验——改一个键、发一个请求、看一次回显。数字签名(NSS3)与 OCR 两条能力因官方镜像未预装对应组件,本篇只给出源码级配置方法与缺件证据,完整 API 面实测见 《Signature PDF 1.11.0 API 使用详解》。

二、前置条件

  1. 栈已按部署篇拉起且可访问:
curl -s -o /dev/null -w '%{http_code}\n' http://localhost:8140/signature

实测回显:

200
  1. 具备进入应用容器的权限(改配置、看渲染产物都要用):
docker exec signaturepdf-1.11.0-web id

实测回显:

uid=0(root) gid=0(root) groups=0(root)
  1. 备好测试 PDF 与 SVG 图层(用于观察配置对处理行为的影响):
cd /opt/data/quzuo/tmp/signature-pdf
ls -la contract-test.pdf overlay-b.svg e2e/g3_hello.png

实测回显:

-rw-r--r-- 1 root root 1061 Nov  2 08:25 contract-test.pdf
-rw-r--r-- 1 root root  448 Nov  2 11:40 overlay-b.svg
-rw-r--r-- 1 root root  309 Nov  2 13:00 e2e/g3_hello.png

三、参数查看与修改方式

3.1 两层配置模型

Signature PDF 的配置分两层,改法与生效时机完全不同:

第 1 层:Docker 环境变量(compose environment:)
   └─ 启动瞬间由 entrypoint.sh 用 envsubst 渲染进 3 个产物:
      apache.conf → /etc/apache2/sites-available/signaturepdf.conf
      php.ini     → /usr/local/etc/php/conf.d/uploads.ini
      config.ini.tpl → /usr/local/signaturepdf/config/config.ini(只渲染 4 个键)
第 2 层:config.ini 本体(Fat-Free $f3->config() 加载)
   └─ 除模板 4 键外,ADMIN_AUTHORIZED_IP、PDF_LOCAL_PATH、METADATA_DEFAULT_FIELDS、
      REVERSE_PROXY_URL、NSS3_*、[signature] retention 等键只能直接编辑该文件

⚠️ 注意:第 1 层只覆盖 4 个键(PDF_STORAGE_PATH、DISABLE_ORGANIZATION、PDF_DEMO_LINK、PDF_STORAGE_ENCRYPTION),其余键环境变量不认——设了也不会进 config.ini。想持久化第 2 层配置,必须挂载定制 config.ini.tpl 或做派生镜像(见 9.2 与常见问题 Q1)。

3.2 查看当前生效值

三个渲染产物逐一实查(宿主侧执行):

docker exec signaturepdf-1.11.0-web cat /usr/local/signaturepdf/config/config.ini

实测回显(本部署启动后原样):

[globals]

; Path to which stored pdf to activate the mode of sharing a signature to several.
; To deactivate this mode, simply do not configure it or leave it empty
PDF_STORAGE_PATH=/data

; Disable organization tab and routes
DISABLE_ORGANIZATION=false

; Manage demo link pdf : true (by default, show), false (hide), or custom link
PDF_DEMO_LINK=

; Encryption activation (default activation if GPG is installed)
PDF_STORAGE_ENCRYPTION=false
docker exec signaturepdf-1.11.0-web cat /usr/local/etc/php/conf.d/uploads.ini

实测回显:

upload_max_filesize = 48M
post_max_size = 48M
max_file_uploads = 401
docker exec signaturepdf-1.11.0-web grep ServerName /etc/apache2/sites-available/signaturepdf.conf

实测回显:

    ServerName localhost:8140

PHP 限额不必进机器查 php.ini,页面内嵌 JS 直接暴露实际生效值:

curl -s http://localhost:8140/signature | grep -oE 'maxSize = [0-9]+|maxPage = [0-9]+'

实测回显:

maxSize = 48000000
maxPage = 400

3.3 内置配置查看面板 /administration

应用自带一个「Administration panel」配置总览页,汇总外部工具版本与关键路径。默认授权名单只有 localhost、127.0.0.1、::1 三个来源 IP(源码 Config.class.php 硬编码合并进 ADMIN_AUTHORIZED_IP),经宿主端口映射访问时来源是 Docker 网关 10.89.60.1,因此直接访问被拒:

curl -s http://localhost:8140/administration; echo; curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8140/administration

实测回显:

You (10.89.60.1) are not allowed to access this file. Check MainController.class.php for more information.
HTTP 200

💡 说明:这里有两个反直觉点。其一,拒绝响应是 HTTP 200 + 纯文本而不是 403——源码 MainController::administration 用的是 PHP die('You ('.$_SERVER['REMOTE_ADDR'].') are not allowed...'),die() 直接输出正文、不改状态码;用监控脚本判断该页可用性时不能只看状态码。其二,提示文案里点名的 MainController.class.php 就是改授权的地方(准确说是 config.ini 的 ADMIN_AUTHORIZED_IP 键,见 6.1)。

把网关 IP 加进授权名单后(改法见 3.4):

curl -s -o /dev/null -w 'HTTP %{http_code}\n' http://localhost:8140/administration
curl -s http://localhost:8140/administration | grep -oE '<title>[^<]+</title>'

实测回显:

HTTP 200
<title>Administration panel</title>

面板正文(纯文本化节选)把工具链版本、缺件状态、存储路径与授权名单一次列全:

Configuration
  PHP version 8.2.35
  rsvg-convert version 2.60.0
  pdftk port to java 3.3.3
  ImageMagick 7.1.1-43
  potrace 1.16
  Ghostscript version 10.05.1
  gpg (GnuPG) 2.4.7
  CertUtil (Package libnss3-tools missing)
  Pdfsig (Package poppler-utils missing)
File Configuration
  PDF_STORAGE_PATH /data/
  UPLOADS /tmp/
  List of authorized IP : localhost 127.0.0.1 ::1 10.89.60.1

CertUtil/Pdfsig 两行 missing 即官方镜像未预装数字签名组件的直接证据(NSS 配置见 6.5)。

3.4 修改方式与生效时机

改法适用键生效时机重启后
改 compose environment: 后 docker compose up -d 重建第 1 层 9 个环境变量重建即生效保留(源头就是它)
docker exec ... vi /usr/local/signaturepdf/config/config.ini 直接编辑第 2 层全部键下一个请求即生效,无需重启丢失(被模板重渲染覆写)
挂载定制 config.ini.tpl 或派生镜像两层全部重建/重发布即生效保留

「直接编辑即时生效」的机制:Fat-Free 在每个请求初始化 Config 时都执行 $this->f3->config(ROOT.'/config/config.ini') 重新读文件。实测佐证——向 config.ini 追加 PDF_LOCAL_PATH=/data 后不做任何重启,紧接着的请求行为立刻改变:

docker exec signaturepdf-1.11.0-web sh -c "printf '\nPDF_LOCAL_PATH=/data\n' >> /usr/local/signaturepdf/config/config.ini"
curl -s -o /dev/null -w 'HTTP %{http_code}\n' 'http://localhost:8140/api/file/get?path=probe.pdf'

实测回显:

HTTP 200

(追加前同一请求返回 403,见 6.2。)

「重启覆写」实测:对容器执行 docker restart 后,上一段追加的键整段消失,文件恢复为模板渲染的 4 键:

docker restart signaturepdf-1.11.0-web && sleep 6
docker exec signaturepdf-1.11.0-web grep -c 'PDF_LOCAL_PATH' /usr/local/signaturepdf/config/config.ini

实测回显:

0

四、参数总览

全部可配项、默认值与来源一览(官方默认取自 installation.md 与 config.ini.example,2026-11-02 实查):

层键默认值作用详解小节
环境变量SERVERNAMElocalhostApache ServerName(部署 URL)5.1
环境变量UPLOAD_MAX_FILESIZE24M单上传文件大小上限5.2
环境变量POST_MAX_SIZE24M整个 POST 请求体上限5.2
环境变量MAX_FILE_UPLOADS201单次请求文件数上限(=页数+1)5.2
环境变量PDF_STORAGE_PATH/data共享存储目录;留空关闭共享模式5.3
环境变量DISABLE_ORGANIZATIONfalse关闭页面整理入口5.4
环境变量PDF_DEMO_LINKtrue演示 PDF 链接:true/false/自定义 URL5.5
环境变量DEFAULT_LANGUAGEfr_FR.UTF-8界面默认语言(决定 locale-gen)5.6
环境变量PDF_STORAGE_ENCRYPTIONfalse共享存储 GPG 加密开关5.7
环境变量DEBUG未设保留临时文件不清理(非官方文档项,源码实查)5.8
config.iniADMIN_AUTHORIZED_IP空(硬编码并入 localhost/127.0.0.1/::1)/administration 授权 IP,空格分隔6.1
config.iniPDF_LOCAL_PATH未设本地文件 API 根目录6.2
config.iniMETADATA_DEFAULT_FIELDS[key].type未设元数据页预置字段6.3
config.iniREVERSE_PROXY_URL''反代改写下沉时的前缀6.4
config.iniNSS3_DIRECTORY/NSS3_PASSWORD/NSS3_NICK未设数字签名 NSS 证书库6.5
config.ini [signature]retention[+N unit]="文案"6 档默认(1hour~1year)覆盖共享保留期选项七

五、Docker 环境变量层详解

5.1 SERVERNAME

渲染进 Apache 虚拟主机的 ServerName(3.2 已见回显 ServerName localhost:8140)。生产填实际域名;不填时 Apache 启动日志出现 AH00558: Could not reliably determine the server's fully qualified domain name 告警,功能不受影响(部署篇 4.1 实测)。它同时决定 Web::send 回显文件名之外的站点自识别 URL 基座(/api/share/new 返回的 url 字段用 $_SERVER['SERVER_NAME']+端口拼接,见 API 篇)。

5.2 三个上传限额

UPLOAD_MAX_FILESIZE/POST_MAX_SIZE/MAX_FILE_UPLOADS 渲染进 uploads.ini(3.2 回显 48M/48M/401)。约束关系:单文件 ≤ UPLOAD_MAX_FILESIZE,整个请求体 ≤ POST_MAX_SIZE(整理多份 PDF 时是「所有文件之和」,最先撞),文件数 ≤ MAX_FILE_UPLOADS - 1(页数上限)。页面 JS 里的 maxSize 取两者较小值、按 1000 进制折算(48M→48000000,源码 Config::convertPHPSizeToBytes 逐级 ×1000,与 1024 进制 MiB 不同——按 MiB 预期请填 50M)。超限文件在落盘前被 PHP 拒收,前端表现为上传直接失败。

5.3 PDF_STORAGE_PATH:留空即关共享

设为空值后共享模式整体关闭——签名页不再出现「分享」表单,前端共享开关归零:

# 实验:把模板渲染出的 PDF_STORAGE_PATH=/data 改为空并即时生效(无需重启)
docker exec signaturepdf-1.11.0-web sh -c "sed -i 's|^PDF_STORAGE_PATH=/data|PDF_STORAGE_PATH=|' /usr/local/signaturepdf/config/config.ini"
curl -s http://localhost:8140/signature | grep -c 'form_sharing'
curl -s http://localhost:8140/signature | grep -o 'var sharingMode = [01]'

实测回显:

0
var sharingMode = 0

改回 /data 后 form_sharing 命中恢复、sharingMode = 1。源码路径:MainController::signature 中 if(!$f3->get('PDF_STORAGE_PATH')) { $f3->set('noSharingMode', true); };此外 Config.class.php 会给不带尾斜杠的值自动补 /(管理面板显示 /data/ 即此行为)。

5.4 DISABLE_ORGANIZATION:只关首页入口,不封路由

设为 true 后首页直接重定向到签名页:

curl -s -o /dev/null -w 'GET / -> HTTP %{http_code} redirect=%{redirect_url}\n' http://localhost:8140/

实测回显:

GET / -> HTTP 302 redirect=http://localhost:8140/signature

⚠️ 注意:/organization 路由本身仍然放行——直接访问依旧返回 200 并渲染整理页(实测 GET /organization -> HTTP 200)。源码 MainController::index 只做首页 reroute,未对 organization 路由加拦截;installation.md 所谓 "Disable the Organize route" 实际只是「隐藏入口」。需要硬关路由时要在前置反代上屏蔽 /organization。

5.5 PDF_DEMO_LINK:三态取值

控制签名页「试试示例 PDF」链接。三种取值实测:

  • 空/true(默认):渲染出指向仓库示例文档的链接——
curl -s http://localhost:8140/signature | grep -o 'document\.pdf' | sort -u

实测回显:

document.pdf

(源码缺省值为 https://raw.githubusercontent.com/24eme/signaturepdf/master/tests/files/document.pdf,阿拉伯语界面自动切换 document_ar.pdf。)

  • false:链接整体消失——实测页面 grep demo 链接命中 0;
  • 自定义 URL:链接指向所填地址,实测回显 href="#https://example.org/demo.pdf"——注意模板在值前拼了 #(前端 JS 拦截该链接代为拉取),填相对路径亦可。

5.6 DEFAULT_LANGUAGE:locale 生成 + 界面缺省语言

启动时入口脚本 sed 取消 /etc/locale.gen 中所填 locale 的注释并 locale-gen,再 export LANG/LANGUAGE/LC_ALL。启动日志可见生成过程(部署篇 4.3 回显 en_US.UTF-8... done)。界面实际语言优先级:?lang= 参数 > LANGUAGE Cookie > 该缺省值。实测带 ?lang=fr 访问返回法文页并种一年期 Cookie:

curl -s -D - -o /dev/null 'http://localhost:8140/signature?lang=fr' | grep -E 'HTTP|Set-Cookie: LANGUAGE'

实测回显(节选):

HTTP/1.1 200 OK
Set-Cookie: LANGUAGE=fr; expires=Tue, 02 Nov 2027 05:02:26 GMT; Max-Age=31536000; path=/; SameSite=Strict

响应页面 <title>Signature PDF - Signer un PDF en ligne</title>(法文)。支持值共 15 种(ar/de/en/es/eu/fr/it/gl/kab/nl/oc/pl/ro/ta/tr,源码 SUPPORTED_LANGUAGES),中文不在列;?lang=zh 会被 selectLanguage 忽略、回落缺省语言。

5.7 PDF_STORAGE_ENCRYPTION:加密开关与 GPG 依赖

设为 true 后,签名页共享表单出现默认勾选的加密选项:

docker exec signaturepdf-1.11.0-web sh -c "sed -i 's|^PDF_STORAGE_ENCRYPTION=false|PDF_STORAGE_ENCRYPTION=true|' /usr/local/signaturepdf/config/config.ini"
curl -s http://localhost:8140/signature | grep -o 'checkbox_encryption[^/]*/'

实测回显:

checkbox_encryption" name="encryption" value="true" checked="checked" form='form_sharing'/

源码约束(Config.class.php):开关为真但 GPG 未安装时强制回落 false——官方镜像已含 /usr/bin/gpg(GnuPG 2.4.7,管理面板可见),故本实验生效。开启后共享目录内全部文件带 .gpg 后缀、服务端不可读明文(密钥只在浏览器 Cookie 与链接 # 片段),共享目录形态实测见部署篇第五章与 API 篇生命周期实验。

5.8 DEBUG:源码级隐藏项

Config.class.php 读 getenv("DEBUG"),为真时各处理路由(sign/share/image2svg/compress)跳过临时文件清理、PDFSignature 跳过中间产物归并,便于排查合成问题。该变量不在 installation.md 配置表内,属调试用途,生产勿开(临时文件会持续占盘)。本篇结论为源码实查(v1.11.0 MainController.class.php 多处 if($f3->get('DEBUG')) return;),未做运行态实验。

六、config.ini [globals] 键详解

以下键均不在 envsubst 模板内,只能按 3.4 方式直接编辑 config.ini(即时生效、重启丢失)。

6.1 ADMIN_AUTHORIZED_IP

控制谁能打开 /administration 面板。值按空格分隔的 IP 列表解析,源码再硬编码并入 localhost 127.0.0.1 ::1:

$this->f3->set('ADMIN_AUTHORIZED_IP', array_merge(["localhost", "127.0.0.1", "::1"],
    explode(' ', $this->f3->get('ADMIN_AUTHORIZED_IP') . '')));
$this->f3->set('IS_ADMIN', in_array(@$_SERVER["REMOTE_ADDR"], $this->f3->get('ADMIN_AUTHORIZED_IP')));

实测(3.3 已演示拒绝态):追加 ADMIN_AUTHORIZED_IP=10.89.60.1 后面板打开,且面板授权名单行显示 localhost 127.0.0.1 ::1 10.89.60.1——合并逻辑的直接可视化。经宿主端口映射访问时来源 IP 是 Docker 网关而非 127.0.0.1,这是「本地 curl 能开、浏览器打不开」的根因;裸机 Apache 直连则无此问题。

⚠️ 注意:该键必须落在 [globals] 段。实测把同样的行写到 [signature] 段下(文件尾新起一段),面板依旧被拒——Fat-Free 按段取键,$f3->get('ADMIN_AUTHORIZED_IP') 只认 [globals] 下的同名键。排障实录见 Q5。

6.2 PDF_LOCAL_PATH:本地文件 API 根目录

设定后 /api/file/get 端点才工作(未设时一律 403)。三重路径校验(源码 Config::getApiLocalFilePath):拒绝含 .. 的路径、realpath 必须落在根目录内、文件必须存在;扩展名只放行 pdf/png/jpg/jpeg。四组实测:

# 未设 PDF_LOCAL_PATH
curl -s -o body.txt -w 'HTTP %{http_code}\n' 'http://localhost:8140/api/file/get?path=contract-test.pdf'
# 设定后:合法路径
curl -s -o ok.pdf -w 'HTTP %{http_code}\n' 'http://localhost:8140/api/file/get?path=probe.pdf'
# 设定后:穿越攻击
curl -s -o trav.txt -w 'HTTP %{http_code}\n' 'http://localhost:8140/api/file/get?path=../../etc/passwd'
# 设定后:不存在 / 非法扩展名
curl -s -o miss.txt -w 'HTTP %{http_code}\n' 'http://localhost:8140/api/file/get?path=nothere.pdf'
curl -s -o ext.txt -w 'HTTP %{http_code}\n' 'http://localhost:8140/api/file/get?path=local-sample.txt'

实测回显:

HTTP 403
HTTP 200
HTTP 403
HTTP 403
HTTP 403

未设时的 403 响应体(Fat-Free 默认错误页,节选):

<h1>Forbidden</h1>
<p>HTTP 403 (GET /api/file/get?path=contract-test.pdf)</p>

⚠️ 注意(重要实测发现):合法路径的 200 响应 Content-Length 为 0,且目标文件被截成 0 字节——实测 probe.pdf 由 1061 字节变 0:

-rw-r--r-- 1 www-data www-data 1061 Nov  2 00:25 /data/probe.pdf   # 请求前
-rw-r--r-- 1 www-data www-data    0 Nov  2 06:42 /data/probe.pdf   # 请求后

根因是 v1.11.0 源码 app.php 把 GET /api/file/get 注册了两次:ApiController->fileGet(读)之后又注册了 ApiController->fileSave(写),后者遮蔽前者;fileSave 把请求体原样写入目标文件,GET 无请求体即写空。带请求体时该端点表现为「写入本地文件」:实测以 --data-binary @contract-test.pdf 再发一次 GET,probe.pdf 恢复为 1061 字节。结论:该端点的「读」能力在当前版本不可用,「写」可用但语义危险(路径即写入目标),配置 PDF_LOCAL_PATH 前务必确认调用方行为,详见 《Signature PDF 1.11.0 API 使用详解》 的逐请求实测。

6.3 METADATA_DEFAULT_FIELDS:元数据页预置字段

按 METADATA_DEFAULT_FIELDS[字段名].type="输入类型" 逐行声明,元数据编辑页会把它们渲染成默认表单字段(模板 metadata.html.php 输出 var defaultFields = <?php echo json_encode($METADATA_DEFAULT_FIELDS) ?>)。实测追加四行:

docker exec signaturepdf-1.11.0-web sh -c "printf 'METADATA_DEFAULT_FIELDS[title].type=\"text\"\nMETADATA_DEFAULT_FIELDS[author].type=\"text\"\nMETADATA_DEFAULT_FIELDS[creationDate].type=\"date\"\nMETADATA_DEFAULT_FIELDS[subject].type=\"email\"\n' >> /usr/local/signaturepdf/config/config.ini"
curl -s http://localhost:8140/metadata | grep -oE 'var defaultFields = [^;]+'

实测回显:

var defaultFields = {"title":{"type":"text"},"author":{"type":"text"},"creationDate":{"type":"date"},"subject":{"type":"email"}}

type 对应 HTML 输入类型(config.ini.example 注明 text/date/number/email 等),字段名即 PDF 信息字典键(Title/Author/CreationDate/Subject 等)。

6.4 REVERSE_PROXY_URL:改写不下沉时的前缀兜底

官方场景:前置反代无法把路径重写透传到应用时,在 config.ini 声明本站公网前缀,模板中所有 href/action/静态资源 URL 与 reroute 目标都会拼上它。实测设 REVERSE_PROXY_URL=http://127.0.0.1:8140 后签名页表单动作变为绝对地址:

curl -s http://localhost:8140/signature | grep -o 'action="http://127.0.0.1:8140/share"'

实测回显:

action="http://127.0.0.1:8140/share"

缺省为空串(Config.class.php 对不存在该键的情况 set('REVERSE_PROXY_URL','')),此时全部相对路径。子路径部署(如 https://host/sign/)优先在反代层做 rewrite,确实做不了才用该键。

6.5 NSS3_DIRECTORY / NSS3_PASSWORD / NSS3_NICK:数字签名证书

三键必须同时存在才启用数字签名路径(源码逐键 exists 判断后初始化 NSSCryptography):

NSS3_DIRECTORY=/path/to/nss3
NSS3_PASSWORD="my secret password"
NSS3_NICK="certificate nick"

配置后每次压印自动调 pdfsig 附加 PKCS#7 签名("Signed with SignaturePDF")。官方镜像缺件实测——容器内 command -v pdfsig certutil 均无输出(ABSENT),管理面板同样显示 CertUtil (Package libnss3-tools missing)、Pdfsig (Package poppler-utils missing):在该镜像里配好三键也不会生效,需派生镜像补装(FROM xgaia/signaturepdf:1.11.0 + RUN apt-get update && apt-get install -y poppler-utils libnss3-tools)。证书库生成用仓库自带 tools/create_nss_certs.sh(官方 installation.md 原文用法):

bash tools/create_nss_certs.sh NSS_DIR PASSWORD NICK URL

生成后 chown www-data NSS 目录,否则 Apache 用户读不到证书库。

七、[signature] retention:自定义共享保留期

共享 PDF 的保留期下拉默认 6 档(1 hour/1 day/1 week/1 month/6 months/1 year,config.ini.example 注释列明)。在 config.ini 新开 [signature] 段、按 retention[+N unit]="文案" 声明即可整体覆盖默认档位。实测追加:

[signature]
retention[+30 minutes]="for 30 minutes"
retention[+2 days]="for two days"

签名页共享表单的下拉随即只剩这两档(源码 MainController::signature 读 signature.retention 注入模板):

<select name='duration' form='form_sharing'>
    <option value='+30 minutes'>for 30 minutes</option>
    <option value='+2 days'>for two days</option>
</select>

保留期语义实测(发起共享选 +30 minutes):

  • 响应 HTTP 302,Location 指向分享链接;
  • 存储目录旁出现 <hash>.expire(11 字节),文件内容就是保留期字符串:
+30 minutes
  • 其 mtime = 发起时刻 + 30 分钟(实测发起于 05:12:28 UTC,mtime 显示 2026-11-02 05:42:28 +0000);追加签名会按当前档位续期;
  • 到期后由 GET /cron 清理(部署篇第十章)。

右侧文案会过 gettext 翻译(example 注释 "Text on the right of the colon will be translated according to the .po files"),界面语言为法语时 "for 30 minutes" 自动译出。

八、生产配置模板

场景假设:协会/小团队内网接力签署站,单台 4C8G、50GB SSD、Ubuntu 26.04,前置 Nginx 反代已配好路径透传(无需 REVERSE_PROXY_URL),共享保留期收紧为 30 分钟与 2 天两档,管理面板仅放行运维跳板与 Docker 网关。

Compose 层(/opt/signaturepdf/docker-compose.yml 增量):

    environment:
      SERVERNAME: "pdf.example.org"
      UPLOAD_MAX_FILESIZE: "48M"
      POST_MAX_SIZE: "48M"
      MAX_FILE_UPLOADS: "401"
      PDF_STORAGE_PATH: "/data"
      DEFAULT_LANGUAGE: "en_US.UTF-8"
      PDF_STORAGE_ENCRYPTION: "true"
    volumes:
      - ./data:/data
      - ./config.ini.tpl:/usr/local/signaturepdf/config/config.ini.tpl:ro

挂载的定制模板 config.ini.tpl(在官方 4 键基础上追加第 2 层键,重启不再丢):

[globals]

PDF_STORAGE_PATH=${PDF_STORAGE_PATH}
DISABLE_ORGANIZATION=${DISABLE_ORGANIZATION}
PDF_DEMO_LINK=${PDF_DEMO_LINK}
PDF_STORAGE_ENCRYPTION=${PDF_STORAGE_ENCRYPTION}

; 管理面板授权:跳板机 + Docker 网关
ADMIN_AUTHORIZED_IP=10.89.60.1 10.0.0.50

[signature]
retention[+30 minutes]="for 30 minutes"
retention[+2 days]="for two days"

要点核对(均可按本篇对应小节复测):PDF_DEMO_LINK 建议置 false 或换内网示例,避免用户浏览器被引向 raw.githubusercontent.com(5.5);加密开关需镜像含 gpg,官方镜像满足(5.7);ADMIN_AUTHORIZED_IP 务必留在 [globals] 段(6.1/Q5);不建议开 PDF_LOCAL_PATH——当前版本该端点读不可用、写有截断风险(6.2)。

九、常见问题

Q1:改好的 config.ini 一重启全没了? 设计如此:entrypoint.sh 每次启动都用 envsubst < config.ini.tpl > config.ini 重刷目标文件(3.4 实测 grep -c 归 0)。持久化只有两条正路——挂载定制模板(第八章)或派生镜像 RUN sed -i '$a ADMIN_AUTHORIZED_IP=...' /usr/local/signaturepdf/config/config.ini.tpl。直接把 config.ini bind mount 进去也会被同一命令覆写,无效。

Q2:/administration 返回 200 却打不开面板? 200 + 纯文本 You (x.x.x.x) are not allowed... 是 die() 的拒绝响应(3.3),把来源 IP 加进 ADMIN_AUTHORIZED_IP 即可;经 Docker 端口映射访问时来源是网关地址(本环境 10.89.60.1),不是 127.0.0.1。

Q3:DISABLE_ORGANIZATION=true 后整理页还能直接访问? 是。该开关只让首页 302 到 /signature,/organization 路由未封(5.4 实测 200)。要硬关在反代上屏蔽路径。

Q4:配了 PDF_LOCAL_PATH 后,/api/file/get 下载回来是空文件,服务器上的文件还被清空了? v1.11.0 路由双注册缺陷:fileSave 遮蔽 fileGet,GET 空请求体即把目标文件写成 0 字节(6.2 实测)。在修复前不要依赖该端点读取文件;需要程序化取回共享 PDF 用 /api/share/get/<hash>/<symmkey>(API 篇实测可用)。

Q5:retention 或 ADMIN_AUTHORIZED_IP 写了没反应? 先查段归属:ADMIN_AUTHORIZED_IP 等键必须在 [globals],retention[...] 必须在 [signature]。实测把 ADMIN_AUTHORIZED_IP 写进 [signature] 段后面板依旧被拒(6.1 踩坑实录);写对段后无需重启、下一请求即生效(3.4)。

Q6:NSS3 三键配了,签名 PDF 里没有数字签名? 官方镜像未装 pdfsig/certutil(6.5 ABSENT 实测),需派生镜像补 poppler-utils libnss3-tools;再核对 NSS 目录属主为 www-data、三键值完全一致(缺一键即整体不启用)。

Q7:PDF_STORAGE_ENCRYPTION=true 但共享页没有加密选项? 镜像缺 GPG 时源码强制回落 false(5.7)。官方镜像自带 gpg 2.4.7 不会触发;自构建精简镜像注意保留。

十、参考链接

Signature PDF 1.11.0 配置参数详解 - Quzuo