spiegel_podman/pkg/api/handlers/compat/containers_create_test.go
r-vdp 4ce2583ad7
compat API: accept inline seccomp profiles
Docker clients send the seccomp profile JSON itself in
HostConfig.SecurityOpt (the docker CLI reads the profile file
client-side and inlines it). The compat API treated the value as a path
to a profile file, so container create failed with "file name too
long". This breaks docker-compat tooling that passes seccomp profiles
through the socket, like Nextcloud AIO and forgejo-runner.

Add a SeccompProfile field to specgen for inline profile content,
next to the existing SeccompProfilePath, as requested in review of the
earlier PR that tried to fix this (#28985). The compat create handler
extracts inline profiles (values starting with "{") from SecurityOpt
and sets the new field. Path-based values and "unconfined" keep their
current meaning, and duplicate seccomp options resolve last-one-wins,
like docker.

Fixes: #27710

Signed-off-by: r-vdp <ramses@well-founded.dev>
2026-09-24 10:31:00 +02:00

83 lines
2.3 KiB
Go

//go:build !remote && (linux || freebsd)
package compat
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestExtractInlineSeccompProfile(t *testing.T) {
inlineProfile := `{"defaultAction":"SCMP_ACT_ALLOW"}`
tests := []struct {
name string
opts []string
expectedProfile string
expectedRest []string
}{
{
name: "no security opts",
opts: nil,
expectedProfile: "",
expectedRest: nil,
},
{
name: "path stays untouched",
opts: []string{"seccomp=/some/path.json"},
expectedProfile: "",
expectedRest: []string{"seccomp=/some/path.json"},
},
{
name: "unconfined stays untouched",
opts: []string{"seccomp=unconfined"},
expectedProfile: "",
expectedRest: []string{"seccomp=unconfined"},
},
{
name: "inline json is extracted",
opts: []string{"seccomp=" + inlineProfile},
expectedProfile: inlineProfile,
expectedRest: []string{},
},
{
name: "inline json with leading whitespace",
opts: []string{"seccomp= \n\t" + inlineProfile},
expectedProfile: " \n\t" + inlineProfile,
expectedRest: []string{},
},
{
name: "deprecated colon separator",
opts: []string{"seccomp:" + inlineProfile},
expectedProfile: inlineProfile,
expectedRest: []string{},
},
{
name: "other options are preserved",
opts: []string{"label=disable", "seccomp=" + inlineProfile, "no-new-privileges"},
expectedProfile: inlineProfile,
expectedRest: []string{"label=disable", "no-new-privileges"},
},
{
name: "duplicate, last inline wins",
opts: []string{"seccomp=/some/path.json", "seccomp=" + inlineProfile},
expectedProfile: inlineProfile,
expectedRest: []string{},
},
{
name: "duplicate, last path wins",
opts: []string{"seccomp=" + inlineProfile, "seccomp=/some/path.json"},
expectedProfile: "",
expectedRest: []string{"seccomp=/some/path.json"},
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
profile, rest := extractInlineSeccompProfile(test.opts)
assert.Equal(t, test.expectedProfile, profile)
assert.Equal(t, test.expectedRest, rest)
})
}
}