Review exception request: kimidoc (daemon + browser-support)

Hello,

I’m requesting a manual review / snap declaration for kimidoc so the automated review can pass for the daemon + browser-support combination.

kimidoc is a self-hosted HTML-to-PDF rendering service (REST API) built on Qt WebEngine. The combination being flagged is required for how the application works.

  • browser-support is needed so the embedded Chromium can keep its sandbox enabled while rendering user-supplied HTML templates. We intentionally do not use --no-sandbox because rendering untrusted content is exactly the security model this application is designed for.

  • daemon: simple is used because the application runs as a headless server (Qt offscreen platform, no graphical UI). Before launching Chromium, the wrapper drops privileges to the snap_daemon user using setpriv, so Chromium is never executed as root.

In addition to Snap confinement, the application includes a few extra security measures:

  • Remote subresource fetching is disabled by default.

  • An SSRF interceptor blocks loopback, link-local, and private network ranges.

  • API key authentication is required.

  • Chromium is started with --disable-gpu.

Please let me know if you need any additional information.

Thanks!

This request has been added to the queue for review by the @reviewers team.

Hello @reviewers, below is the relevant snapcraft.yaml content that can help in review:

name: kimidoc
base: core24
version: '0.1.0'

summary: HTML template to PDF rendering service

description: |
  Self-hosted document rendering API. Store HTML templates with
  mustache-style placeholders and render PDFs (invoices, reports, resumes,
  certificates, cards) via REST. Chromium-quality output including complex
  scripts/RTL, custom fonts, charts, and real page numbering.

grade: devel
confinement: strict

# Chromium cannot run as root with its sandbox enabled, while snap daemons
# start as root. The launcher drops privileges to snap_daemon before exec.
system-usernames:
  snap_daemon: shared

plugs:
  kf6-core24:
    interface: content
    content: kf6-core24-all
    target: $SNAP/kf6
    default-provider: kf6-core24

  gpu-2404:
    interface: content
    target: $SNAP/gpu-2404
    default-provider: mesa-2404

apps:
  kimidoc:
    command: bin/kimidoc-launch
    daemon: simple
    restart-condition: on-failure

    plugs:
      - network
      - network-bind
      - browser-support
      - opengl

    environment:
      QT_QPA_PLATFORM: offscreen

      # Headless service: Chromium is forced into software rendering.
      QTWEBENGINE_CHROMIUM_FLAGS: --disable-gpu

      HOME: $SNAP_DATA
      XDG_CACHE_HOME: $SNAP_DATA/.cache

      LD_LIBRARY_PATH: $SNAP/kf6/usr/lib/x86_64-linux-gnu:$SNAP/kf6/usr/lib/x86_64-linux-gnu/libproxy:$SNAP/gpu-2404/x86_64-linux-gnu
      QT_PLUGIN_PATH: $SNAP/kf6/usr/lib/x86_64-linux-gnu/qt6/plugins
      QTWEBENGINEPROCESS_PATH: $SNAP/kf6/usr/lib/qt6/libexec/QtWebEngineProcess
      QTWEBENGINE_RESOURCES_PATH: $SNAP/kf6/usr/share/qt6/resources

      KIMIDOC_PAGEDJS: $SNAP/usr/share/kimidoc/paged.polyfill.js
      KIMIDOC_WEBUI: $SNAP/usr/share/kimidoc/webui
      KIMIDOC_TEMPLATES: $SNAP/usr/share/kimidoc/templates

parts:
  kimidoc:
    plugin: cmake
    source: .

    cmake-parameters:
      - -DCMAKE_BUILD_TYPE=Release
      - -DCMAKE_INSTALL_PREFIX=/usr
      - -DCMAKE_EXE_LINKER_FLAGS=-Wl,-rpath-link,/snap/kde-qt6-core24-sdk/current/usr/lib/x86_64-linux-gnu,--allow-shlib-undefined

    build-snaps:
      - kde-qt6-core24-sdk

    build-environment:
      - CMAKE_PREFIX_PATH: /snap/kde-qt6-core24-sdk/current/usr
      - LD_LIBRARY_PATH: /snap/kde-qt6-core24-sdk/current/usr/lib/x86_64-linux-gnu

    stage-packages:
      - fonts-noto-core
      - fonts-noto-cjk
      - fonts-noto-color-emoji

  launcher:
    plugin: dump
    source: packaging

    organize:
      kimidoc-launch: bin/kimidoc-launch

    stage:
      - bin/kimidoc-launch

Let me know if you need clarification on anything.

Thanks

Hello @keshavnrj,

Thank you for the detailed request. I notice that in your request that you intend to use chromium’s sandbox, but you do not have allow-sandbox: true defined in your snapcraft.yaml. Is this intentional, I do not believe chromium’s sandbox will work without this being defined. Please expand on this before I can proceed further with the review.

In addition, due to the elevated level of privileges the combination of daemon and browser-support provide, if accepted, granting this will require publisher vetting akin to classic requests. If kimidoc is an open-source project, please provide links to the upstream repository to help with this process, or let me know if it is closed-source.

Thank you!

Hello @elisehdy, thank you for the careful review - you caught a real omission.

On allow-sandbox: using Chromium’s sandbox is intentional, the missing allow-sandbox: true was an oversight, not a decision, and is now fixed in the snapcraft.yaml (it will be included in the next upload):

plugs:
  browser-sandbox:
    interface: browser-support
    allow-sandbox: true

For context on how the snap is built to keep the sandbox active:

You are also correct that without allow-sandbox: true the sandbox would not survive full AppArmor confinement.

Our development host runs snapd in partial confinement (Arch Linux), which masked the issue. In that environment we verified that:

  • the sandboxed zygote runs in a separate user namespace,
  • NoNewPrivs is enabled,
  • and no --no-sandbox flag is present on any Chromium child process.

Under enforcing AppArmor, however, user namespace creation would be denied without the allow-sandbox: true attribute. That is why this change is necessary and has now been made.

Regarding the source code: kimidoc is closed-source (proprietary).

I understand that the combination of a daemon and browser-support warrants publisher vetting similar to classic confinement requests, and I’m happy to go through that process. Please let me know what additional information or verification you need from me.

Thanks

Hello @reviewers, any update on this? Let me know if you need any clarification. The latest revision contains all the adjustments @elisehdy pointed out and i verified that the service now runs and behave as expected on Ubuntu (strict confinement), specially the sandbox support for chromium/Qt Webengine is working correctly.

Hi, just a gentle follow-up on this request - it’s been in the review queue since 27 July.

Is there anything further I can provide to help the review along (detail on how the interfaces are used, latest snapcraft.yaml ) Happy to adjust the snap if the current approach isn’t the preferred one.

Rectly we managed to deploy the snap with manual connection to browser-sandbox using snap connect kimidoc:browser-sandbox, and it is running on the production infrastructure at kimidoc.com :smiling_face_with_sunglasses: