Summary
kubelogin on Linux appears to rely on Azure Identity's persistent token cache backend for interactive user flows (devicecode, interactive, ropc). In containerized environments using Docker's default seccomp profile, the required kernel keyring syscalls (keyctl, add_key) are blocked, so token persistence fails and users are prompted to re-authenticate on every kubectl command.
This works fine on macOS, but fails in a VS Code dev container / Docker container unless seccomp is loosened.
Environment
- kubelogin:
v0.2.18
- host/container OS: Ubuntu Linux in VS Code dev container
- container runtime behavior: Docker seccomp active (
Seccomp: 2)
- login mode:
devicecode
Reproduction
Inside the container:
kubelogin -v=5 get-token \
--login devicecode \
--server-id <server-id> \
--client-id <client-id> \
--tenant-id <tenant-id> \
--environment AzurePublicCloud
Observed log:
failed to create cache: persistent storage isn't available due to error "couldn't get the user keyring due to error \"operation not permitted\""
Then every subsequent kubelogin get-token / kubectl invocation prompts for device login again.
Additional evidence
Inside the container:
grep '^Seccomp:' /proc/self/status
Output:
keyctl also fails directly:
keyctl show
keyctl get_persistent @u
Output:
Using Docker/devcontainer with:
{
"runArgs": ["--security-opt", "seccomp=unconfined"]
}
makes token persistence work again, which strongly suggests the failure is due to blocked kernel keyring syscalls.
What appears to be happening
From the current implementation:
get-token enables persistent cache
- interactive credential paths call
newPersistentCache()
- that calls Azure Identity persistent cache creation
- on Linux, Azure Identity uses the kernel key retention service
--cache-dir appears to control the authentication record (auth.json), not the actual persistent token cache backend.
So in default container environments:
auth.json is still created
- persistent token cache is not
- authentication still works
- but the user gets prompted every command
Why this is a problem
This is a poor fit for common container/devcontainer workflows because Docker's default seccomp profile blocks keyctl by design.
At the moment, users appear to have only these options:
- run with
seccomp=unconfined or a custom seccomp profile that allows kernel keyring syscalls
- switch login mode entirely (e.g.
azurecli) if tenant access permits it (mine does not)
For users who need devicecode in containers, there doesn't appear to be a container-friendly fallback.
Here's how to make a custom seccomp profile based on the Docker default profile that allows the required syscalls.
However, this may be a security concern because these syscalls are not namespaced.
mkdir -p .devcontainer
curl -fsSL https://raw.githubusercontent.com/moby/moby/v28.2.2/profiles/seccomp/default.json \
-o .devcontainer/seccomp-kubelogin.json
jq '
.syscalls += [
{
"names": ["keyctl", "add_key"],
"action": "SCMP_ACT_ALLOW"
}
]
' .devcontainer/seccomp-kubelogin.json \
> .devcontainer/seccomp-kubelogin.tmp
mv .devcontainer/seccomp-kubelogin.tmp .devcontainer/seccomp-kubelogin.json
devcontainer.json
{
"securityOpt": [
"seccomp=${localWorkspaceFolder}/.devcontainer/seccomp-kubelogin.json"
]
}
Summary
kubeloginon Linux appears to rely on Azure Identity's persistent token cache backend for interactive user flows (devicecode,interactive,ropc). In containerized environments using Docker's default seccomp profile, the required kernel keyring syscalls (keyctl,add_key) are blocked, so token persistence fails and users are prompted to re-authenticate on everykubectlcommand.This works fine on macOS, but fails in a VS Code dev container / Docker container unless seccomp is loosened.
Environment
v0.2.18Seccomp: 2)devicecodeReproduction
Inside the container:
Observed log:
Then every subsequent
kubelogin get-token/kubectlinvocation prompts for device login again.Additional evidence
Inside the container:
grep '^Seccomp:' /proc/self/statusOutput:
keyctlalso fails directly:Output:
Using Docker/devcontainer with:
{ "runArgs": ["--security-opt", "seccomp=unconfined"] }makes token persistence work again, which strongly suggests the failure is due to blocked kernel keyring syscalls.
What appears to be happening
From the current implementation:
get-tokenenables persistent cachenewPersistentCache()--cache-dirappears to control the authentication record (auth.json), not the actual persistent token cache backend.So in default container environments:
auth.jsonis still createdWhy this is a problem
This is a poor fit for common container/devcontainer workflows because Docker's default seccomp profile blocks
keyctlby design.At the moment, users appear to have only these options:
seccomp=unconfinedor a custom seccomp profile that allows kernel keyring syscallsazurecli) if tenant access permits it (mine does not)For users who need
devicecodein containers, there doesn't appear to be a container-friendly fallback.Here's how to make a custom seccomp profile based on the Docker default profile that allows the required syscalls.
However, this may be a security concern because these syscalls are not namespaced.
devcontainer.json
{ "securityOpt": [ "seccomp=${localWorkspaceFolder}/.devcontainer/seccomp-kubelogin.json" ] }