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: ""
# oauth_token_secret_name: postgresql-operator
# pod_environment_configmap: ""
# spilo_fsgroup: "103"
pod_management_policy: "ordered_ready"
pdb_name_format: "postgres-{cluster}-pdb"
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.
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**
boolean flag to override the operator defaults (set by the
`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
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**
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

View File

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

View File

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

View File

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

View File

@ -30,6 +30,8 @@ type PostgresSpec struct {
TeamID string `json:"teamId"`
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
// in that case the var evaluates to nil and the value is taken from the operator config
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.
func (in *KubernetesMetaConfiguration) DeepCopyInto(out *KubernetesMetaConfiguration) {
*out = *in
if in.SpiloFSGroup != nil {
in, out := &in.SpiloFSGroup, &out.SpiloFSGroup
*out = new(int64)
**out = **in
}
out.OAuthTokenSecretName = in.OAuthTokenSecretName
out.InfrastructureRolesSecretName = in.InfrastructureRolesSecretName
if in.ClusterLabels != nil {
@ -402,6 +407,11 @@ func (in *PostgresSpec) DeepCopyInto(out *PostgresSpec) {
out.Volume = in.Volume
in.Patroni.DeepCopyInto(&out.Patroni)
out.Resources = in.Resources
if in.SpiloFSGroup != nil {
in, out := &in.SpiloFSGroup, &out.SpiloFSGroup
*out = new(int64)
**out = **in
}
if in.EnableMasterLoadBalancer != nil {
in, out := &in.EnableMasterLoadBalancer, &out.EnableMasterLoadBalancer
*out = new(bool)

View File

@ -432,6 +432,7 @@ func generatePodTemplate(
initContainers []v1.Container,
sidecarContainers []v1.Container,
tolerationsSpec *[]v1.Toleration,
spiloFSGroup *int64,
nodeAffinity *v1.Affinity,
terminateGracePeriod int64,
podServiceAccountName string,
@ -445,6 +446,11 @@ func generatePodTemplate(
terminateGracePeriodSeconds := terminateGracePeriod
containers := []v1.Container{*spiloContainer}
containers = append(containers, sidecarContainers...)
securityContext := v1.PodSecurityContext{}
if spiloFSGroup != nil {
securityContext.FSGroup = spiloFSGroup
}
podSpec := v1.PodSpec{
ServiceAccountName: podServiceAccountName,
@ -452,6 +458,7 @@ func generatePodTemplate(
Containers: containers,
InitContainers: initContainers,
Tolerations: *tolerationsSpec,
SecurityContext: &securityContext,
}
if shmVolume {
@ -831,6 +838,12 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
tolerationSpec := tolerations(&spec.Tolerations, c.OpConfig.PodToleration)
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
if podTemplate, err = generatePodTemplate(
c.Namespace,
@ -839,6 +852,7 @@ func (c *Cluster) generateStatefulSet(spec *acidv1.PostgresSpec) (*v1beta1.State
spec.InitContainers,
sidecarContainers,
&tolerationSpec,
effectiveFSGroup,
nodeAffinity(c.OpConfig.NodeReadinessLabel),
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
c.OpConfig.PodServiceAccountName,
@ -1340,6 +1354,7 @@ func (c *Cluster) generateLogicalBackupJob() (*batchv1beta1.CronJob, error) {
[]v1.Container{},
[]v1.Container{},
&[]v1.Toleration{},
nil,
nodeAffinity(c.OpConfig.NodeReadinessLabel),
int64(c.OpConfig.PodTerminateGracePeriod.Seconds()),
c.OpConfig.PodServiceAccountName,

View File

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

View File

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