jenkinsci / jenkinsci/git-client-plugin

`fixSELinuxLabel()` relabels SSH key files to `ssh_home_t`, breaking access for ssh running under `container_t

Open
#1,764 5 comments 1 reaction 0 assignees View on GitHub
Dominant language
Java
Stars
152
Forks
402
Avg merge
6h 47m
Merged PRs (30d)
4

Description

### Jenkins and plugins versions report

Newest LTS Jenkins version and Newest git-client-plugin

### What Operating System are you using (both controller, and any agents involved in the problem)?

RHEL 9
RKE 2 1.34.6

### Reproduction steps

Inside a Jenkins container (controller or agent):

```sh
touch /tmp/test && chmod 400 /tmp/testt
ls -lZ /tmp/test # container_file_t readable
chcon --type=ssh_home_t /tmp/test; echo $? # exits 0
ls /tmp/test # Permission denied
```

After the relabel succeeds, the calling process can no longer access its own file. `ls -l` from the calling context shows `??????????` for the file mode, because `stat()` is denied.

When the git-client-plugin runs this same chcon against the temporary key it writes to `tmp/jenkins-gitclient-ssh*.key`, the subsequent ssh invocation fails with:

```
Warning: Identity file …/jenkins-gitclient-ssh….key not accessible: Permission denied.
```

and the git operation failss

On some runs on some clusters we have also seen the relabel itself be denied by the kernel generating an AVC like:

```
type=AVC msg=audit(...): avc: denied { relabelto } for pid=... comm="chcon"
name="jenkins-gitclient-ssh...key"
scontext=system_u:system_r:container_t:s0:c290,c1002
tcontext=system_u:object_r:ssh_home_t:s0:c290,c1002
tclass=file permissive=0
```

In that case the file keeps its `container_file_t` label and the build often succeeds despite the `[WARNING] Failed (1) performing chcon helper command for SELinux` log line. Whether a given run takes the "relabel denied" path or the "relabel succeeds, subsequent reads denied" path appears to depend on the underlying filesystem and host policy root cause of the variation is still under investigaton on our side.

### Expected Results

The plugin does not relabel files to a type that the calling process cannot then read

### Actual Results

## Summary

`fixSELinuxLabel()` runs `chcon --type='ssh_home_t` on the temporary SSH key file when it detects a labeled security context. This is correct when the ssh client that will read the key runs under `sshd_t` (or another domain for which `ssh_home_t` is the expected file tyype).

When Jenkins runs inside a container under the standard `container-selinux` policy (Kubernetes pods labeled `container_t`, including RKE2, OpenShift, plain containerd/CRI-O), the ssh client invoked by git also runs as `container_t`. Under that policy:

- `container_t` can read `container_file_t` (the default workspace label).
- `container_t` cannot `getattr`/`read`/`open` files labeled `ssh_home_t`.

So relabeling the tmp key to `ssh_home_t makes it unreadable to the same process that needs to read it.

We have observed this failure on RKE2 with `rke2-selinux` and `container-selinux` installed and SELinux enforcing, with the Jenkins controller and inbound agent containers both running as `container_t`.

### Anything else?

The function currently checks:

1. `/proc/self/attr/current contains a labeled context.
2. `/sys/fs/selinux/enforce` reads as `1`.

Neither check answers the question that actually matters: is the process domain one for which `ssh_home_t` is the correct file type? A process running as `container_t` will not benefit from the relabel and will be actively hamed by it.

## Suggested fixes

1. **Add a system property to skip the relabel.** Somthing loke skipSELinuxRelabel` defaulting to `false` to preserve existing behavior, settable by operators who know their environment doesn't need or want it. Lowest-risk change and allows site self-mitigation without rebuilding base images.

2. **Detect the calling SELinux domain and skip the relabel for container domains.** Read the type from `/proc/self/attr/current`; if it's `container_t` (or matches a list of container-family domains: `container_init_t`, `container_user_t`, etc.), skip the chcon. The temp key keeps its `container_file_t` label, which `container_t` ssh can already read.

3. **Verify after relabel.** After `chcon` exits 0, do a `Files.isReadable(key)` (or equivalent) check from the plugin. If the file is no longer readable, revert the label or surface a clear error explaining what happened

4. **Document the failure mode.** Note in the plugin docs that on systems where Jenkins runs under `container_t`, the `chcon` step is at best a no-op and at worst breaks the build, and that shimming `chcon` to exit 0 inside the image is a known workaround.

## Workaround we're using

```Dockerfile
RUN printf '#!/bin/sh\nexit 0\n' > /usr/local/bin/chcon && chmod 0755 /usr/local/bin/chcon
```

Placed in both controller and agent images. Verified to fix the failure across all affected clusters.

### Are you interested in contributing a fix?

_No response_

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.