Merge pull request #2394 from smira/insecure-capabilities
fix: provide only available capabilities to insecure environmentmaster
commit
cbf808fb09
|
@ -8,6 +8,7 @@ import (
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
@ -1518,29 +1519,36 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
|
||||||
|
|
||||||
product := "buildkit_test"
|
product := "buildkit_test"
|
||||||
|
|
||||||
var command []string
|
command := []string{"sh", "-c", `cat /proc/self/status | grep CapEff | cut -f 2`}
|
||||||
mode := llb.SecurityModeSandbox
|
mode := llb.SecurityModeSandbox
|
||||||
var allowedEntitlements []entitlements.Entitlement
|
var allowedEntitlements []entitlements.Entitlement
|
||||||
|
var assertCaps func(caps uint64)
|
||||||
secMode := sb.Value("secmode")
|
secMode := sb.Value("secmode")
|
||||||
if secMode == securitySandbox {
|
if secMode == securitySandbox {
|
||||||
/*
|
assertCaps = func(caps uint64) {
|
||||||
$ capsh --decode=00000000a80425fb
|
/*
|
||||||
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
|
$ capsh --decode=00000000a80425fb
|
||||||
cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
|
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
|
||||||
*/
|
cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
|
||||||
command = []string{"sh", "-c", `cat /proc/self/status | grep CapEff | grep "00000000a80425fb"`}
|
*/
|
||||||
|
require.EqualValues(t, 0xa80425fb, caps)
|
||||||
|
}
|
||||||
allowedEntitlements = []entitlements.Entitlement{}
|
allowedEntitlements = []entitlements.Entitlement{}
|
||||||
} else {
|
} else {
|
||||||
skipDockerd(t, sb)
|
skipDockerd(t, sb)
|
||||||
/*
|
assertCaps = func(caps uint64) {
|
||||||
$ capsh --decode=0000003fffffffff
|
/*
|
||||||
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
|
$ capsh --decode=0000003fffffffff
|
||||||
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,
|
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
|
||||||
cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
|
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,
|
||||||
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
|
cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
|
||||||
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
|
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
|
||||||
*/
|
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
|
||||||
command = []string{"sh", "-c", `cat /proc/self/status | grep CapEff | grep "0000003fffffffff"`}
|
*/
|
||||||
|
|
||||||
|
// require that _at least_ minimum capabilities are granted
|
||||||
|
require.EqualValues(t, 0x3fffffffff, caps&0x3fffffffff)
|
||||||
|
}
|
||||||
mode = llb.SecurityModeInsecure
|
mode = llb.SecurityModeInsecure
|
||||||
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
|
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
|
||||||
}
|
}
|
||||||
|
@ -1594,6 +1602,11 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
capsValue, err := strconv.ParseUint(strings.TrimSpace(stdout.String()), 16, 64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assertCaps(capsValue)
|
||||||
|
|
||||||
return &client.Result{}, nil
|
return &client.Result{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -676,29 +677,36 @@ func testPushByDigest(t *testing.T, sb integration.Sandbox) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSecurityMode(t *testing.T, sb integration.Sandbox) {
|
func testSecurityMode(t *testing.T, sb integration.Sandbox) {
|
||||||
var command string
|
command := `sh -c 'cat /proc/self/status | grep CapEff | cut -f 2 > /out'`
|
||||||
mode := llb.SecurityModeSandbox
|
mode := llb.SecurityModeSandbox
|
||||||
var allowedEntitlements []entitlements.Entitlement
|
var allowedEntitlements []entitlements.Entitlement
|
||||||
|
var assertCaps func(caps uint64)
|
||||||
secMode := sb.Value("secmode")
|
secMode := sb.Value("secmode")
|
||||||
if secMode == securitySandbox {
|
if secMode == securitySandbox {
|
||||||
/*
|
assertCaps = func(caps uint64) {
|
||||||
$ capsh --decode=00000000a80425fb
|
/*
|
||||||
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
|
$ capsh --decode=00000000a80425fb
|
||||||
cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
|
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap,
|
||||||
*/
|
cap_net_bind_service,cap_net_raw,cap_sys_chroot,cap_mknod,cap_audit_write,cap_setfcap
|
||||||
command = `sh -c 'cat /proc/self/status | grep CapEff | grep "00000000a80425fb"'`
|
*/
|
||||||
|
require.EqualValues(t, 0xa80425fb, caps)
|
||||||
|
}
|
||||||
allowedEntitlements = []entitlements.Entitlement{}
|
allowedEntitlements = []entitlements.Entitlement{}
|
||||||
} else {
|
} else {
|
||||||
skipDockerd(t, sb)
|
skipDockerd(t, sb)
|
||||||
/*
|
assertCaps = func(caps uint64) {
|
||||||
$ capsh --decode=0000003fffffffff
|
/*
|
||||||
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
|
$ capsh --decode=0000003fffffffff
|
||||||
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,
|
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
|
||||||
cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
|
cap_setuid,cap_setpcap,cap_linux_immutable,cap_net_bind_service,cap_net_broadcast,cap_net_admin,cap_net_raw,
|
||||||
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
|
cap_ipc_lock,cap_ipc_owner,cap_sys_module,cap_sys_rawio,cap_sys_chroot,cap_sys_ptrace,cap_sys_pacct,cap_sys_admin,
|
||||||
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
|
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write,
|
||||||
*/
|
cap_audit_control,cap_setfcap,cap_mac_override,cap_mac_admin,cap_syslog,cap_wake_alarm,cap_block_suspend,cap_audit_read
|
||||||
command = `sh -c 'cat /proc/self/status | grep CapEff | grep "0000003fffffffff"'`
|
*/
|
||||||
|
|
||||||
|
// require that _at least_ minimum capabilities are granted
|
||||||
|
require.EqualValues(t, 0x3fffffffff, caps&0x3fffffffff)
|
||||||
|
}
|
||||||
mode = llb.SecurityModeInsecure
|
mode = llb.SecurityModeInsecure
|
||||||
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
|
allowedEntitlements = []entitlements.Entitlement{entitlements.EntitlementSecurityInsecure}
|
||||||
}
|
}
|
||||||
|
@ -714,11 +722,31 @@ func testSecurityMode(t *testing.T, sb integration.Sandbox) {
|
||||||
def, err := st.Marshal(sb.Context())
|
def, err := st.Marshal(sb.Context())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
destDir, err := ioutil.TempDir("", "buildkit")
|
||||||
|
require.NoError(t, err)
|
||||||
|
defer os.RemoveAll(destDir)
|
||||||
|
|
||||||
_, err = c.Solve(sb.Context(), def, SolveOpt{
|
_, err = c.Solve(sb.Context(), def, SolveOpt{
|
||||||
|
Exports: []ExportEntry{
|
||||||
|
{
|
||||||
|
Type: ExporterLocal,
|
||||||
|
OutputDir: destDir,
|
||||||
|
},
|
||||||
|
},
|
||||||
AllowedEntitlements: allowedEntitlements,
|
AllowedEntitlements: allowedEntitlements,
|
||||||
}, nil)
|
}, nil)
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
contents, err := ioutil.ReadFile(filepath.Join(destDir, "out"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
caps, err := strconv.ParseUint(strings.TrimSpace(string(contents)), 16, 64)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
t.Logf("Caps: %x", caps)
|
||||||
|
|
||||||
|
assertCaps(caps)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSecurityModeSysfs(t *testing.T, sb integration.Sandbox) {
|
func testSecurityModeSysfs(t *testing.T, sb integration.Sandbox) {
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
//go:build dfrunsecurity
|
||||||
// +build dfrunsecurity
|
// +build dfrunsecurity
|
||||||
|
|
||||||
package dockerfile
|
package dockerfile
|
||||||
|
@ -93,7 +94,7 @@ func testRunSecurityInsecure(t *testing.T, sb integration.Sandbox) {
|
||||||
|
|
||||||
dockerfile := []byte(`
|
dockerfile := []byte(`
|
||||||
FROM busybox
|
FROM busybox
|
||||||
RUN --security=insecure [ "$(cat /proc/self/status | grep CapBnd)" == "CapBnd: 0000003fffffffff" ]
|
RUN --security=insecure [ "$(printf '%x' $(( $(cat /proc/self/status | grep CapBnd | cut -f 2 | sed s#^#0x#) & 0x3fffffffff)))" == "3fffffffff" ]
|
||||||
RUN [ "$(cat /proc/self/status | grep CapBnd)" == "CapBnd: 00000000a80425fb" ]
|
RUN [ "$(cat /proc/self/status | grep CapBnd)" == "CapBnd: 00000000a80425fb" ]
|
||||||
`)
|
`)
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,11 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"github.com/containerd/containerd/containers"
|
"github.com/containerd/containerd/containers"
|
||||||
"github.com/containerd/containerd/oci"
|
"github.com/containerd/containerd/oci"
|
||||||
|
"github.com/containerd/containerd/pkg/cap"
|
||||||
"github.com/containerd/containerd/pkg/userns"
|
"github.com/containerd/containerd/pkg/userns"
|
||||||
specs "github.com/opencontainers/runtime-spec/specs-go"
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -17,46 +19,11 @@ import (
|
||||||
// WithInsecureSpec sets spec with All capability.
|
// WithInsecureSpec sets spec with All capability.
|
||||||
func WithInsecureSpec() oci.SpecOpts {
|
func WithInsecureSpec() oci.SpecOpts {
|
||||||
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
|
return func(_ context.Context, _ oci.Client, _ *containers.Container, s *specs.Spec) error {
|
||||||
addCaps := []string{
|
addCaps, err := getAllCaps()
|
||||||
"CAP_FSETID",
|
if err != nil {
|
||||||
"CAP_KILL",
|
return err
|
||||||
"CAP_FOWNER",
|
|
||||||
"CAP_MKNOD",
|
|
||||||
"CAP_CHOWN",
|
|
||||||
"CAP_DAC_OVERRIDE",
|
|
||||||
"CAP_NET_RAW",
|
|
||||||
"CAP_SETGID",
|
|
||||||
"CAP_SETUID",
|
|
||||||
"CAP_SETPCAP",
|
|
||||||
"CAP_SETFCAP",
|
|
||||||
"CAP_NET_BIND_SERVICE",
|
|
||||||
"CAP_SYS_CHROOT",
|
|
||||||
"CAP_AUDIT_WRITE",
|
|
||||||
"CAP_MAC_ADMIN",
|
|
||||||
"CAP_MAC_OVERRIDE",
|
|
||||||
"CAP_DAC_READ_SEARCH",
|
|
||||||
"CAP_SYS_PTRACE",
|
|
||||||
"CAP_SYS_MODULE",
|
|
||||||
"CAP_SYSLOG",
|
|
||||||
"CAP_SYS_RAWIO",
|
|
||||||
"CAP_SYS_ADMIN",
|
|
||||||
"CAP_LINUX_IMMUTABLE",
|
|
||||||
"CAP_SYS_BOOT",
|
|
||||||
"CAP_SYS_NICE",
|
|
||||||
"CAP_SYS_PACCT",
|
|
||||||
"CAP_SYS_TTY_CONFIG",
|
|
||||||
"CAP_SYS_TIME",
|
|
||||||
"CAP_WAKE_ALARM",
|
|
||||||
"CAP_AUDIT_READ",
|
|
||||||
"CAP_AUDIT_CONTROL",
|
|
||||||
"CAP_SYS_RESOURCE",
|
|
||||||
"CAP_BLOCK_SUSPEND",
|
|
||||||
"CAP_IPC_LOCK",
|
|
||||||
"CAP_IPC_OWNER",
|
|
||||||
"CAP_LEASE",
|
|
||||||
"CAP_NET_ADMIN",
|
|
||||||
"CAP_NET_BROADCAST",
|
|
||||||
}
|
}
|
||||||
|
|
||||||
s.Process.Capabilities.Bounding = append(s.Process.Capabilities.Bounding, addCaps...)
|
s.Process.Capabilities.Bounding = append(s.Process.Capabilities.Bounding, addCaps...)
|
||||||
s.Process.Capabilities.Ambient = append(s.Process.Capabilities.Ambient, addCaps...)
|
s.Process.Capabilities.Ambient = append(s.Process.Capabilities.Ambient, addCaps...)
|
||||||
s.Process.Capabilities.Effective = append(s.Process.Capabilities.Effective, addCaps...)
|
s.Process.Capabilities.Effective = append(s.Process.Capabilities.Effective, addCaps...)
|
||||||
|
@ -160,3 +127,76 @@ func getFreeLoopID() (int, error) {
|
||||||
}
|
}
|
||||||
return 0, errors.Errorf("error getting free loop device: %v", uerr)
|
return 0, errors.Errorf("error getting free loop device: %v", uerr)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
currentCaps []string
|
||||||
|
currentCapsErr error
|
||||||
|
currentCapsOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
func getCurrentCaps() ([]string, error) {
|
||||||
|
currentCapsOnce.Do(func() {
|
||||||
|
currentCaps, currentCapsErr = cap.Current()
|
||||||
|
})
|
||||||
|
|
||||||
|
return currentCaps, currentCapsErr
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAllCaps() ([]string, error) {
|
||||||
|
availableCaps, err := getCurrentCaps()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("error getting current capabilities: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// see if any of the base linux35Caps are not available to be granted
|
||||||
|
// they are either not supported by the kernel or dropped at the process level
|
||||||
|
for _, cap := range availableCaps {
|
||||||
|
if _, exists := linux35Caps[cap]; !exists {
|
||||||
|
logrus.Warnf("capability %s could not be granted for insecure mode", cap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return availableCaps, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// linux35Caps provides a list of capabilities available on Linux 3.5 kernel
|
||||||
|
var linux35Caps = map[string]struct{}{
|
||||||
|
"CAP_FSETID": {},
|
||||||
|
"CAP_KILL": {},
|
||||||
|
"CAP_FOWNER": {},
|
||||||
|
"CAP_MKNOD": {},
|
||||||
|
"CAP_CHOWN": {},
|
||||||
|
"CAP_DAC_OVERRIDE": {},
|
||||||
|
"CAP_NET_RAW": {},
|
||||||
|
"CAP_SETGID": {},
|
||||||
|
"CAP_SETUID": {},
|
||||||
|
"CAP_SETPCAP": {},
|
||||||
|
"CAP_SETFCAP": {},
|
||||||
|
"CAP_NET_BIND_SERVICE": {},
|
||||||
|
"CAP_SYS_CHROOT": {},
|
||||||
|
"CAP_AUDIT_WRITE": {},
|
||||||
|
"CAP_MAC_ADMIN": {},
|
||||||
|
"CAP_MAC_OVERRIDE": {},
|
||||||
|
"CAP_DAC_READ_SEARCH": {},
|
||||||
|
"CAP_SYS_PTRACE": {},
|
||||||
|
"CAP_SYS_MODULE": {},
|
||||||
|
"CAP_SYSLOG": {},
|
||||||
|
"CAP_SYS_RAWIO": {},
|
||||||
|
"CAP_SYS_ADMIN": {},
|
||||||
|
"CAP_LINUX_IMMUTABLE": {},
|
||||||
|
"CAP_SYS_BOOT": {},
|
||||||
|
"CAP_SYS_NICE": {},
|
||||||
|
"CAP_SYS_PACCT": {},
|
||||||
|
"CAP_SYS_TTY_CONFIG": {},
|
||||||
|
"CAP_SYS_TIME": {},
|
||||||
|
"CAP_WAKE_ALARM": {},
|
||||||
|
"CAP_AUDIT_READ": {},
|
||||||
|
"CAP_AUDIT_CONTROL": {},
|
||||||
|
"CAP_SYS_RESOURCE": {},
|
||||||
|
"CAP_BLOCK_SUSPEND": {},
|
||||||
|
"CAP_IPC_LOCK": {},
|
||||||
|
"CAP_IPC_OWNER": {},
|
||||||
|
"CAP_LEASE": {},
|
||||||
|
"CAP_NET_ADMIN": {},
|
||||||
|
"CAP_NET_BROADCAST": {},
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue