Replies: 4 comments 1 reply
|
Thanks Ben - this is really great. Specifically on the entrypoint issue: My conclusion was the guidance for anyone writing a TES complaint container needs some guidance like:
I think that would be true? I don't think anyone provides guidance on writing TES containers, and it's annoying because as @beforan mentioned, entrypoint is a useful feature of the OCI standard, that anyone who writes containers uses all the time. As we care deeply about analysis on containers, I would say we could do the job of how to write one that is compliant, to save this issue arising. Because you can see the headache now - we sell the future on building around a standard, and it turns out it's implemented differently and my TRE can't run your analysis. So - is it ambitious we ask for change? From the GA4GH TES standard, or Funnel as implementers? This also feels generally interesting - I don't know if there is a paper in this? |
|
Hi, one thing that struck me about this comparison is the fact that TESK makes I know a lot of tools and libraries assume they can write to |
|
the other difference is TESK will obay the RESOURCES section, Funnel is try - no big deal Bigger one is TESK tasks can be subject to Network Policies allowing finer grained control on access to other systems on the network / cluster |
Uh oh!
There was an error while loading. Please reload this page.
Context
The Task Execution Service (TES) API aims to provide a standard mechanism for executing batch computing tasks. Rather than implement a TES server ourselves we instead use one of the several off-the-shelf implementations available.
In our federated analysis experiments, we have been using (docker-based) Funnel as the task execution engine for our Five-Safes-TES deployments. Funnel is simple to install for the default Docker backend, but has a number of security concerns when interacting with sensitive data on TRE's.
An alternative implementation is TESK which mitigates the security problems of docker-based Funnel due to its Kubernetes-based implementation.
Funnel
Funnel is a Go implementation of TES that translates task messages directly into
docker runcommands. Its main appeal is simplicity - it's a single binary that runs anywhere Docker is available, with all logs in one place. Note that Funnel also supports Kubernetes as a compute backend, but our deployment uses the default Docker backend.The core security concern for TRE use with the Docker backend is that the worker process needs to talk to the Docker daemon to launch task containers. The Docker daemon runs as root, so any process that can issue commands to it has root-equivalent reach on the host.
Also, Funnel's default
RunCommandtemplate doesn't add--user,--cap-drop,--security-opt, or user-namespace flags, so containers inherit Docker's defaults - root inside the container, full default capability set, no userns remapping, as you can see in the default run template inconfig/default.goof the Funnel source code.These defaults can be overridden via Funnel's config - including switching to the Kubernetes backend - but hardening requires deliberate per-deployment configuration, so the security burden falls entirely on correct operator configuration.
TESK
TESK is a Kubernetes-native TES implementation where each job runs as a Kubernetes pod. This makes it straightforward to enforce pod-level security policies via Kubernetes admission controllers - in contrast to Funnel's default Docker model.
It is composed of two components:
tesk-api- receives the TES HTTP request, converts it to a Kubernetes Job spec for the taskmaster and submits it. Written in Java.tesk-core- contains the taskmaster and filer images. The taskmaster runs inside the first Kubernetes Job and dynamically spawns the filer and executor Jobs as separate Kubernetes Jobs. Written in Python.Diagram from the TESK repository, Apache 2.0
Director-WFS
The downside of TESK is the operational overhead of running a Kubernetes cluster. The director-wfs from Swansea University Medical School is an opinionated full-stack deployment of TESK on KIND (Kubernetes IN Docker), with hardening enforced by Gatekeeper admission control, runtime detection via Falco, network policies, monitoring, and ingress management all active by default. A single bootstrap script stands up the whole cluster on macOS or Ubuntu.
KIND runs each Kubernetes node as a Docker container rather than a physical or virtual machine, keeping the infrastructure footprint minimal - a single Linux server or macOS host is enough. Note that
director-wfstargets single-server deployments rather than horizontally-scalable production clusters.High-level Security Comparison
Integrating with 5STES via deployment on Azure VM
VM Specs
Installation
We wrapped the script with our environment-specific variables. Note that we disable SSL as the TRE-agent communicates with TESK over a private network:
On a fresh install you'll also need to add your user to the docker group:
sudo usermod -aG docker $USER newgrp dockerPointing the TRE-Agent at TESK
Update
TesAPIUrlin your TRE-Agent.env:And add the TESK VM's IP to
/etc/hostson the TRE-Agent machine so the hostname resolves correctly:Testing
Successfully validated the following containerised applications / TES tasks against an Azure based deployment of 5S-TES and an Azure VM with
director-wfsdeployed on itBehavioural difference: TESK does not honour container
ENTRYPOINTAffects: any container whose Docker image declares an
ENTRYPOINTand relies on it being run automatically.When migrating workloads from Funnel to TESK, the most important thing to know is that TESK ignores a container image's
ENTRYPOINTand execscommand[0]from the TES task payload directly. This is by design — see the comment inTesKubernetesConverter.javain thetesk-apisource:For example, our SQL Runner container previously worked with Funnel using just the arguments:
On TESK this must become:
Looking at the TES-API schema definition it defines
commandasargv, withcommand[0]being the program to execute:TESK follows this strictly. Funnel's default behaviour effectively treats
commandas arguments to the image's existingENTRYPOINT— which diverges from the spec, but is convenient when working with bio tools that ship their own entrypoints. The behaviour comes from Funnel passing the command directly todocker runwithout overriding--entrypoint, rather than from a deliberate design choice; it can be changed by customising Funnel'sRunCommandtemplate to add--entrypoint "", but it's not the default. Containers written against Funnel that rely onENTRYPOINTbeing honoured will break on TESK until their TES payloads are updated to include the program name ascommand[0].Bug: .NET containers fail with a misleading OOM error under Gatekeeper
Affects: any .NET container running under a Kubernetes setup that enforces
readOnlyRootFilesystem: true(whichdirector-wfsdoes by default via Gatekeeper). Not specific to the SQL Runner — any .NET image will hit this.When running our SQL Runner container without the
DOTNET_EnableDiagnostics=0environment variable, the executor pod failed immediately with what looked like an OOM error:The pod also reported exit code
137, which typically signals an OOM kill. However, inspection of the pod showed no memory pressure - this is a red herring.What's actually happening: at startup, the .NET runtime tries to create a Unix socket at
/tmp/dotnet-diagnostic-<pid>for its diagnostic pipe (used by debuggers and profilers). Thedirector-wfsTESK stack enables Gatekeeper withreadOnlyRootFilesystem: true, which makes the entire container filesystem read-only - including/tmp. CoreCLR fails to create the socket very early in initialisation, before any user code runs, and reports0x8007000E(E_OUTOFMEMORY- a misleading error code for what is actually a filesystem write failure).This doesn't surface under Funnel because Funnel's worker mounts a writable host directory at
/tmpfor every task, so the diagnostic socket has somewhere to land.The fix is to set
DOTNET_EnableDiagnostics=0in your executor environment, which tells the runtime not to create the diagnostic pipe at all:Alternatively,
DOTNET_EnableDiagnostics=0can be baked directly into the container image viaENV DOTNET_EnableDiagnostics=0in the Dockerfile, which avoids having to include it in every TES task payload. This is the recommended approach for containers intended for TRE deployment, where diagnostic tooling is unlikely to be needed.All reactions