StatefulSet fsGroup config option to allow non-root spilo (#531)

* StatefulSet fsGroup config option to allow non-root spilo

* Allow Postgres CRD to overide SpiloFSGroup of the Operator.

* Document FSGroup of a Pod cannot be changed after creation.
This commit is contained in:
Aaron Miller 2019-06-04 07:38:26 -07:00 committed by Sergey Dudoladov
parent 5a0e95ac45
commit ec5b1d4d58
11 changed files with 45 additions and 0 deletions

View File

@ -31,6 +31,7 @@ configKubernetes:
# node_readiness_label: "" # node_readiness_label: ""
# oauth_token_secret_name: postgresql-operator # oauth_token_secret_name: postgresql-operator
# pod_environment_configmap: "" # pod_environment_configmap: ""
# spilo_fsgroup: "103"
pod_management_policy: "ordered_ready" pod_management_policy: "ordered_ready"
pdb_name_format: "postgres-{cluster}-pdb" pdb_name_format: "postgres-{cluster}-pdb"
pod_role_label: spilo-role pod_role_label: spilo-role

View File

@ -58,6 +58,13 @@ These parameters are grouped directly under the `spec` key in the manifest.
custom docker image that overrides the **docker_image** operator parameter. custom docker image that overrides the **docker_image** operator parameter.
It should be a [Spilo](https://github.com/zalando/spilo) image. Optional. It should be a [Spilo](https://github.com/zalando/spilo) image. Optional.
* **spiloFSGroup**
the Persistent Volumes for the spilo pods in the StatefulSet will be owned
and writable by the group ID specified. This will override the **spilo_fsgroup**
operator parameter. This is required to run Spilo as a non-root process, but
requires a custom spilo image. Note the FSGroup of a Pod cannot be changed
without recreating a new Pod.
* **enableMasterLoadBalancer** * **enableMasterLoadBalancer**
boolean flag to override the operator defaults (set by the boolean flag to override the operator defaults (set by the
`enable_master_load_balancer` parameter) to define whether to enable the load `enable_master_load_balancer` parameter) to define whether to enable the load

View File

@ -228,6 +228,11 @@ configuration they are grouped under the `kubernetes` key.
that should be assigned to the Postgres pods. The priority class itself must that should be assigned to the Postgres pods. The priority class itself must
be defined in advance. Default is empty (use the default priority class). be defined in advance. Default is empty (use the default priority class).
* **spilo_fsgroup**
the Persistent Volumes for the spilo pods in the StatefulSet will be owned and writable by the group ID specified.
This is required to run Spilo as a non-root process, but requires a custom spilo image. Note the FSGroup of a Pod
cannot be changed without recreating a new Pod.
* **spilo_privileged** * **spilo_privileged**
whether the Spilo container should run in privileged mode. Privileged mode is whether the Spilo container should run in privileged mode. Privileged mode is
used for AWS volume resizing and not required if you don't need that used for AWS volume resizing and not required if you don't need that

View File

@ -37,6 +37,7 @@ spec:
limits: limits:
cpu: 300m cpu: 300m
memory: 300Mi memory: 300Mi
# spiloFSGroup: 103
patroni: patroni:
initdb: initdb:
encoding: "UTF8" encoding: "UTF8"

View File

@ -24,6 +24,7 @@ configuration:
cluster_domain: cluster.local cluster_domain: cluster.local
oauth_token_secret_name: postgresql-operator oauth_token_secret_name: postgresql-operator
pod_role_label: spilo-role pod_role_label: spilo-role
# spilo_fsgroup: 103
spilo_privileged: false spilo_privileged: false
cluster_labels: cluster_labels:
application: spilo application: spilo

View File

@ -46,6 +46,7 @@ type KubernetesMetaConfiguration struct {
PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"` PodServiceAccountRoleBindingDefinition string `json:"pod_service_account_role_binding_definition,omitempty"`
PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"` PodTerminateGracePeriod Duration `json:"pod_terminate_grace_period,omitempty"`
SpiloPrivileged bool `json:"spilo_privileged,omitemty"` SpiloPrivileged bool `json:"spilo_privileged,omitemty"`
SpiloFSGroup *int64 `json:"spilo_fsgroup,omitempty"`
WatchedNamespace string `json:"watched_namespace,omitempty"` WatchedNamespace string `json:"watched_namespace,omitempty"`
PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"` PDBNameFormat config.StringTemplate `json:"pdb_name_format,omitempty"`
SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"` SecretNameTemplate config.StringTemplate `json:"secret_name_template,omitempty"`

View File

@ -30,6 +30,8 @@ type PostgresSpec struct {
TeamID string `json:"teamId"` TeamID string `json:"teamId"`
DockerImage string `json:"dockerImage,omitempty"` DockerImage string `json:"dockerImage,omitempty"`
SpiloFSGroup *int64 `json:"spiloFSGroup,omitempty"`
// vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest // vars that enable load balancers are pointers because it is important to know if any of them is omitted from the Postgres manifest
// in that case the var evaluates to nil and the value is taken from the operator config // in that case the var evaluates to nil and the value is taken from the operator config
EnableMasterLoadBalancer *bool `json:"enableMasterLoadBalancer,omitempty"` EnableMasterLoadBalancer *bool `json:"enableMasterLoadBalancer,omitempty"`

View File

@ -66,6 +66,11 @@ func (in *CloneDescription) DeepCopy() *CloneDescription {
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfiguration) { func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfiguration) {
*out = *in *out = *in
if in.SpiloFSGroup != nil {
in, out := &in.SpiloFSGroup, &out.SpiloFSGroup
*out = new(int64)
**out = **in
}
out.OAuthTokenSecretName = in.OAuthTokenSecretName out.OAuthTokenSecretName = in.OAuthTokenSecretName
out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName
if in.ClusterLabels != nil { if in.ClusterLabels != nil {
@ -402,6 +407,11 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
out.Volume = in.Volume out.Volume = in.Volume
in.Patroni.DeepCopyInto(&out.Patroni) in.Patroni.DeepCopyInto(&out.Patroni)
out.Resources = in.Resources out.Resources = in.Resources
if in.SpiloFSGroup != nil {
in, out := &in.SpiloFSGroup, &out.SpiloFSGroup
*out = new(int64)
**out = **in
}
if in.EnableMasterLoadBalancer != nil { if in.EnableMasterLoadBalancer != nil {
in, out := &in.EnableMasterLoadBalancer, &out.EnableMasterLoadBalancer in, out := &in.EnableMasterLoadBalancer, &out.EnableMasterLoadBalancer
*out = new(bool) *out = new(bool)

View File

@ -432,6 +432,7 @@ func generatePodTemplate(
initContainers []v1.Container, initContainers []v1.Container,
sidecarContainers []v1.Container, sidecarContainers []v1.Container,
tolerationsSpec *[]v1.Toleration, tolerationsSpec *[]v1.Toleration,
spiloFSGroup *int64,
nodeAffinity *v1.Affinity, nodeAffinity *v1.Affinity,
terminateGracePeriod int64, terminateGracePeriod int64,
podServiceAccountName string, podServiceAccountName string,
@ -445,6 +446,11 @@ func generatePodTemplate(
terminateGracePeriodSeconds := terminateGracePeriod terminateGracePeriodSeconds := terminateGracePeriod
containers := []v1.Container{*spiloContainer} containers := []v1.Container{*spiloContainer}
containers = append(containers, sidecarContainers...) containers = append(containers, sidecarContainers...)
securityContext := v1.PodSecurityContext{}
if spiloFSGroup != nil {
securityContext.FSGroup = spiloFSGroup
}
podSpec := v1.PodSpec{ podSpec := v1.PodSpec{
ServiceAccountName: podServiceAccountName, ServiceAccountName: podServiceAccountName,
@ -452,6 +458,7 @@ func generatePodTemplate(
Containers: containers, Containers: containers,
InitContainers: initContainers, InitContainers: initContainers,
Tolerations: *tolerationsSpec, Tolerations: *tolerationsSpec,
SecurityContext: &securityContext,
} }
if shmVolume { if shmVolume {
@ -831,6 +838,12 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration) tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName) effectivePodPriorityClassName := util.Coalesce(spec.PodPriorityClassName, c.OpConfig.PodPriorityClassName)
// determine the FSGroup for the spilo pod
effectiveFSGroup := c.OpConfig.Resources.SpiloFSGroup
if spec.SpiloFSGroup != nil {
effectiveFSGroup = spec.SpiloFSGroup
}
// generate pod template for the statefulset, based on the spilo container and sidecars // generate pod template for the statefulset, based on the spilo container and sidecars
if podTemplate, err = generatePodTemplate( if podTemplate, err = generatePodTemplate(
c.Namespace, c.Namespace,
@ -839,6 +852,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
spec.InitContainers, spec.InitContainers,
sidecarContainers, sidecarContainers,
&tolerationSpec, &tolerationSpec,
effectiveFSGroup,
nodeAffinity(c.OpConfig.NodeReadinessLabel), nodeAffinity(c.OpConfig.NodeReadinessLabel),
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()), int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
c.OpConfig.PodServiceAccountName, c.OpConfig.PodServiceAccountName,
@ -1340,6 +1354,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) {
[]v1.Container{}, []v1.Container{},
[]v1.Container{}, []v1.Container{},
&[]v1.Toleration{}, &[]v1.Toleration{},
nil,
nodeAffinity(c.OpConfig.NodeReadinessLabel), nodeAffinity(c.OpConfig.NodeReadinessLabel),
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()), int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
c.OpConfig.PodServiceAccountName, c.OpConfig.PodServiceAccountName,

View File

@ -42,6 +42,7 @@ func (c *Controller) importConfigurationFromCRD(fromCRD *acidv1.OperatorConfigur
result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap result.PodEnvironmentConfigMap = fromCRD.Kubernetes.PodEnvironmentConfigMap
result.PodTerminateGracePeriod = time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod) result.PodTerminateGracePeriod = time.Duration(fromCRD.Kubernetes.PodTerminateGracePeriod)
result.SpiloPrivileged = fromCRD.Kubernetes.SpiloPrivileged result.SpiloPrivileged = fromCRD.Kubernetes.SpiloPrivileged
result.SpiloFSGroup = fromCRD.Kubernetes.SpiloFSGroup
result.ClusterDomain = fromCRD.Kubernetes.ClusterDomain result.ClusterDomain = fromCRD.Kubernetes.ClusterDomain
result.WatchedNamespace = fromCRD.Kubernetes.WatchedNamespace result.WatchedNamespace = fromCRD.Kubernetes.WatchedNamespace
result.PDBNameFormat = fromCRD.Kubernetes.PDBNameFormat result.PDBNameFormat = fromCRD.Kubernetes.PDBNameFormat

View File

@ -25,6 +25,7 @@ type Resources struct {
PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"` PodLabelWaitTimeout time.Duration `name:"pod_label_wait_timeout" default:"10m"`
PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"` PodDeletionWaitTimeout time.Duration `name:"pod_deletion_wait_timeout" default:"10m"`
PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"` PodTerminateGracePeriod time.Duration `name:"pod_terminate_grace_period" default:"5m"`
SpiloFSGroup *int64 `name:"spilo_fsgroup"`
PodPriorityClassName string `name:"pod_priority_class_name"` PodPriorityClassName string `name:"pod_priority_class_name"`
ClusterDomain string `name:"cluster_domain" default:"cluster.local"` ClusterDomain string `name:"cluster_domain" default:"cluster.local"`
SpiloPrivileged bool `name:"spilo_privileged" default:"false"` SpiloPrivileged bool `name:"spilo_privileged" default:"false"`