↓ До тексту
  1. Статті/

Stirling PDF - офісний PDF-комбайн на власному сервері

cat /etc/passwd
Автор
cat /etc/passwd
Технічний блог про Linux, DevOps та Open Source технології.
Зміст
Офіс без адміна - частина серії статей.
Частина 2: ця стаття (ти тут)

У будь-якому офісі щодня треба щось зробити з PDF: зшити кілька сканів у один файл, стиснути важкий документ, щоб проліз у пошту, перегнати Word чи Excel у PDF перед відправкою, зняти пароль зі звіту, розпізнати текст у скані. І майже завжди це закінчується однаково - людина забиває в пошук «merge pdf online», тягне файл на перший-ліпший сайт і тисне кнопку. А в тому файлі - договір, виписка з банку чи персональні дані клієнтів, які щойно поїхали на невідомий сервер у невідомій юрисдикції.

Stirling PDF закриває цю діру одним рухом: це веб-набір інструментів для PDF, який ти ставиш на власну машину. Об’єднання, розбиття, стиснення, конвертація Office<->PDF, OCR, підписи, додавання й зняття паролів, поворот сторінок - десятки операцій, і всі вони виконуються на твоєму сервері. Файли нікуди не їдуть. Для офісу це ідеальний варіант: один контейнер, веб-інтерфейс, а в третій версії вхід захищений паролем уже з коробки, тож сервіс не стоїть нарозхрист для всієї локальної мережі.

Це другий пост серії «Офіс без адміна»: ставимо корисні сервіси так, щоб їх потягнула людина без фаху системного адміністратора. У першому ми підняли власний сейф для паролів на Vaultwarden. Сьогодні - власний PDF-комбайн, від порожньої VM до працюючого інструмента в браузері.

Що потрібно зробити?
#

Підняти Stirling PDF на одній віртуалці для офісу приблизно на 20 людей:

  • винести дані (конфіги, базу користувачів, мовні дані OCR) на окремий диск - щоб бекапити й переносити їх окремо від системи;
  • поставити Docker і запустити зв’язку Stirling PDF + Caddy одним docker compose;
  • узяти правильний образ - той, що вміє конвертувати кириличні документи й розпізнавати текст, а не урізаний;
  • докинути українські мовні дані для OCR;
  • зробити HTTPS без публічного домену через внутрішній центр сертифікації Caddy;
  • насамкінець замкнути двері - змінити типовий пароль адміна, щоб сервіс не був відкритий для всіх.

З чим працюю?
#

Stirling PDF - це не Vaultwarden. Повний образ тягне за собою LibreOffice для конвертації Office-документів і рушій OCR, тож і апетити в нього більші. Будьмо чесними: реалістичний мінімум - 2 vCPU / 4 ГБ RAM, комфортно - 4 vCPU / 8 ГБ. У простої сервіс їсть близько 1.3 ГБ пам’яті, а під час OCR може стрибнути до ~2 ГБ.

Я розгортав на такій машині:

  • 4 vCPU, 6 ГБ RAM;
  • 12 ГБ системного диска + окремий 4 ГБ диск під дані;
  • ОС Ubuntu 26.04 LTS;
  • плюс 2 ГБ swap - дешева страховка на піки OCR.

Сервер у прикладі доступний у локальній мережі за адресою 192.168.1.51 - публічного домену в нас поки немає. Усюди далі підставляй свою адресу.

Окремий диск під дані
#

Логіка та сама, що й у пості про Vaultwarden: дані не повинні лежати там само, де система. У Stirling PDF на окремий диск ми винесемо конфіги, базу з користувачами й паролями та мовні дані OCR - усе, що боляче втратити при переставлянні системи. Тому під сервіс ми й підкинули віртуалці другий диск на 4 ГБ.

У системі він з’являється порожнім пристроєм /dev/sdb. Форматуємо його в ext4 з міткою spdfdata й монтуємо:

1
2
sudo mkfs.ext4 -L spdfdata /dev/sdb
sudo mkdir -p /mnt/data

Щоб диск підмонтувався сам після перезавантаження, додаємо його в /etc/fstab по UUID (імена пристроїв можуть помінятися місцями). UUID підкаже blkid:

1
sudo blkid /dev/sdb

Рядок у /etc/fstab (підстав свій UUID):

1
UUID=<...> /mnt/data ext4 defaults,noatime 0 2

Перевіряємо запис, одразу монтуючи все з fstab:

1
sudo mount -a

Якщо команда відпрацювала без помилок і /mnt/data змонтувався - диск готовий і переживе перезавантаження.

Поки ми тут, додамо обіцяні 2 ГБ swap - саме задля пікових OCR-операцій:

1
2
3
4
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

І теж закріплюємо в /etc/fstab, щоб swap вмикався сам:

1
/swapfile none swap sw 0 0

Докладніше про те, навіщо окремий диск і як це рятує при переносі, розписано в пості про Vaultwarden - тут я свідомо стисло.

Docker з репозиторіїв Ubuntu
#

Stirling PDF ми запустимо в контейнері. Як і минулого разу, нам не потрібен окремий офіційний репозиторій Docker: у штатних репозиторіях Ubuntu 26.04 лежить цілком свіжий. Ставимо двома пакетами:

1
sudo apt install -y docker.io docker-compose-v2

Це дає Docker 29.x і сучасний docker compose версії 2.40.x (саме docker compose, двома словами). Більше нічого підключати не треба.

Який образ брати: чому саме fat
#

А тут - головна розвилка, на якій легко зробити помилку й потім довго дивуватися, «чому не працює». У Stirling PDF є три варіанти образу:

  • ultra-lite - лише базові операції. Без OCR, без конвертації Office-документів, без входу з паролем. Виглядає спокусливо «легким», але для офісу це пастка.
  • стандартний (latest) - майже все, окрім частини важких залежностей.
  • fat - повний комплект: LibreOffice для конвертації, OCR і, що критично, вшиті шрифти.

Для офісу, якому треба перетворювати на PDF кириличні документи, вибір однозначний - fat. Саме повний образ несе в собі шрифти, завдяки яким українські .docx і .xlsx конвертуються правильно, а не перетворюються на квадратики. Плюс до цього - OCR і LibreOffice. ultra-lite для такої задачі брати не можна: ні OCR, ні конвертації Office, ні навіть входу.

Ще один важливий момент - ім’я образу. Старе frooodle/s-pdf уже застаріле. Актуальний образ тягнемо звідси:

1
docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.1.0-fat

Stirling PDF і Caddy одним compose-файлом
#

Складемо весь сервіс в одній папці /opt/stirling. Stirling ми не публікуємо на хост напряму - його прикриє Caddy, який проксуватиме запити всередині мережі compose, а назовні віддаватиме HTTPS.

1
2
sudo mkdir -p /opt/stirling
cd /opt/stirling

Ось 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
28
29
30
31
32
33
34
35
36
37
38
# /opt/stirling/docker-compose.yml
services:
  stirling-pdf:
    image: docker.stirlingpdf.com/stirlingtools/stirling-pdf:3.1.0-fat
    container_name: stirling-pdf
    restart: unless-stopped
    environment:
      SECURITY_ENABLELOGIN: "true"
      SECURITY_INITIALLOGIN_USERNAME: "admin"
      SECURITY_INITIALLOGIN_PASSWORD: "<надійний-пароль>"
      SYSTEM_DEFAULTLOCALE: "uk-UA"
      JAVA_TOOL_OPTIONS: "-Xms512m -Xmx2500m"
      PROCESS_EXECUTOR_SESSION_LIMIT_LIBRE_OFFICE_SESSION_LIMIT: "1"
    volumes:
      - /mnt/data/stirling/configs:/configs
      - /mnt/data/stirling/customFiles:/customFiles
      - /mnt/data/stirling/pipeline:/pipeline
      - /mnt/data/stirling/logs:/logs
      - /mnt/data/stirling/tessdata:/usr/share/tessdata
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: "4.0"

  caddy:
    image: caddy:2
    container_name: caddy
    restart: unless-stopped
    depends_on:
      - stirling-pdf
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - /mnt/data/caddy:/data
      - /mnt/data/caddy-config:/config

Розберемо, що тут важливо:

  • SECURITY_ENABLELOGIN: "true" - вмикаємо вхід із паролем. У третій версії для не-ultra-lite образів він і так увімкнений за замовчуванням, але краще сказати це явно.
  • SECURITY_INITIALLOGIN_USERNAME / SECURITY_INITIALLOGIN_PASSWORD задають першого адміністратора. Є нюанс, про який нижче: ці змінні спрацьовують лише на найпершому запуску, поки ще не створено базу користувачів у /configs.
  • SYSTEM_DEFAULTLOCALE: "uk-UA" - українська мова інтерфейсу за замовчуванням.
  • JAVA_TOOL_OPTIONS: "-Xms512m -Xmx2500m" - рамки для Java: з чого стартувати й куди не виходити по пам’яті.
  • PROCESS_EXECUTOR_...LIBRE_OFFICE_SESSION_LIMIT: "1" - дозволяємо LibreOffice лише одну конвертацію за раз, щоб не з’їсти всю пам’ять на пачці документів.
  • Усі томи ведуть на наш окремий диск /mnt/data/stirling/*. Окремо зверни увагу на tessdata - про нього наступний розділ.
  • deploy.resources.limits ставить стелю: 4 ГБ пам’яті й 4 ядра, щоб сервіс не поклав усю машину.

Caddy тут такий самий, як у пості про Vaultwarden: приймає HTTPS і передає запити Stirling’у.

Українська мова для OCR
#

Повний образ приходить із мовними даними для OCR тільки на англійську. Але є тонкість: ми змонтували власний том у /usr/share/tessdata, а том перекриває те, що вшите в образ. Тобто якщо просто лишити теку порожньою, OCR узагалі не матиме мов. Тому в неї треба покласти і англійську, і українську руками.

Качаємо дані в /mnt/data/stirling/tessdata:

1
2
3
cd /mnt/data/stirling/tessdata
sudo curl -LO https://github.com/tesseract-ocr/tessdata_fast/raw/main/eng.traineddata
sudo curl -LO https://github.com/tesseract-ocr/tessdata_fast/raw/main/ukr.traineddata

Ми беремо tessdata_fast - це менші й швидші моделі. Якщо захочеш точнішого розпізнавання ціною швидкості, є tessdata_best.

Два правила, щоб потім не ловити сюрпризів: ніколи не видаляй eng (без англійської частина сценаріїв ламається), і том tessdata має бути постійним - якщо він пропаде при переставлянні, мови зникнуть разом із ним.

Caddyfile: HTTPS без домену
#

HTTPS робимо так само, як у пості про Vaultwarden - через внутрішній центр сертифікації Caddy, бо публічного домену в нас немає. Внутрішній порт Stirling PDF - 8080, саме на нього Caddy і проксує:

1
2
3
4
5
6
7
8
{
    default_sni 192.168.1.51
}

192.168.1.51 {
    tls internal
    reverse_proxy stirling-pdf:8080
}

tls internal каже Caddy стати власним CA, а default_sni потрібен, бо при зверненні по IP клієнт не надсилає імені сервера, і без цього рядка TLS-рукостискання обривається.

Приємна відмінність від минулого разу: у Caddy немає типового обмеження на розмір тіла запиту, тож навіть великі PDF завантажуються без зайвих налаштувань. Якби замість Caddy стояв nginx, довелося б окремо піднімати client_max_body_size - а тут нічого не треба.

Запускаємо все:

1
docker compose up -d

Роздаємо довіру
#

Компроміс життя без домену той самий: сертифікат видав наш власний CA, якого браузери поки не знають. Кореневий сертифікат Caddy лежить тут:

1
/mnt/data/caddy/caddy/pki/authorities/local/root.crt

Цей root.crt треба раз установити на офісні пристрої в довірені - як саме для Linux, Windows, macOS і мобільних, докладно розписано в пості про Vaultwarden. Перевірити, що сервер відповідає по HTTPS, можна одразу, вказавши curl наш кореневий сертифікат:

1
curl --cacert /mnt/data/caddy/caddy/pki/authorities/local/root.crt https://192.168.1.51/

У відповідь прилетить код 200 - сервер живий.

Що воно вміє
#

Тепер найприємніше - заради чого все й затівалося. Відкриваємо https://192.168.1.51/, входимо й бачимо головну: десятки інструментів, розкладених по групах.

Головна Stirling PDF: десятки інструментів для PDF в одному місці

Чотири сценарії, які в офісі трапляються щодня:

  • Об’єднати кілька PDF в один. Класика: три скани договору зшити в один файл перед відправкою. Кидаєш файли, розставляєш порядок - отримуєш один PDF.
  • Стиснути важкий PDF. Документ на 40 МБ не лізе в пошту - проганяєш через стиснення й отримуєш файл, який спокійно йде листом.
  • Конвертувати український Word чи Excel у PDF. Ось тут і окупається fat-образ: кириличний .docx чи .xlsx перетворюється на PDF із правильними літерами, а не квадратиками, бо потрібні шрифти вже всередині.
  • Розпізнати текст у скані (OCR). Сканований український документ, по якому не можна шукати й копіювати, прогониш через OCR - і на виході маєш PDF, у якому текст виділяється й шукається.

Усе це - у браузері, кількома кліками, і жоден файл не покидає сервера:

Замикаємо двері
#

Найважливіше для офісного сервісу - щоб ним не скористався хто завгодно з мережі. У третій версії Stirling PDF вхід із паролем для нашого образу увімкнений за замовчуванням, і це добре. Але є дві речі, які треба зробити руками.

По-перше, типовий пароль. Якщо не задати свій, Stirling стартує з парою admin / stirling і сам змусить змінити пароль на першому вході. Не лишай його типовим: або задай свій одразу через SECURITY_INITIALLOGIN_PASSWORD, або зміни при першому вході.

По-друге, важливий нюанс із тими змінними. SECURITY_INITIALLOGIN_USERNAME і SECURITY_INITIALLOGIN_PASSWORD задають першого адміна лише на найпершому запуску - поки ще не створено базу користувачів (вона лягає в /configs). Після першого старту ці змінні вже нічого не змінюють: далі користувачами й паролями керуєш в інтерфейсі, у розділі Admin -> user management. Тобто дописати нового співробітника через зміну compose-файлу не вийде - це робиться в веб-інтерфейсі.

Є ще й повноцінний вхід через зовнішнього провайдера (SSO / OAuth2 - Keycloak, Authentik, Authelia, Google), і в третій версії він безкоштовний. Але для офісу без адміна це надмір: звичайного входу з паролем цілком достатньо, а SSO варто чіпати лише тоді, коли в компанії вже є централізований вхід, до якого хочеться це причепити.

Заковики, які варто знати
#

Дві речі, через які в новачків «не працює»:

  • ultra-lite - пастка. Він легший, але без OCR, без конвертації Office і навіть без входу. Для офісу бери fat - інакше половина сценаріїв просто відсутня, а кирилиця в конвертації перетвориться на квадратики.
  • Не видаляй eng із tessdata. Том tessdata перекриває вшиті в образ мови, тож туди мусять лежати і англійська, і українська. Прибереш англійську - частина сценаріїв відвалиться. І сам том має бути постійним, інакше мови зникнуть при переставлянні.

Куди далі
#

Маємо власний PDF-комбайн: один раз розгорнули - і офіс перестав носити договори та виписки на випадкові онлайн-сайти. Наступні кроки, коли буде настрій: SMTP, щоб користувачі могли відновлювати пароль листом, і SSO, якщо в компанії вже є централізований вхід.

Якщо ж уся ця історія з «зроби сам» - не твоє, і хочеться, щоб хтось просто розгорнув і наглядав за цим замість тебе, напиши в teneta.tech - підкажуть і візьмуть на себе.

А серію «Офіс без адміна» продовжимо: цеглинка за цеглинкою зберемо офісну інфраструктуру, яку потягне звичайна людина без системного адміністратора за плечима.

Офіс без адміна - частина серії статей.
Частина 2: ця стаття (ти тут)

Related

Vaultwarden - менеджер паролів для офісу без адміна

Паролі команди живуть у чатах і на стікерах, поки хтось не звільниться разом із доступами. Ставимо власний Vaultwarden на одну маленьку VM: окремий диск під дані, Docker, HTTPS без домену через внутрішній CA Caddy, а далі - спільний сейф організації з колекціями по відділах і запрошеннями на ~20 людей. Усе кроками, які осилить офіс без штатного адміна.

Cowrie - honeypot, що ловить незваних гостей

·5 хвилин
Honeypot - це сервер-приманка: він навмисне виглядає вразливим, впускає зловмисника зі слабким паролем і тихо записує все, що той робить. Піднімаємо Cowrie в Docker, самі граємо «гостя» і дивимось, хто клюне - у реальному часі.