Merge pull request #2394 from smira/insecure-capabilities

fix: provide only available capabilities to insecure environment
master
Tõnis Tiigi 2021-10-15 17:08:29 -07:00 committed by GitHub
commit cbf808fb09
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 154 additions and 72 deletions

View File

@ -8,6 +8,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
"path/filepath" "path/filepath"
"strconv"
"strings" "strings"
"testing" "testing"
"time" "time"
@ -1518,20 +1519,24 @@ 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 $ capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap, 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 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 $ capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid, 0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
@ -1540,7 +1545,10 @@ func testClientGatewayContainerSecurityMode(t *testing.T, sb integration.Sandbox
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write, 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 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
} }

View File

@ -17,6 +17,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"runtime" "runtime"
"strconv"
"strings" "strings"
"syscall" "syscall"
"testing" "testing"
@ -676,20 +677,24 @@ 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 $ capsh --decode=00000000a80425fb
0x00000000a80425fb=cap_chown,cap_dac_override,cap_fowner,cap_fsetid,cap_kill,cap_setgid,cap_setuid,cap_setpcap, 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 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 $ capsh --decode=0000003fffffffff
0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid, 0x0000003fffffffff=cap_chown,cap_dac_override,cap_dac_read_search,cap_fowner,cap_fsetid,cap_kill,cap_setgid,
@ -698,7 +703,10 @@ func testSecurityMode(t *testing.T, sb integration.Sandbox) {
cap_sys_boot,cap_sys_nice,cap_sys_resource,cap_sys_time,cap_sys_tty_config,cap_mknod,cap_lease,cap_audit_write, 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 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) {

View File

@ -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" ]
`) `)

View File

@ -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": {},
}