mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-11 17:12:51 +00:00
add as-current-user for fn run
This commit is contained in:
@@ -5,6 +5,7 @@ package container
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os/user"
|
||||||
|
|
||||||
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
runtimeexec "sigs.k8s.io/kustomize/kyaml/fn/runtime/exec"
|
||||||
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
"sigs.k8s.io/kustomize/kyaml/fn/runtime/runtimeutil"
|
||||||
@@ -124,6 +125,8 @@ type Filter struct {
|
|||||||
runtimeutil.ContainerSpec `json:",inline" yaml:",inline"`
|
runtimeutil.ContainerSpec `json:",inline" yaml:",inline"`
|
||||||
|
|
||||||
Exec runtimeexec.Filter
|
Exec runtimeexec.Filter
|
||||||
|
|
||||||
|
UIDGID string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Filter) String() string {
|
func (c Filter) String() string {
|
||||||
@@ -167,7 +170,7 @@ func (c *Filter) getCommand() (string, []string) {
|
|||||||
"--network", string(network),
|
"--network", string(network),
|
||||||
|
|
||||||
// added security options
|
// added security options
|
||||||
"--user", c.User.String(),
|
"--user", c.UIDGID,
|
||||||
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
"--security-opt=no-new-privileges", // don't allow the user to escalate privileges
|
||||||
// note: don't make fs readonly because things like heredoc rely on writing tmp files
|
// note: don't make fs readonly because things like heredoc rely on writing tmp files
|
||||||
}
|
}
|
||||||
@@ -182,13 +185,28 @@ func (c *Filter) getCommand() (string, []string) {
|
|||||||
return "docker", a
|
return "docker", a
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewContainer returns a new container filter
|
// getUIDGID will return "nobody" if asCurrentUser is false. Otherwise
|
||||||
func NewContainer(spec runtimeutil.ContainerSpec) Filter {
|
// return "uid:gid" according to current user who runs the command.
|
||||||
f := Filter{ContainerSpec: spec}
|
func getUIDGID(asCurrentUser bool) (string, error) {
|
||||||
// default user is nobody
|
if !asCurrentUser {
|
||||||
if f.ContainerSpec.User.IsEmpty() {
|
return "nobody", nil
|
||||||
f.ContainerSpec.User = runtimeutil.UserNobody
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return f
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%s", u.Uid, u.Gid), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewContainer returns a new container filter
|
||||||
|
func NewContainer(spec runtimeutil.ContainerSpec, asCurrentUser bool) (Filter, error) {
|
||||||
|
f := Filter{ContainerSpec: spec}
|
||||||
|
u, err := getUIDGID(asCurrentUser)
|
||||||
|
if err != nil {
|
||||||
|
return f, err
|
||||||
|
}
|
||||||
|
f.UIDGID = u
|
||||||
|
|
||||||
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ package container
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os/user"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
@@ -15,11 +16,16 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func TestFilter_setupExec(t *testing.T) {
|
func TestFilter_setupExec(t *testing.T) {
|
||||||
|
u, err := user.Current()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
var tests = []struct {
|
var tests = []struct {
|
||||||
name string
|
name string
|
||||||
functionConfig string
|
functionConfig string
|
||||||
expectedArgs []string
|
expectedArgs []string
|
||||||
instance Filter
|
containerSpec runtimeutil.ContainerSpec
|
||||||
|
asCurrentUser bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "command",
|
name: "command",
|
||||||
@@ -36,12 +42,9 @@ metadata:
|
|||||||
"--user", "nobody",
|
"--user", "nobody",
|
||||||
"--security-opt=no-new-privileges",
|
"--security-opt=no-new-privileges",
|
||||||
},
|
},
|
||||||
instance: NewContainer(
|
containerSpec: runtimeutil.ContainerSpec{
|
||||||
runtimeutil.ContainerSpec{
|
|
||||||
Image: "example.com:version",
|
Image: "example.com:version",
|
||||||
User: "nobody",
|
|
||||||
},
|
},
|
||||||
),
|
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -63,9 +66,8 @@ metadata:
|
|||||||
runtimeutil.ContainerSpec{
|
runtimeutil.ContainerSpec{
|
||||||
Image: "example.com:version",
|
Image: "example.com:version",
|
||||||
Network: true,
|
Network: true,
|
||||||
User: "nobody",
|
|
||||||
},
|
},
|
||||||
),
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
@@ -87,8 +89,7 @@ metadata:
|
|||||||
"--mount", fmt.Sprintf("type=%s,source=%s,target=%s,readonly", "volume", "myvol", "/local/"),
|
"--mount", fmt.Sprintf("type=%s,source=%s,target=%s,readonly", "volume", "myvol", "/local/"),
|
||||||
"--mount", fmt.Sprintf("type=%s,source=%s,target=%s,readonly", "tmpfs", "", "/local/"),
|
"--mount", fmt.Sprintf("type=%s,source=%s,target=%s,readonly", "tmpfs", "", "/local/"),
|
||||||
},
|
},
|
||||||
instance: NewContainer(
|
containerSpec: runtimeutil.ContainerSpec{
|
||||||
runtimeutil.ContainerSpec{
|
|
||||||
Image: "example.com:version",
|
Image: "example.com:version",
|
||||||
StorageMounts: []runtimeutil.StorageMount{
|
StorageMounts: []runtimeutil.StorageMount{
|
||||||
{MountType: "bind", Src: "/mount/path", DstPath: "/local/"},
|
{MountType: "bind", Src: "/mount/path", DstPath: "/local/"},
|
||||||
@@ -96,12 +97,10 @@ metadata:
|
|||||||
{MountType: "volume", Src: "myvol", DstPath: "/local/"},
|
{MountType: "volume", Src: "myvol", DstPath: "/local/"},
|
||||||
{MountType: "tmpfs", Src: "", DstPath: "/local/"},
|
{MountType: "tmpfs", Src: "", DstPath: "/local/"},
|
||||||
},
|
},
|
||||||
User: "nobody",
|
|
||||||
},
|
},
|
||||||
),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "root user",
|
name: "as current user",
|
||||||
functionConfig: `apiVersion: apps/v1
|
functionConfig: `apiVersion: apps/v1
|
||||||
kind: Deployment
|
kind: Deployment
|
||||||
metadata:
|
metadata:
|
||||||
@@ -112,15 +111,13 @@ metadata:
|
|||||||
"--rm",
|
"--rm",
|
||||||
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
|
"-i", "-a", "STDIN", "-a", "STDOUT", "-a", "STDERR",
|
||||||
"--network", "none",
|
"--network", "none",
|
||||||
"--user", "root",
|
"--user", fmt.Sprintf("%s:%s", u.Uid, u.Gid),
|
||||||
"--security-opt=no-new-privileges",
|
"--security-opt=no-new-privileges",
|
||||||
},
|
},
|
||||||
instance: NewContainer(
|
containerSpec: runtimeutil.ContainerSpec{
|
||||||
runtimeutil.ContainerSpec{
|
|
||||||
Image: "example.com:version",
|
Image: "example.com:version",
|
||||||
User: "root",
|
|
||||||
},
|
},
|
||||||
),
|
asCurrentUser: true,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -131,18 +128,23 @@ metadata:
|
|||||||
if !assert.NoError(t, err) {
|
if !assert.NoError(t, err) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
tt.instance.Exec.FunctionConfig = cfg
|
|
||||||
tt.instance.Env = append(tt.instance.Env, "KYAML_TEST=FOO")
|
instance, err := NewContainer(tt.containerSpec, tt.asCurrentUser)
|
||||||
tt.instance.setupExec()
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
instance.Exec.FunctionConfig = cfg
|
||||||
|
instance.Env = append(instance.Env, "KYAML_TEST=FOO")
|
||||||
|
instance.setupExec()
|
||||||
|
|
||||||
tt.expectedArgs = append(tt.expectedArgs,
|
tt.expectedArgs = append(tt.expectedArgs,
|
||||||
runtimeutil.NewContainerEnvFromStringSlice(tt.instance.Env).GetDockerFlags()...)
|
runtimeutil.NewContainerEnvFromStringSlice(instance.Env).GetDockerFlags()...)
|
||||||
tt.expectedArgs = append(tt.expectedArgs, tt.instance.Image)
|
tt.expectedArgs = append(tt.expectedArgs, instance.Image)
|
||||||
|
|
||||||
if !assert.Equal(t, "docker", tt.instance.Exec.Path) {
|
if !assert.Equal(t, "docker", instance.Exec.Path) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
if !assert.Equal(t, tt.expectedArgs, tt.instance.Exec.Args) {
|
if !assert.Equal(t, tt.expectedArgs, instance.Exec.Args) {
|
||||||
t.FailNow()
|
t.FailNow()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -19,21 +19,6 @@ const (
|
|||||||
|
|
||||||
var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
|
var functionAnnotationKeys = []string{FunctionAnnotationKey, oldFunctionAnnotationKey}
|
||||||
|
|
||||||
// ContainerUser is a type for username/uid used in container
|
|
||||||
type ContainerUser string
|
|
||||||
|
|
||||||
func (u *ContainerUser) String() string {
|
|
||||||
return string(*u)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (u *ContainerUser) IsEmpty() bool {
|
|
||||||
return string(*u) == ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
|
||||||
UserNobody ContainerUser = "nobody"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ContainerNetworkName is a type for network name used in container
|
// ContainerNetworkName is a type for network name used in container
|
||||||
type ContainerNetworkName string
|
type ContainerNetworkName string
|
||||||
|
|
||||||
@@ -171,9 +156,6 @@ type ContainerSpec struct {
|
|||||||
// Mounts are the storage or directories to mount into the container
|
// Mounts are the storage or directories to mount into the container
|
||||||
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
StorageMounts []StorageMount `json:"mounts,omitempty" yaml:"mounts,omitempty"`
|
||||||
|
|
||||||
// User is the username/uid that application runs as in continer
|
|
||||||
User ContainerUser `json:"user,omitempty" yaml:"user,omitempty"`
|
|
||||||
|
|
||||||
// Env is a slice of env string that will be exposed to container
|
// Env is a slice of env string that will be exposed to container
|
||||||
Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
|
Env []string `json:"envs,omitempty" yaml:"envs,omitempty"`
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ type RunFns struct {
|
|||||||
functionFilterProvider func(
|
functionFilterProvider func(
|
||||||
filter runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter, error)
|
filter runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter, error)
|
||||||
|
|
||||||
// User username used to run the application in container,
|
// AsCurrentUser is a boolean to indicate whether docker container should use
|
||||||
User runtimeutil.ContainerUser
|
// the uid and gid that run the command
|
||||||
|
AsCurrentUser bool
|
||||||
|
|
||||||
// Env contains environment variables that will be exported to container
|
// Env contains environment variables that will be exported to container
|
||||||
Env []string
|
Env []string
|
||||||
@@ -299,10 +300,7 @@ func (r RunFns) getFunctionFilters(global bool, fns ...*yaml.RNode) (
|
|||||||
// TODO(eddiezane): Provide error info about which function needs the network
|
// TODO(eddiezane): Provide error info about which function needs the network
|
||||||
return fltrs, errors.Errorf("network required but not enabled with --network")
|
return fltrs, errors.Errorf("network required but not enabled with --network")
|
||||||
}
|
}
|
||||||
// command line username and envs has higher priority
|
// merge envs from imperative and declarative
|
||||||
if !r.User.IsEmpty() {
|
|
||||||
spec.Container.User = r.User
|
|
||||||
}
|
|
||||||
spec.Container.Env = r.mergeContainerEnv(spec.Container.Env)
|
spec.Container.Env = r.mergeContainerEnv(spec.Container.Env)
|
||||||
|
|
||||||
c, err := r.functionFilterProvider(*spec, api)
|
c, err := r.functionFilterProvider(*spec, api)
|
||||||
@@ -407,13 +405,18 @@ func (r *RunFns) ffp(spec runtimeutil.FunctionSpec, api *yaml.RNode) (kio.Filter
|
|||||||
}
|
}
|
||||||
if !r.DisableContainers && spec.Container.Image != "" {
|
if !r.DisableContainers && spec.Container.Image != "" {
|
||||||
// TODO: Add a test for this behavior
|
// TODO: Add a test for this behavior
|
||||||
c := container.NewContainer(runtimeutil.ContainerSpec{
|
c, err := container.NewContainer(
|
||||||
|
runtimeutil.ContainerSpec{
|
||||||
Image: spec.Container.Image,
|
Image: spec.Container.Image,
|
||||||
Network: spec.Container.Network,
|
Network: spec.Container.Network,
|
||||||
StorageMounts: r.StorageMounts,
|
StorageMounts: r.StorageMounts,
|
||||||
User: spec.Container.User,
|
|
||||||
Env: spec.Container.Env,
|
Env: spec.Container.Env,
|
||||||
})
|
},
|
||||||
|
r.AsCurrentUser,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
cf := &c
|
cf := &c
|
||||||
cf.Exec.FunctionConfig = api
|
cf.Exec.FunctionConfig = api
|
||||||
cf.Exec.GlobalScope = r.GlobalScope
|
cf.Exec.GlobalScope = r.GlobalScope
|
||||||
|
|||||||
@@ -60,7 +60,10 @@ kind:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
filter, _ := instance.functionFilterProvider(spec, api)
|
filter, _ := instance.functionFilterProvider(spec, api)
|
||||||
c := container.NewContainer(runtimeutil.ContainerSpec{Image: "example.com:version"})
|
c, err := container.NewContainer(runtimeutil.ContainerSpec{Image: "example.com:version"}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
cf := &c
|
cf := &c
|
||||||
cf.Exec.FunctionConfig = api
|
cf.Exec.FunctionConfig = api
|
||||||
assert.Equal(t, cf, filter)
|
assert.Equal(t, cf, filter)
|
||||||
@@ -91,7 +94,10 @@ kind:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
filter, _ := instance.functionFilterProvider(spec, api)
|
filter, _ := instance.functionFilterProvider(spec, api)
|
||||||
c := container.NewContainer(runtimeutil.ContainerSpec{Image: "example.com:version"})
|
c, err := container.NewContainer(runtimeutil.ContainerSpec{Image: "example.com:version"}, false)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
cf := &c
|
cf := &c
|
||||||
cf.Exec.FunctionConfig = api
|
cf.Exec.FunctionConfig = api
|
||||||
cf.Exec.GlobalScope = true
|
cf.Exec.GlobalScope = true
|
||||||
|
|||||||
Reference in New Issue
Block a user