mirror of https://github.com/k3s-io/k3s
FSGroup implementation
parent
f960b05fe1
commit
3cd12f5e05
|
@ -13188,18 +13188,11 @@
|
|||
},
|
||||
"v1.PodSecurityContext": {
|
||||
"id": "v1.PodSecurityContext",
|
||||
"description": "PodSecurityContext holds pod-level security attributes and common container settings.",
|
||||
"description": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.",
|
||||
"properties": {
|
||||
"supplementalGroups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "integer"
|
||||
},
|
||||
"description": "SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container."
|
||||
},
|
||||
"seLinuxOptions": {
|
||||
"$ref": "v1.SELinuxOptions",
|
||||
"description": "SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
|
||||
"description": "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
|
||||
},
|
||||
"runAsUser": {
|
||||
"type": "integer",
|
||||
|
@ -13209,6 +13202,18 @@
|
|||
"runAsNonRoot": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
|
||||
},
|
||||
"supplementalGroups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "integer"
|
||||
},
|
||||
"description": "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container."
|
||||
},
|
||||
"fsGroup": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw "
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -3836,18 +3836,11 @@
|
|||
},
|
||||
"v1.PodSecurityContext": {
|
||||
"id": "v1.PodSecurityContext",
|
||||
"description": "PodSecurityContext holds pod-level security attributes and common container settings.",
|
||||
"description": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.",
|
||||
"properties": {
|
||||
"supplementalGroups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "integer"
|
||||
},
|
||||
"description": "SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container."
|
||||
},
|
||||
"seLinuxOptions": {
|
||||
"$ref": "v1.SELinuxOptions",
|
||||
"description": "SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
|
||||
"description": "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container."
|
||||
},
|
||||
"runAsUser": {
|
||||
"type": "integer",
|
||||
|
@ -3857,6 +3850,18 @@
|
|||
"runAsNonRoot": {
|
||||
"type": "boolean",
|
||||
"description": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence."
|
||||
},
|
||||
"supplementalGroups": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "integer"
|
||||
},
|
||||
"description": "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container."
|
||||
},
|
||||
"fsGroup": {
|
||||
"type": "integer",
|
||||
"format": "int64",
|
||||
"description": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw "
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -52,6 +52,8 @@ import (
|
|||
kubetypes "k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/master/ports"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/chmod"
|
||||
"k8s.io/kubernetes/pkg/util/chown"
|
||||
"k8s.io/kubernetes/pkg/util/io"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
nodeutil "k8s.io/kubernetes/pkg/util/node"
|
||||
|
@ -320,6 +322,9 @@ func (s *KubeletServer) UnsecuredKubeletConfig() (*KubeletConfig, error) {
|
|||
writer = &io.NsenterWriter{}
|
||||
}
|
||||
|
||||
chmodRunner := chmod.New()
|
||||
chownRunner := chown.New()
|
||||
|
||||
tlsOptions, err := s.InitializeTLS()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -393,6 +398,8 @@ func (s *KubeletServer) UnsecuredKubeletConfig() (*KubeletConfig, error) {
|
|||
MaxPods: s.MaxPods,
|
||||
MinimumGCAge: s.MinimumGCAge,
|
||||
Mounter: mounter,
|
||||
ChownRunner: chownRunner,
|
||||
ChmodRunner: chmodRunner,
|
||||
NetworkPluginName: s.NetworkPluginName,
|
||||
NetworkPlugins: ProbeNetworkPlugins(s.NetworkPluginDir),
|
||||
NodeStatusUpdateFrequency: s.NodeStatusUpdateFrequency,
|
||||
|
@ -661,6 +668,8 @@ func SimpleKubelet(client *client.Client,
|
|||
MaxPods: maxPods,
|
||||
MinimumGCAge: minimumGCAge,
|
||||
Mounter: mount.New(),
|
||||
ChownRunner: chown.New(),
|
||||
ChmodRunner: chmod.New(),
|
||||
NodeStatusUpdateFrequency: nodeStatusUpdateFrequency,
|
||||
OOMAdjuster: oom.NewFakeOOMAdjuster(),
|
||||
OSInterface: osInterface,
|
||||
|
@ -843,6 +852,8 @@ type KubeletConfig struct {
|
|||
MaxPods int
|
||||
MinimumGCAge time.Duration
|
||||
Mounter mount.Interface
|
||||
ChownRunner chown.Interface
|
||||
ChmodRunner chmod.Interface
|
||||
NetworkPluginName string
|
||||
NetworkPlugins []network.NetworkPlugin
|
||||
NodeName string
|
||||
|
@ -938,6 +949,8 @@ func CreateAndInitKubelet(kc *KubeletConfig) (k KubeletBootstrap, pc *config.Pod
|
|||
kc.RktStage1Image,
|
||||
kc.Mounter,
|
||||
kc.Writer,
|
||||
kc.ChownRunner,
|
||||
kc.ChmodRunner,
|
||||
kc.DockerDaemonContainer,
|
||||
kc.SystemContainer,
|
||||
kc.ConfigureCBR0,
|
||||
|
|
|
@ -1864,7 +1864,7 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
<div class="sect2">
|
||||
<h3 id="_v1_podsecuritycontext">v1.PodSecurityContext</h3>
|
||||
<div class="paragraph">
|
||||
<p>PodSecurityContext holds pod-level security attributes and common container settings.</p>
|
||||
<p>PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
|
@ -1885,15 +1885,8 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">supplementalGroups</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#integer">[integer]</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">seLinuxOptions</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_selinuxoptions">v1.SELinuxOptions</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
|
@ -1912,6 +1905,22 @@ Both these may change in the future. Incoming requests are matched against the h
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">supplementalGroups</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">A list of groups applied to the first process run in each container, in addition to the container’s primary GID. If unspecified, no groups will be added to any container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#integer">[integer]</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">fsGroup</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:<br>
|
||||
<br>
|
||||
1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR’d with rw-rw</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int64)</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -4238,7 +4247,7 @@ Populated by the system when a graceful deletion is requested. Read-only. More i
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2015-10-22 10:21:28 UTC
|
||||
Last updated 2015-10-22 20:40:01 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -4975,7 +4975,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2015-10-22 10:21:28 UTC
|
||||
Last updated 2015-10-22 20:40:01 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -4846,7 +4846,7 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
<div class="sect2">
|
||||
<h3 id="_v1_podsecuritycontext">v1.PodSecurityContext</h3>
|
||||
<div class="paragraph">
|
||||
<p>PodSecurityContext holds pod-level security attributes and common container settings.</p>
|
||||
<p>PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.</p>
|
||||
</div>
|
||||
<table class="tableblock frame-all grid-all" style="width:100%; ">
|
||||
<colgroup>
|
||||
|
@ -4867,15 +4867,8 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">supplementalGroups</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#integer">[integer]</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">seLinuxOptions</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#_v1_selinuxoptions">v1.SELinuxOptions</a></p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
|
@ -4894,6 +4887,22 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
<td class="tableblock halign-left valign-top"><p class="tableblock">boolean</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">supplementalGroups</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">A list of groups applied to the first process run in each container, in addition to the container’s primary GID. If unspecified, no groups will be added to any container.</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#integer">[integer]</a> array</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">fsGroup</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:<br>
|
||||
<br>
|
||||
1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR’d with rw-rw</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">false</p></td>
|
||||
<td class="tableblock halign-left valign-top"><p class="tableblock">integer (int64)</p></td>
|
||||
<td class="tableblock halign-left valign-top"></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
@ -6857,7 +6866,7 @@ The resulting set of endpoints can be viewed as:<br>
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2015-10-22 10:21:22 UTC
|
||||
Last updated 2015-10-22 20:39:55 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -23144,7 +23144,7 @@ span.icon > [class^="icon-"], span.icon > [class*=" icon-"] { cursor: default; }
|
|||
</div>
|
||||
<div id="footer">
|
||||
<div id="footer-text">
|
||||
Last updated 2015-10-22 10:21:22 UTC
|
||||
Last updated 2015-10-22 20:39:55 UTC
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
|
|
@ -1486,14 +1486,6 @@ func deepCopy_api_PodSecurityContext(in PodSecurityContext, out *PodSecurityCont
|
|||
out.HostNetwork = in.HostNetwork
|
||||
out.HostPID = in.HostPID
|
||||
out.HostIPC = in.HostIPC
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.SELinuxOptions != nil {
|
||||
out.SELinuxOptions = new(SELinuxOptions)
|
||||
if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
|
||||
|
@ -1514,6 +1506,20 @@ func deepCopy_api_PodSecurityContext(in PodSecurityContext, out *PodSecurityCont
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -998,8 +998,8 @@ type PodSpec struct {
|
|||
}
|
||||
|
||||
// PodSecurityContext holds pod-level security attributes and common container settings.
|
||||
// Some fields are also present in SecurityContext. Field values of SecurityContext take
|
||||
// precedence over field values of PodSecurityContext.
|
||||
// Some fields are also present in container.securityContext. Field values of
|
||||
// container.securityContext take precedence over field values of PodSecurityContext.
|
||||
type PodSecurityContext struct {
|
||||
// Use the host's network namespace. If this option is set, the ports that will be
|
||||
// used must be specified.
|
||||
|
@ -1011,12 +1011,6 @@ type PodSecurityContext struct {
|
|||
// Use the host's ipc namespace.
|
||||
// Optional: Default to false.
|
||||
HostIPC bool `json:"hostIPC,omitempty"`
|
||||
|
||||
// SupplementalGroups can be used to specify a list of
|
||||
// additional groups which the main container process will run
|
||||
// as. This will be applied to all containers in the pod in
|
||||
// addition to the primary group of the container.
|
||||
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"`
|
||||
// The SELinux context to be applied to all containers.
|
||||
// If unspecified, the container runtime will allocate a random SELinux context for each
|
||||
// container. May also be set in SecurityContext. If set in
|
||||
|
@ -1036,6 +1030,20 @@ type PodSecurityContext struct {
|
|||
// May also be set in SecurityContext. If set in both SecurityContext and
|
||||
// PodSecurityContext, the value specified in SecurityContext takes precedence.
|
||||
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
|
||||
// A list of groups applied to the first process run in each container, in addition
|
||||
// to the container's primary GID. If unspecified, no groups will be added to
|
||||
// any container.
|
||||
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"`
|
||||
// A special supplemental group that applies to all containers in a pod.
|
||||
// Some volume types allow the Kubelet to change the ownership of that volume
|
||||
// to be owned by the pod:
|
||||
//
|
||||
// 1. The owning GID will be the FSGroup
|
||||
// 2. The setgid bit is set (new files created in the volume will be owned by FSGroup)
|
||||
// 3. The permission bits are OR'd with rw-rw----
|
||||
//
|
||||
// If unset, the Kubelet will not modify the ownership and permissions of any volume.
|
||||
FSGroup *int64 `json:"fsGroup,omitempty"`
|
||||
}
|
||||
|
||||
// PodStatus represents information about the status of a pod. Status may trail the actual
|
||||
|
|
|
@ -440,6 +440,12 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -469,5 +475,11 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *PodSecurityCont
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1501,14 +1501,6 @@ func deepCopy_v1_PodProxyOptions(in PodProxyOptions, out *PodProxyOptions, c *co
|
|||
}
|
||||
|
||||
func deepCopy_v1_PodSecurityContext(in PodSecurityContext, out *PodSecurityContext, c *conversion.Cloner) error {
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.SELinuxOptions != nil {
|
||||
out.SELinuxOptions = new(SELinuxOptions)
|
||||
if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
|
||||
|
@ -1529,6 +1521,20 @@ func deepCopy_v1_PodSecurityContext(in PodSecurityContext, out *PodSecurityConte
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1258,13 +1258,10 @@ type PodSpec struct {
|
|||
}
|
||||
|
||||
// PodSecurityContext holds pod-level security attributes and common container settings.
|
||||
// Some fields are also present in container.securityContext. Field values of
|
||||
// container.securityContext take precedence over field values of PodSecurityContext.
|
||||
type PodSecurityContext struct {
|
||||
// SupplementalGroups can be used to specify a list of
|
||||
// additional groups which the main container process will run
|
||||
// as. This will be applied to all containers in the pod in
|
||||
// addition to the primary group of the container.
|
||||
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"`
|
||||
// SELinuxOptions is the SELinux context to be applied to all containers
|
||||
// The SELinux context to be applied to all containers.
|
||||
// If unspecified, the container runtime will allocate a random SELinux context for each
|
||||
// container. May also be set in SecurityContext. If set in
|
||||
// both SecurityContext and PodSecurityContext, the value specified in SecurityContext
|
||||
|
@ -1283,6 +1280,20 @@ type PodSecurityContext struct {
|
|||
// May also be set in SecurityContext. If set in both SecurityContext and
|
||||
// PodSecurityContext, the value specified in SecurityContext takes precedence.
|
||||
RunAsNonRoot *bool `json:"runAsNonRoot,omitempty"`
|
||||
// A list of groups applied to the first process run in each container, in addition
|
||||
// to the container's primary GID. If unspecified, no groups will be added to
|
||||
// any container.
|
||||
SupplementalGroups []int64 `json:"supplementalGroups,omitempty"`
|
||||
// A special supplemental group that applies to all containers in a pod.
|
||||
// Some volume types allow the Kubelet to change the ownership of that volume
|
||||
// to be owned by the pod:
|
||||
//
|
||||
// 1. The owning GID will be the FSGroup
|
||||
// 2. The setgid bit is set (new files created in the volume will be owned by FSGroup)
|
||||
// 3. The permission bits are OR'd with rw-rw----
|
||||
//
|
||||
// If unset, the Kubelet will not modify the ownership and permissions of any volume.
|
||||
FSGroup *int64 `json:"fsGroup,omitempty"`
|
||||
}
|
||||
|
||||
// PodStatus represents information about the status of a pod. Status may trail the actual
|
||||
|
|
|
@ -975,11 +975,12 @@ func (PodProxyOptions) SwaggerDoc() map[string]string {
|
|||
}
|
||||
|
||||
var map_PodSecurityContext = map[string]string{
|
||||
"": "PodSecurityContext holds pod-level security attributes and common container settings.",
|
||||
"supplementalGroups": "SupplementalGroups can be used to specify a list of additional groups which the main container process will run as. This will be applied to all containers in the pod in addition to the primary group of the container.",
|
||||
"seLinuxOptions": "SELinuxOptions is the SELinux context to be applied to all containers If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.",
|
||||
"": "PodSecurityContext holds pod-level security attributes and common container settings. Some fields are also present in container.securityContext. Field values of container.securityContext take precedence over field values of PodSecurityContext.",
|
||||
"seLinuxOptions": "The SELinux context to be applied to all containers. If unspecified, the container runtime will allocate a random SELinux context for each container. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.",
|
||||
"runAsUser": "The UID to run the entrypoint of the container process. Defaults to user specified in image metadata if unspecified. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence for that container.",
|
||||
"runAsNonRoot": "Indicates that the container must run as a non-root user. If true, the Kubelet will validate the image at runtime to ensure that it does not run as UID 0 (root) and fail to start the container if it does. If unset or false, no such validation will be performed. May also be set in SecurityContext. If set in both SecurityContext and PodSecurityContext, the value specified in SecurityContext takes precedence.",
|
||||
"supplementalGroups": "A list of groups applied to the first process run in each container, in addition to the container's primary GID. If unspecified, no groups will be added to any container.",
|
||||
"fsGroup": "A special supplemental group that applies to all containers in a pod. Some volume types allow the Kubelet to change the ownership of that volume to be owned by the pod:\n\n1. The owning GID will be the FSGroup 2. The setgid bit is set (new files created in the volume will be owned by FSGroup) 3. The permission bits are OR'd with rw-rw ",
|
||||
}
|
||||
|
||||
func (PodSecurityContext) SwaggerDoc() map[string]string {
|
||||
|
|
|
@ -464,14 +464,6 @@ func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecu
|
|||
out.HostNetwork = in.HostNetwork
|
||||
out.HostPID = in.HostPID
|
||||
out.HostIPC = in.HostIPC
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.SELinuxOptions != nil {
|
||||
out.SELinuxOptions = new(api.SELinuxOptions)
|
||||
if err := deepCopy_api_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
|
||||
|
@ -492,6 +484,20 @@ func deepCopy_api_PodSecurityContext(in api.PodSecurityContext, out *api.PodSecu
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -353,6 +353,12 @@ func convert_api_PodSecurityContext_To_v1_PodSecurityContext(in *api.PodSecurity
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -382,5 +388,11 @@ func convert_v1_PodSecurityContext_To_api_PodSecurityContext(in *v1.PodSecurityC
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -497,14 +497,6 @@ func deepCopy_v1_PersistentVolumeClaimVolumeSource(in v1.PersistentVolumeClaimVo
|
|||
}
|
||||
|
||||
func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurityContext, c *conversion.Cloner) error {
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.SELinuxOptions != nil {
|
||||
out.SELinuxOptions = new(v1.SELinuxOptions)
|
||||
if err := deepCopy_v1_SELinuxOptions(*in.SELinuxOptions, out.SELinuxOptions, c); err != nil {
|
||||
|
@ -525,6 +517,20 @@ func deepCopy_v1_PodSecurityContext(in v1.PodSecurityContext, out *v1.PodSecurit
|
|||
} else {
|
||||
out.RunAsNonRoot = nil
|
||||
}
|
||||
if in.SupplementalGroups != nil {
|
||||
out.SupplementalGroups = make([]int64, len(in.SupplementalGroups))
|
||||
for i := range in.SupplementalGroups {
|
||||
out.SupplementalGroups[i] = in.SupplementalGroups[i]
|
||||
}
|
||||
} else {
|
||||
out.SupplementalGroups = nil
|
||||
}
|
||||
if in.FSGroup != nil {
|
||||
out.FSGroup = new(int64)
|
||||
*out.FSGroup = *in.FSGroup
|
||||
} else {
|
||||
out.FSGroup = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -64,6 +64,8 @@ import (
|
|||
"k8s.io/kubernetes/pkg/types"
|
||||
"k8s.io/kubernetes/pkg/util"
|
||||
"k8s.io/kubernetes/pkg/util/bandwidth"
|
||||
"k8s.io/kubernetes/pkg/util/chmod"
|
||||
"k8s.io/kubernetes/pkg/util/chown"
|
||||
utilerrors "k8s.io/kubernetes/pkg/util/errors"
|
||||
kubeio "k8s.io/kubernetes/pkg/util/io"
|
||||
"k8s.io/kubernetes/pkg/util/mount"
|
||||
|
@ -174,6 +176,8 @@ func NewMainKubelet(
|
|||
rktStage1Image string,
|
||||
mounter mount.Interface,
|
||||
writer kubeio.Writer,
|
||||
chownRunner chown.Interface,
|
||||
chmodRunner chmod.Interface,
|
||||
dockerDaemonContainer string,
|
||||
systemContainer string,
|
||||
configureCBR0 bool,
|
||||
|
@ -285,6 +289,8 @@ func NewMainKubelet(
|
|||
oomWatcher: oomWatcher,
|
||||
cgroupRoot: cgroupRoot,
|
||||
mounter: mounter,
|
||||
chmodRunner: chmodRunner,
|
||||
chownRunner: chownRunner,
|
||||
writer: writer,
|
||||
configureCBR0: configureCBR0,
|
||||
podCIDR: podCIDR,
|
||||
|
@ -581,6 +587,10 @@ type Kubelet struct {
|
|||
|
||||
// Mounter to use for volumes.
|
||||
mounter mount.Interface
|
||||
// chown.Interface implementation to use
|
||||
chownRunner chown.Interface
|
||||
// chmod.Interface implementation to use
|
||||
chmodRunner chmod.Interface
|
||||
|
||||
// Writer interface to use for volumes.
|
||||
writer kubeio.Writer
|
||||
|
|
|
@ -114,6 +114,12 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap,
|
|||
podVolumes := make(kubecontainer.VolumeMap)
|
||||
for i := range pod.Spec.Volumes {
|
||||
volSpec := &pod.Spec.Volumes[i]
|
||||
hasFSGroup := false
|
||||
var fsGroup int64 = 0
|
||||
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.FSGroup != nil {
|
||||
hasFSGroup = true
|
||||
fsGroup = *pod.Spec.SecurityContext.FSGroup
|
||||
}
|
||||
|
||||
rootContext, err := kl.getRootDirContext()
|
||||
if err != nil {
|
||||
|
@ -134,6 +140,12 @@ func (kl *Kubelet) mountExternalVolumes(pod *api.Pod) (kubecontainer.VolumeMap,
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hasFSGroup && builder.SupportsOwnershipManagement() && !builder.IsReadOnly() {
|
||||
err := kl.manageVolumeOwnership(pod, internal, builder, fsGroup)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
podVolumes[volSpec.Name] = builder
|
||||
}
|
||||
return podVolumes, nil
|
||||
|
|
|
@ -0,0 +1,63 @@
|
|||
// +build linux
|
||||
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed 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 kubelet
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
// Bitmask to OR with current ownership of volumes that allow ownership management by the Kubelet
|
||||
const managedOwnershipBitmask = os.FileMode(0660)
|
||||
|
||||
// manageVolumeOwnership modifies the given volume to be owned by fsGroup.
|
||||
func (kl *Kubelet) manageVolumeOwnership(pod *api.Pod, volSpec *volume.Spec, builder volume.Builder, fsGroup int64) error {
|
||||
return filepath.Walk(builder.GetPath(), func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stat, ok := info.Sys().(*syscall.Stat_t)
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
|
||||
if stat == nil {
|
||||
glog.Errorf("Got nil stat_t for path %v while managing ownership of volume %v for pod %s/%s", path, volSpec.Name, pod.Namespace, pod.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
err = kl.chownRunner.Chown(path, int(stat.Uid), int(fsGroup))
|
||||
if err != nil {
|
||||
glog.Errorf("Chown failed on %v: %v", path, err)
|
||||
}
|
||||
|
||||
err = kl.chmodRunner.Chmod(path, info.Mode()|managedOwnershipBitmask|os.ModeSetgid)
|
||||
if err != nil {
|
||||
glog.Errorf("Chmod failed on %v: %v", path, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
// +build !linux
|
||||
|
||||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed 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 kubelet
|
||||
|
||||
import (
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/volume"
|
||||
)
|
||||
|
||||
func (_ *Kubelet) manageVolumeOwnership(pod *api.Pod, volSpec *volume.Spec, builder volume.Builder, fsGroup int64) error {
|
||||
return nil
|
||||
}
|
|
@ -38,7 +38,7 @@ type SimpleSecurityContextProvider struct{}
|
|||
// The security context provider can make changes to the Config with which
|
||||
// the container is created.
|
||||
func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, container *api.Container, config *docker.Config) {
|
||||
effectiveSC := determineEffectiveSecurityContext(pod, container)
|
||||
effectiveSC := DetermineEffectiveSecurityContext(pod, container)
|
||||
if effectiveSC == nil {
|
||||
return
|
||||
}
|
||||
|
@ -52,24 +52,28 @@ func (p SimpleSecurityContextProvider) ModifyContainerConfig(pod *api.Pod, conta
|
|||
// security options, whether the container is privileged, volume binds, etc.
|
||||
func (p SimpleSecurityContextProvider) ModifyHostConfig(pod *api.Pod, container *api.Container, hostConfig *docker.HostConfig) {
|
||||
// Apply pod security context
|
||||
if pod.Spec.SecurityContext != nil {
|
||||
// We skip application of supplemental groups to the
|
||||
if container.Name != leaky.PodInfraContainerName && pod.Spec.SecurityContext != nil {
|
||||
// TODO: We skip application of supplemental groups to the
|
||||
// infra container to work around a runc issue which
|
||||
// requires containers to have the '/etc/group'. For
|
||||
// more information see:
|
||||
// https://github.com/opencontainers/runc/pull/313
|
||||
// This can be removed once the fix makes it into the
|
||||
// required version of docker.
|
||||
if pod.Spec.SecurityContext.SupplementalGroups != nil && container.Name != leaky.PodInfraContainerName {
|
||||
if pod.Spec.SecurityContext.SupplementalGroups != nil {
|
||||
hostConfig.GroupAdd = make([]string, len(pod.Spec.SecurityContext.SupplementalGroups))
|
||||
for i, group := range pod.Spec.SecurityContext.SupplementalGroups {
|
||||
hostConfig.GroupAdd[i] = strconv.Itoa(int(group))
|
||||
}
|
||||
}
|
||||
|
||||
if pod.Spec.SecurityContext.FSGroup != nil {
|
||||
hostConfig.GroupAdd = append(hostConfig.GroupAdd, strconv.Itoa(int(*pod.Spec.SecurityContext.FSGroup)))
|
||||
}
|
||||
}
|
||||
|
||||
// Apply effective security context for container
|
||||
effectiveSC := determineEffectiveSecurityContext(pod, container)
|
||||
effectiveSC := DetermineEffectiveSecurityContext(pod, container)
|
||||
if effectiveSC == nil {
|
||||
return
|
||||
}
|
||||
|
@ -116,7 +120,7 @@ func makeCapabilites(capAdd []api.Capability, capDrop []api.Capability) ([]strin
|
|||
return addCaps, dropCaps
|
||||
}
|
||||
|
||||
func determineEffectiveSecurityContext(pod *api.Pod, container *api.Container) *api.SecurityContext {
|
||||
func DetermineEffectiveSecurityContext(pod *api.Pod, container *api.Container) *api.SecurityContext {
|
||||
effectiveSc := securityContextFromPodSecurityContext(pod)
|
||||
containerSc := container.SecurityContext
|
||||
|
||||
|
|
|
@ -179,19 +179,35 @@ func TestModifyHostConfigPodSecurityContext(t *testing.T) {
|
|||
supplementalGroupsSC.SupplementalGroups = []int64{2222}
|
||||
supplementalGroupHC := fullValidHostConfig()
|
||||
supplementalGroupHC.GroupAdd = []string{"2222"}
|
||||
fsGroupHC := fullValidHostConfig()
|
||||
fsGroupHC.GroupAdd = []string{"1234"}
|
||||
bothHC := fullValidHostConfig()
|
||||
bothHC.GroupAdd = []string{"2222", "1234"}
|
||||
fsGroup := int64(1234)
|
||||
|
||||
testCases := map[string]struct {
|
||||
securityContext *api.PodSecurityContext
|
||||
expected *docker.HostConfig
|
||||
}{
|
||||
"nil Security Context": {
|
||||
"nil": {
|
||||
securityContext: nil,
|
||||
expected: fullValidHostConfig(),
|
||||
},
|
||||
"Security Context with SupplementalGroup": {
|
||||
"SupplementalGroup": {
|
||||
securityContext: supplementalGroupsSC,
|
||||
expected: supplementalGroupHC,
|
||||
},
|
||||
"FSGroup": {
|
||||
securityContext: &api.PodSecurityContext{FSGroup: &fsGroup},
|
||||
expected: fsGroupHC,
|
||||
},
|
||||
"FSGroup + SupplementalGroups": {
|
||||
securityContext: &api.PodSecurityContext{
|
||||
SupplementalGroups: []int64{2222},
|
||||
FSGroup: &fsGroup,
|
||||
},
|
||||
expected: bothHC,
|
||||
},
|
||||
}
|
||||
|
||||
provider := NewSimpleSecurityContextProvider()
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed 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 chmod
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Interface is something that knows how to run the chmod system call.
|
||||
// It is non-recursive.
|
||||
type Interface interface {
|
||||
// Chmod changes the mode of the given file, implementing the same
|
||||
// semantics as os.Chmod.
|
||||
Chmod(path string, filemode os.FileMode) error
|
||||
}
|
||||
|
||||
func New() Interface {
|
||||
return &chmodRunner{}
|
||||
}
|
||||
|
||||
type chmodRunner struct{}
|
||||
|
||||
func (_ *chmodRunner) Chmod(path string, mode os.FileMode) error {
|
||||
return os.Chmod(path, mode)
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright 2014 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed 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 chown provides an interface and implementations
|
||||
// for things that run run the chmod system call.
|
||||
package chmod
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed 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 chown
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Interface is something that knows how to run the chown system call.
|
||||
// It is non-recursive.
|
||||
type Interface interface {
|
||||
// Chown changes the owning UID and GID of a file, implementing
|
||||
// the exact same semantics as os.Chown.
|
||||
Chown(path string, uid, gid int) error
|
||||
}
|
||||
|
||||
func New() Interface {
|
||||
return &chownRunner{}
|
||||
}
|
||||
|
||||
type chownRunner struct{}
|
||||
|
||||
func (_ *chownRunner) Chown(path string, uid, gid int) error {
|
||||
return os.Chown(path, uid, gid)
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
Copyright 2015 The Kubernetes Authors All rights reserved.
|
||||
|
||||
Licensed 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 chown provides utilities to chown a path
|
||||
package chown
|
|
@ -177,6 +177,10 @@ type awsElasticBlockStoreBuilder struct {
|
|||
|
||||
var _ volume.Builder = &awsElasticBlockStoreBuilder{}
|
||||
|
||||
func (_ *awsElasticBlockStoreBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *awsElasticBlockStoreBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
|
|
|
@ -151,6 +151,10 @@ type cephfsBuilder struct {
|
|||
|
||||
var _ volume.Builder = &cephfsBuilder{}
|
||||
|
||||
func (_ *cephfsBuilder) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (cephfsVolume *cephfsBuilder) SetUp() error {
|
||||
return cephfsVolume.SetUpAt(cephfsVolume.GetPath())
|
||||
|
|
|
@ -153,6 +153,10 @@ func detachDiskLogError(cd *cinderVolume) {
|
|||
}
|
||||
}
|
||||
|
||||
func (_ *cinderVolumeBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *cinderVolumeBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
}
|
||||
|
|
|
@ -107,6 +107,10 @@ type downwardAPIVolumeBuilder struct {
|
|||
// downwardAPIVolumeBuilder implements volume.Builder interface
|
||||
var _ volume.Builder = &downwardAPIVolumeBuilder{}
|
||||
|
||||
func (_ *downwardAPIVolumeBuilder) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetUp puts in place the volume plugin.
|
||||
// This function is not idempotent by design. We want the data to be refreshed periodically.
|
||||
// The internal sync interval of kubelet will drive the refresh of data.
|
||||
|
|
|
@ -137,6 +137,10 @@ type emptyDir struct {
|
|||
chconRunner chconRunner
|
||||
}
|
||||
|
||||
func (_ *emptyDir) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetUp creates new directory.
|
||||
func (ed *emptyDir) SetUp() error {
|
||||
return ed.SetUpAt(ed.GetPath())
|
||||
|
|
|
@ -164,6 +164,10 @@ type fcDiskBuilder struct {
|
|||
|
||||
var _ volume.Builder = &fcDiskBuilder{}
|
||||
|
||||
func (_ *fcDiskBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *fcDiskBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
}
|
||||
|
|
|
@ -113,6 +113,10 @@ type flockerBuilder struct {
|
|||
readOnly bool
|
||||
}
|
||||
|
||||
func (_ *flockerBuilder) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (b flockerBuilder) GetPath() string {
|
||||
return b.flocker.path
|
||||
}
|
||||
|
|
|
@ -165,6 +165,10 @@ type gcePersistentDiskBuilder struct {
|
|||
|
||||
var _ volume.Builder = &gcePersistentDiskBuilder{}
|
||||
|
||||
func (_ *gcePersistentDiskBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *gcePersistentDiskBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
|
|
|
@ -109,6 +109,10 @@ type gitRepoVolumeBuilder struct {
|
|||
|
||||
var _ volume.Builder = &gitRepoVolumeBuilder{}
|
||||
|
||||
func (_ *gitRepoVolumeBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// SetUp creates new directory and clones a git repo.
|
||||
func (b *gitRepoVolumeBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
|
|
|
@ -135,6 +135,10 @@ type glusterfsBuilder struct {
|
|||
|
||||
var _ volume.Builder = &glusterfsBuilder{}
|
||||
|
||||
func (_ *glusterfsBuilder) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *glusterfsBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
|
|
|
@ -167,6 +167,10 @@ type hostPathBuilder struct {
|
|||
|
||||
var _ volume.Builder = &hostPathBuilder{}
|
||||
|
||||
func (_ *hostPathBuilder) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetUp does nothing.
|
||||
func (b *hostPathBuilder) SetUp() error {
|
||||
return nil
|
||||
|
|
|
@ -158,6 +158,10 @@ type iscsiDiskBuilder struct {
|
|||
|
||||
var _ volume.Builder = &iscsiDiskBuilder{}
|
||||
|
||||
func (_ *iscsiDiskBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *iscsiDiskBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
}
|
||||
|
|
|
@ -169,6 +169,10 @@ type nfsBuilder struct {
|
|||
|
||||
var _ volume.Builder = &nfsBuilder{}
|
||||
|
||||
func (_ *nfsBuilder) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// SetUp attaches the disk and bind mounts to the volume path.
|
||||
func (b *nfsBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
|
|
|
@ -192,6 +192,10 @@ type rbdBuilder struct {
|
|||
|
||||
var _ volume.Builder = &rbdBuilder{}
|
||||
|
||||
func (_ *rbdBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *rbdBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
}
|
||||
|
|
|
@ -97,6 +97,10 @@ type secretVolumeBuilder struct {
|
|||
|
||||
var _ volume.Builder = &secretVolumeBuilder{}
|
||||
|
||||
func (_ *secretVolumeBuilder) SupportsOwnershipManagement() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (b *secretVolumeBuilder) SetUp() error {
|
||||
return b.SetUpAt(b.GetPath())
|
||||
}
|
||||
|
|
|
@ -156,6 +156,10 @@ type FakeVolume struct {
|
|||
Plugin *FakeVolumePlugin
|
||||
}
|
||||
|
||||
func (_ *FakeVolume) SupportsOwnershipManagement() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (fv *FakeVolume) SetUp() error {
|
||||
return fv.SetUpAt(fv.GetPath())
|
||||
}
|
||||
|
|
|
@ -45,6 +45,14 @@ type Builder interface {
|
|||
// IsReadOnly is a flag that gives the builder's ReadOnly attribute.
|
||||
// All persistent volumes have a private readOnly flag in their builders.
|
||||
IsReadOnly() bool
|
||||
// SupportsOwnershipManagement returns whether this builder wants
|
||||
// ownership management for the volume. If this method returns true,
|
||||
// the Kubelet will:
|
||||
//
|
||||
// 1. Make the volume owned by group FSGroup
|
||||
// 2. Set the setgid bit is set (new files created in the volume will be owned by FSGroup)
|
||||
// 3. Logical OR the permission bits with rw-rw----
|
||||
SupportsOwnershipManagement() bool
|
||||
}
|
||||
|
||||
// Cleaner interface provides methods to cleanup/unmount the volumes.
|
||||
|
|
|
@ -69,6 +69,10 @@ func (p *plugin) Admit(a admission.Attributes) (err error) {
|
|||
}
|
||||
}
|
||||
|
||||
if pod.Spec.SecurityContext != nil && pod.Spec.SecurityContext.FSGroup != nil {
|
||||
return apierrors.NewForbidden(a.GetResource(), pod.Name, fmt.Errorf("SecurityContext.FSGroup is forbidden"))
|
||||
}
|
||||
|
||||
for _, v := range pod.Spec.Containers {
|
||||
if v.SecurityContext != nil {
|
||||
if v.SecurityContext.SELinuxOptions != nil {
|
||||
|
|
|
@ -101,6 +101,8 @@ func TestPodSecurityContextAdmission(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
fsGroup := int64(1001)
|
||||
|
||||
tests := []struct {
|
||||
securityContext api.PodSecurityContext
|
||||
errorExpected bool
|
||||
|
@ -115,6 +117,12 @@ func TestPodSecurityContextAdmission(t *testing.T) {
|
|||
},
|
||||
errorExpected: true,
|
||||
},
|
||||
{
|
||||
securityContext: api.PodSecurityContext{
|
||||
FSGroup: &fsGroup,
|
||||
},
|
||||
errorExpected: true,
|
||||
},
|
||||
}
|
||||
for _, test := range tests {
|
||||
pod.Spec.SecurityContext = &test.securityContext
|
||||
|
|
|
@ -37,6 +37,23 @@ var _ = Describe("EmptyDir volumes", func() {
|
|||
|
||||
f := NewFramework("emptydir")
|
||||
|
||||
// TODO: Enable security context everywhere and remove skip for FSGroup
|
||||
It("new files should be created with FSGroup ownership when container is root [Conformance] [Skipped]", func() {
|
||||
doTestSetgidFSGroup(f, testImageRootUid, api.StorageMediumMemory)
|
||||
})
|
||||
|
||||
It("new files should be created with FSGroup ownership when container is non-root [Conformance] [Skipped]", func() {
|
||||
doTestSetgidFSGroup(f, testImageNonRootUid, api.StorageMediumMemory)
|
||||
})
|
||||
|
||||
It("volume on default medium should have the correct mode using FSGroup [Conformance] [Skipped]", func() {
|
||||
doTestVolumeModeFSGroup(f, testImageRootUid, api.StorageMediumDefault)
|
||||
})
|
||||
|
||||
It("volume on tmpfs should have the correct mode using FSGroup [Conformance] [Skipped]", func() {
|
||||
doTestVolumeModeFSGroup(f, testImageRootUid, api.StorageMediumMemory)
|
||||
})
|
||||
|
||||
It("volume on tmpfs should have the correct mode [Conformance]", func() {
|
||||
doTestVolumeMode(f, testImageRootUid, api.StorageMediumMemory)
|
||||
})
|
||||
|
@ -99,6 +116,91 @@ const (
|
|||
volumeName = "test-volume"
|
||||
)
|
||||
|
||||
func doTestSetgidFSGroup(f *Framework, image string, medium api.StorageMedium) {
|
||||
var (
|
||||
volumePath = "/test-volume"
|
||||
filePath = path.Join(volumePath, "test-file")
|
||||
source = &api.EmptyDirVolumeSource{Medium: medium}
|
||||
pod = testPodWithVolume(testImageRootUid, volumePath, source)
|
||||
)
|
||||
|
||||
pod.Spec.Containers[0].Args = []string{
|
||||
fmt.Sprintf("--fs_type=%v", volumePath),
|
||||
fmt.Sprintf("--new_file_0660=%v", filePath),
|
||||
fmt.Sprintf("--file_perm=%v", filePath),
|
||||
fmt.Sprintf("--file_owner=%v", filePath),
|
||||
}
|
||||
|
||||
pod.Spec.SecurityContext = &api.PodSecurityContext{}
|
||||
fsGroup := int64(123)
|
||||
pod.Spec.SecurityContext.FSGroup = &fsGroup
|
||||
|
||||
msg := fmt.Sprintf("emptydir 0644 on %v", formatMedium(medium))
|
||||
out := []string{
|
||||
"perms of file \"/test-volume/test-file\": -rw-rw----",
|
||||
"content of file \"/test-volume/test-file\": mount-tester new file",
|
||||
"owner GID of \"/test-volume/test-file\": 123",
|
||||
}
|
||||
if medium == api.StorageMediumMemory {
|
||||
out = append(out, "mount type of \"/test-volume\": tmpfs")
|
||||
}
|
||||
f.TestContainerOutput(msg, pod, 0, out)
|
||||
}
|
||||
|
||||
func doTestVolumeModeFSGroup(f *Framework, image string, medium api.StorageMedium) {
|
||||
var (
|
||||
volumePath = "/test-volume"
|
||||
source = &api.EmptyDirVolumeSource{Medium: medium}
|
||||
pod = testPodWithVolume(testImageRootUid, volumePath, source)
|
||||
)
|
||||
|
||||
pod.Spec.Containers[0].Args = []string{
|
||||
fmt.Sprintf("--fs_type=%v", volumePath),
|
||||
fmt.Sprintf("--file_perm=%v", volumePath),
|
||||
}
|
||||
|
||||
fsGroup := int64(1001)
|
||||
pod.Spec.SecurityContext = &api.PodSecurityContext{FSGroup: &fsGroup}
|
||||
|
||||
msg := fmt.Sprintf("emptydir volume type on %v", formatMedium(medium))
|
||||
out := []string{
|
||||
"perms of file \"/test-volume\": -rwxrwxrwx",
|
||||
}
|
||||
if medium == api.StorageMediumMemory {
|
||||
out = append(out, "mount type of \"/test-volume\": tmpfs")
|
||||
}
|
||||
f.TestContainerOutput(msg, pod, 0, out)
|
||||
}
|
||||
|
||||
func doTest0644FSGroup(f *Framework, image string, medium api.StorageMedium) {
|
||||
var (
|
||||
volumePath = "/test-volume"
|
||||
filePath = path.Join(volumePath, "test-file")
|
||||
source = &api.EmptyDirVolumeSource{Medium: medium}
|
||||
pod = testPodWithVolume(image, volumePath, source)
|
||||
)
|
||||
|
||||
pod.Spec.Containers[0].Args = []string{
|
||||
fmt.Sprintf("--fs_type=%v", volumePath),
|
||||
fmt.Sprintf("--new_file_0644=%v", filePath),
|
||||
fmt.Sprintf("--file_perm=%v", filePath),
|
||||
}
|
||||
|
||||
pod.Spec.SecurityContext = &api.PodSecurityContext{}
|
||||
fsGroup := int64(123)
|
||||
pod.Spec.SecurityContext.FSGroup = &fsGroup
|
||||
|
||||
msg := fmt.Sprintf("emptydir 0644 on %v", formatMedium(medium))
|
||||
out := []string{
|
||||
"perms of file \"/test-volume/test-file\": -rw-r--r--",
|
||||
"content of file \"/test-volume/test-file\": mount-tester new file",
|
||||
}
|
||||
if medium == api.StorageMediumMemory {
|
||||
out = append(out, "mount type of \"/test-volume\": tmpfs")
|
||||
}
|
||||
f.TestContainerOutput(msg, pod, 0, out)
|
||||
}
|
||||
|
||||
func doTestVolumeMode(f *Framework, image string, medium api.StorageMedium) {
|
||||
var (
|
||||
volumePath = "/test-volume"
|
||||
|
|
Loading…
Reference in New Issue