Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/bpm/runc/adapter/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,11 @@ func (a *RuncAdapter) BuildSpec(
specbuilder.Apply(spec, specbuilder.WithNamespace("pid"))
}

// Disable seccomp if not supported (e.g., architecture emulation)
if !a.features.SeccompSupported {
specbuilder.Apply(spec, specbuilder.WithoutSeccomp())
}

if procCfg.Unsafe != nil && procCfg.Unsafe.Privileged {
specbuilder.Apply(spec, specbuilder.WithPrivileged())
}
Expand Down
84 changes: 84 additions & 0 deletions src/bpm/runc/adapter/adapter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -816,6 +816,90 @@ var _ = Describe("RuncAdapter", func() {
})
})

Context("when seccomp is not supported", func() {
BeforeEach(func() {
features.SeccompSupported = false
identityGlob := func(pattern string) ([]string, error) {
return []string{pattern}, nil
}
runcAdapter = NewRuncAdapter(features, identityGlob, mountSharer.MakeShared, volumeLocker)
})

It("disables seccomp in the spec", func() {
spec, err := runcAdapter.BuildSpec(logger, bpmCfg, procCfg, user)
Expect(err).NotTo(HaveOccurred())
Expect(spec.Linux.Seccomp).To(BeNil())
})

It("does not affect other security features", func() {
spec, err := runcAdapter.BuildSpec(logger, bpmCfg, procCfg, user)
Expect(err).NotTo(HaveOccurred())

// User should still be the unprivileged user
Expect(spec.Process.User).To(Equal(user))

// NoNewPrivileges should still be true
Expect(spec.Process.NoNewPrivileges).To(BeTrue())

// Masked and readonly paths should still be set
Expect(spec.Linux.MaskedPaths).NotTo(BeEmpty())
Expect(spec.Linux.ReadonlyPaths).NotTo(BeEmpty())

// Capabilities should still be limited
Expect(spec.Process.Capabilities.Bounding).To(Equal([]string{"CAP_TAIN", "CAP_SAICIN"}))

// Mounts should still have nosuid
var hasMountWithNosuid bool
for _, mount := range spec.Mounts {
for _, opt := range mount.Options {
if opt == "nosuid" {
hasMountWithNosuid = true
break
}
}
}
Expect(hasMountWithNosuid).To(BeTrue())
})

Context("when privileged mode is also enabled", func() {
BeforeEach(func() {
procCfg.Unsafe = &config.Unsafe{Privileged: true}
})

It("privileged mode takes precedence", func() {
spec, err := runcAdapter.BuildSpec(logger, bpmCfg, procCfg, user)
Expect(err).NotTo(HaveOccurred())

// Seccomp should still be nil
Expect(spec.Linux.Seccomp).To(BeNil())

// But other privileged settings should apply
Expect(spec.Process.User).To(Equal(specs.User{UID: 0, GID: 0}))
Expect(spec.Process.NoNewPrivileges).To(BeFalse())
Expect(spec.Linux.MaskedPaths).To(Equal([]string{}))
Expect(spec.Linux.ReadonlyPaths).To(Equal([]string{}))
})
})
})

Context("when seccomp is supported", func() {
BeforeEach(func() {
features.SeccompSupported = true
identityGlob := func(pattern string) ([]string, error) {
return []string{pattern}, nil
}
runcAdapter = NewRuncAdapter(features, identityGlob, mountSharer.MakeShared, volumeLocker)
})

It("includes seccomp in the spec", func() {
spec, err := runcAdapter.BuildSpec(logger, bpmCfg, procCfg, user)
Expect(err).NotTo(HaveOccurred())
Expect(spec.Linux.Seccomp).NotTo(BeNil())
Expect(spec.Linux.Seccomp.Architectures).NotTo(BeEmpty())
Expect(spec.Linux.Seccomp.Syscalls).NotTo(BeEmpty())
})
})

Context("when the user requests a privileged container", func() {
BeforeEach(func() {
procCfg.Unsafe = &config.Unsafe{Privileged: true}
Expand Down
11 changes: 11 additions & 0 deletions src/bpm/runc/specbuilder/specbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,17 @@ var RootUser = specs.User{
GID: 0,
}

// WithoutSeccomp disables seccomp filtering. This is needed when running
// in environments where seccomp BPF filters cannot be loaded (e.g., when
// running x86_64 binaries on an ARM64 kernel via Rosetta emulation).
// Unlike WithPrivileged(), this only disables seccomp without granting
// additional privileges or removing other security features.
func WithoutSeccomp() SpecOption {
return func(spec *specs.Spec) {
spec.Linux.Seccomp = nil
}
}

func WithPrivileged() SpecOption {
return func(spec *specs.Spec) {
Apply(spec, WithCapabilities(DefaultPrivilegedCapabilities()))
Expand Down
145 changes: 145 additions & 0 deletions src/bpm/runc/specbuilder/specbuilder_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
// Copyright (C) 2018-Present CloudFoundry.org Foundation, Inc. All rights reserved.
//
// This program and the accompanying materials are made available under
// the terms of the under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
//
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
// License for the specific language governing permissions and limitations
// under the License.

package specbuilder_test

import (
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
specs "github.com/opencontainers/runtime-spec/specs-go"

"bpm/runc/specbuilder"
)

func TestSpecbuilder(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Specbuilder Suite")
}

var _ = Describe("SpecBuilder", func() {
Describe("WithoutSeccomp", func() {
var spec *specs.Spec

BeforeEach(func() {
spec = specbuilder.DefaultSpec()
})

It("removes seccomp from the spec", func() {
Expect(spec.Linux.Seccomp).NotTo(BeNil())

specbuilder.Apply(spec, specbuilder.WithoutSeccomp())

Expect(spec.Linux.Seccomp).To(BeNil())
})

It("does not affect other security features", func() {
// Capture original values
originalNoNewPrivileges := spec.Process.NoNewPrivileges
originalMaskedPaths := spec.Linux.MaskedPaths
originalReadonlyPaths := spec.Linux.ReadonlyPaths
originalUser := spec.Process.User

specbuilder.Apply(spec, specbuilder.WithoutSeccomp())

// Verify other security features are unchanged
Expect(spec.Process.NoNewPrivileges).To(Equal(originalNoNewPrivileges))
Expect(spec.Linux.MaskedPaths).To(Equal(originalMaskedPaths))
Expect(spec.Linux.ReadonlyPaths).To(Equal(originalReadonlyPaths))
Expect(spec.Process.User).To(Equal(originalUser))
})

It("does not affect capabilities", func() {
// Add some capabilities
specbuilder.Apply(spec, specbuilder.WithCapabilities([]string{"CAP_NET_BIND_SERVICE"}))

originalCaps := spec.Process.Capabilities

specbuilder.Apply(spec, specbuilder.WithoutSeccomp())

// Verify capabilities are unchanged
Expect(spec.Process.Capabilities).To(Equal(originalCaps))
})

It("does not affect user settings", func() {
testUser := specs.User{UID: 1000, GID: 1000}
specbuilder.Apply(spec, specbuilder.WithUser(testUser))

specbuilder.Apply(spec, specbuilder.WithoutSeccomp())

Expect(spec.Process.User).To(Equal(testUser))
})

It("does not affect mount options", func() {
// Check that nosuid is still present on mounts
var hasMountWithNosuid bool
for _, mount := range spec.Mounts {
for _, opt := range mount.Options {
if opt == "nosuid" {
hasMountWithNosuid = true
break
}
}
}

Expect(hasMountWithNosuid).To(BeTrue(), "Expected at least one mount to have nosuid option")

specbuilder.Apply(spec, specbuilder.WithoutSeccomp())

// Verify nosuid is still present after WithoutSeccomp
hasMountWithNosuid = false
for _, mount := range spec.Mounts {
for _, opt := range mount.Options {
if opt == "nosuid" {
hasMountWithNosuid = true
break
}
}
}

Expect(hasMountWithNosuid).To(BeTrue(), "Expected nosuid to remain on mounts")
})

Context("when applied before WithPrivileged", func() {
It("WithPrivileged still removes seccomp", func() {
specbuilder.Apply(spec, specbuilder.WithoutSeccomp())
Expect(spec.Linux.Seccomp).To(BeNil())

specbuilder.Apply(spec, specbuilder.WithPrivileged())
Expect(spec.Linux.Seccomp).To(BeNil())
})
})

Context("when applied after WithPrivileged", func() {
It("seccomp remains nil", func() {
specbuilder.Apply(spec, specbuilder.WithPrivileged())
Expect(spec.Linux.Seccomp).To(BeNil())

specbuilder.Apply(spec, specbuilder.WithoutSeccomp())
Expect(spec.Linux.Seccomp).To(BeNil())
})
})
})

Describe("DefaultSpec", func() {
It("includes seccomp by default", func() {
spec := specbuilder.DefaultSpec()
Expect(spec.Linux.Seccomp).NotTo(BeNil())
Expect(spec.Linux.Seccomp.Architectures).NotTo(BeEmpty())
Expect(spec.Linux.Seccomp.Syscalls).NotTo(BeEmpty())
})
})
})
Loading
Loading