fix: provide only available capabilities to insecure environment
The problem this change is trying to fix are the environments where some capabilities are already dropped, so they can't be granted to the job with `--security=insecure`. I know that probably fixed set of capabilities was implemented to provide a stable build environment, but at the same time this breaks environments with reduced capabilities. Signed-off-by: Andrey Smirnov <andrey.smirnov@talos-systems.com>master
parent
ba673bbdab
commit
a5d1cfc1e4
|
@ -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