Add sealed CI integration: a GitHub Action and a GitLab CI template - #182
Add sealed CI integration: a GitHub Action and a GitLab CI template#182gronke wants to merge 1 commit into
Conversation
|
Yes, #181 is simpler in sense of the Docker layer, which I personally find recommendable hardening. Why not lock down permissions, so that this can be safely trusted in a multi-staged job environment. For me a clear reason to prefer this one over 181, though I find it better documents the intention. Consider it the pitch of the idea and this here being the more elaborate version of it. |
|
Okay, thanks for the clarification. So I will answer only here, but it also applies to 181. May I ask what your basic intention is? Do you want to use it to test the Validator development itself, or do you focus on using the Validator in other use cases that e.g. create XML documents that need validation? We are currently focussing our development efforts on version 2 of the Validator. As this might change how the Validator is to be used in a CI we would like to postpone dealing with this PR until we have more clarity on V2. So may I ask you to please leave this PR open and have some patience with us. Starting from September we should be able to re-assess your nice work. Until then, others can already benefit from it. |
scripts/validate.sh is the engine behind both platforms: download the released standalone jar and the validation configuration (SHA-256-verified when pinned), validate each document, print PASS/FAIL with the failing rule ids and fail on any rejection. The GitHub Action (action.yml) maps its inputs onto the script's environment; the GitLab template fetches the same script from this repository at KOSIT_SCRIPT_REF and drives it with the same variables, so both platforms run identical logic. With Docker available the action executes each validation sealed (the "sealed" input, default "auto"): inside an eclipse-temurin:21-jre container without network access, a read-only root, no capabilities and no privilege escalation, run with an init process, a pids limit and the caller's uid; the document, the jar and the configuration are mounted read-only, only the report directory and a bounded /tmp tmpfs are writable. Downloads always happen outside the container, so the validator itself never touches the network, and the host JVM setup is skipped entirely for "sealed: always". The image tag pins the Java 21 JRE contract and floats the patch level: the released jars are the checksummed artifacts, the runtime is a security-patch channel; an "image" input (KOSIT_IMAGE on GitLab) accepts a digest-pinned reference for stricter policies. The GitLab job image is the same runtime, giving both platforms an identical execution environment. The action self-test validates a conformant XRechnung reference instance and asserts a corrupted one is rejected, exercising the sealed path through the empty-input fallback and an explicit image override, plus the host path.
ba93207 to
599042f
Compare
|
The latter - I develop library that produces EN 16931 / XRechnung documents, and its CI validates the generated corpus with the released, unmodified validator and the XRechnung configuration on every change. To give you an example, this is the current CI configuration of that project: conformance:
name: Conformance (Mustang / KoSIT / veraPDF)
runs-on: ubuntu-latest
timeout-minutes: 20
env:
SEALED: "1"
steps:
- uses: actions/checkout@v7
- uses: ./.github/actions/mustang-validate
- name: Validate the corpora with the KoSIT validator (sealed)
uses: gronke/fork-kosit-validator@ba93207b776962d57ad6ec9adb0739196fc206ae
with:
version: "1.6.2"
checksum: 244978514ad48f67c7573acfffc8f4fd73d81feda6f276710033f9913579857e
configuration: https://github.com/itplr-kosit/validator-configuration-xrechnung/releases/download/v2026-01-31/xrechnung-3.0.2-validator-configuration-2026-01-31.zip
configuration-checksum: 6a5a5911a421b25fbc423f62f93f894df7b236f5d73ca4f84bb222a945082704
files: crates/*/tests/corpus/*.xml
sealed: always
- name: Validate the hybrid PDF pipeline (sealed)
run: bash scripts/validate-pdf.shAt this moment my repository still vendors a custom GitHub Action for ZUGFeRD/mustangproject, which so far I haven't suggested upstream yet. For a developer of a library it is useful to test against external and independently maintained test suites. Running external tools in a CI can be dangerous though, because it could leak environment secrets, private code, compromise following build steps or caches - and this is where the sealed Docker images are useful. They reduce the review effort to inspecting the Action, finding the containers drop privileges and allow to assess the remaining potential threat for the CI. By offering them, we allow users of this validator Action to keep focus on their code. Beyond my very own use case for the Action, other folks may use ARC to produce documents with needs for validation.
I imagine the proposed usage of this action can't be that different from the proposal for v1. I would also not mind if you'd stop providing an action for a new major release. check:
uses: itplr-kosit/validator@v1
files: /tmp/*.xmlThis PR has more complexity than necessarily needed to get to a GitHub Action. I noticed you work on a GitLab environment, which is why I added a reusable template for GitLab as well. Maybe you even find that useful to run a series of test documents on v1 and v2 for a differential comparison of results and performance.
Of course, I'll leave it on GitHub (and from my own project). Looking forward to hear more news from v2 in September. |
|
Thanks for the clarification. Without having it crosschecked with KoSIT, but I think they don't have the knowledge and resources to continuously provide a common Docker image, Github actions etc. So maybe this could be provided by you as a separate project and from within this project, we could link to your upstream projects then. I am convinced that this would be very benefitial for the community. |
|
In this case the maintenance of a GitHub Action is pretty straight forward. It runs a self-test, so that we could configure it to block cutting a new release of the action by accident with a broken version. It would fail noticeably on every PR. What could complicate the situation is the different GitLab development and GitHub environments. But in the worst case failure surfaces on sync to GitHub, with the consequence that the GitHub Action is skipping an update until the build and selftest are green again. What I think we should definitely do, if considering this route, is conducting more tests from a GitLab runner, because my work environment is on GitHub, where I would notice mismatch. Can do that testing, but wanted to check back whether you would even consider to feature a GitHub Action or GitLab CI Template.
Sure, I will then vendor these from the project I'm currently working on and offer them from there. I doubt that many users will want to trust an additional party for validating their documents, but the code will be public and can be copied by whoever likes to. We do not need to build or package Docker images, this runs on I did not include an Action to to manage the Action's release cycle, which after hearing your feedback would be useful to have. (e.g. GitHub automatically cuts a new release tag when the default branch changed, unless the self-test fails, then it would flag the issue (and notify somebody?).
Thanks for all the insight and your feedback 🤩 If you suggest to chat about this in a call, please feel free to send an email/invite to stefan@gronke.net - I'll be behind the screen for the next couple more hours. |
|
Thanks Stefan - won't happen today. I first need to discuss it with KoSIT and next week I am on vacation ... |
This adds first-class CI support for the validator, so any project can validate its XML documents against a validation configuration as part of a pipeline, using the released, unmodified validator jar.
One engine, two platforms
scripts/validate.shcontains the whole logic once: download the released standalone jar and the configuration, verify them against optional SHA-256 pins, validate every document, printPASS/FAILper file with the failing rule ids, fail the job on any rejection, and expose the reports plusaccepted/rejectedcounts. The GitHub Action (action.yml) is a thin composite mapping its inputs onto the script's environment; the GitLab template (gitlab/validator.gitlab-ci.yml) fetches the same script from this repository atKOSIT_SCRIPT_REFand drives it with the same variables. Both platforms therefore run identical, independently reviewable logic; there is no second copy to keep in sync.Sealed execution
Validators process untrusted documents, and CI machines hold credentials.
The action therefore executes the validator inside a container when Docker is available (the
sealedinput:autoby default,always,never): aneclipse-temurin:21-jrecontainer with no network access, a read-only root filesystem, all capabilities dropped and the caller's uid. Only the jar, the configuration and a scratch directory are mounted. Downloads always happen outside the container, so the validator itself never touches the network. Withsealed: alwaysthe host JVM setup is skipped entirely.Runtime policy: pin the contract, float the patch level
The released jars are the checksummed artifacts; the container image is a security-patch channel. The default image tag
eclipse-temurin:21-jrepins the Java 21 JRE contract and floats the patch level, so JRE and OS security updates arrive without a change to this repository. That matters for a repository that does not receive frequent maintenance commits.Consumers with a stricter supply-chain policy pass a digest-pinned reference through the
imageinput (KOSIT_IMAGEon GitLab). The GitLab job image is the same runtime, giving both platforms an identical execution environment.Development and self-test
A
.devcontainer/provides the matching Maven and Temurin 21 setup for working on the validator itself. The action's self-test validates a conformant XRechnung reference instance from the official test suite and asserts that a corrupted copy is rejected, through the sealed path (default-image fallback and explicit image override) and the host JVM path.Compatibility
No change to the Java build, the Maven artifacts or the release layout; everything added is additive (
action.yml,scripts/,gitlab/,.devcontainer/, one README section).#181 proposes a minimal variant of the same idea, though a compartmentalized approach using sealed Docker images is advisable. This requires Docker to be available in the runner, which is why I'd like to provide both alternatives for review and feedback.