diff --git a/cmd/aws-application-networking-k8s/main.go b/cmd/aws-application-networking-k8s/main.go index 6fe09d7e..0216e92c 100644 --- a/cmd/aws-application-networking-k8s/main.go +++ b/cmd/aws-application-networking-k8s/main.go @@ -179,7 +179,7 @@ func main() { TaggingServiceAPIDisabled: config.DisableTaggingServiceAPI, }, metrics.Registry) if err != nil { - setupLog.Fatal("cloud client setup failed: %s", err) + setupLog.Fatalf("cloud client setup failed: %s", err) } // do not create the webhook server when running locally diff --git a/config/rbac/cluster-role-controller.yaml b/config/rbac/cluster-role-controller.yaml index 04eb21b1..b617bdc8 100644 --- a/config/rbac/cluster-role-controller.yaml +++ b/config/rbac/cluster-role-controller.yaml @@ -81,19 +81,9 @@ rules: resources: - pods verbs: - - create - - delete - get - list - - patch - - update - watch -- apiGroups: - - "" - resources: - - pods/finalizers - verbs: - - update - apiGroups: - "" resources: diff --git a/docs/guides/pod-readiness-gates.md b/docs/guides/pod-readiness-gates.md index d7b13d2e..7c06ed34 100644 --- a/docs/guides/pod-readiness-gates.md +++ b/docs/guides/pod-readiness-gates.md @@ -18,6 +18,19 @@ This prevents the rolling update of a deployment from terminating old pods until ## Setup Pod readiness gates rely on [»admission webhooks«](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/), where the Kubernetes API server makes calls to the AWS Gateway API controller as part of pod creation. This call is made using TLS, so the controller must present a TLS certificate. This certificate is stored as a standard Kubernetes secret. If you are using Helm, the certificate will automatically be configured as part of the Helm install. +For Helm installs, certificate validity is controlled by `webhookTLS.validityDays`. +The default is `36500` days to preserve existing behavior. You can opt in to +`365` days for annual certificate rotation: + +```bash +helm upgrade --install gateway-api-controller \ + oci://public.ecr.aws/aws-application-networking-k8s/aws-gateway-controller-chart \ + --namespace aws-application-networking-system \ + --set webhookTLS.validityDays=365 +``` + +When using `365`, plan and automate annual certificate rotation. + If you are manually deploying the controller using the ```deploy.yaml``` file, you will need to either patch the ```deploy.yaml``` file (see ```scripts/patch-deploy-yaml.sh```) or generate the secret following installation (see ```scripts/gen-webhook-secret.sh```) and manually enable the webhook via the ```WEBHOOK_ENABLED``` environment variable. Note that, without the secret in place, the controller cannot start successfully, and you will see an error message like the following: diff --git a/helm/templates/_helpers.tpl b/helm/templates/_helpers.tpl index 986b4fa8..8b92c104 100644 --- a/helm/templates/_helpers.tpl +++ b/helm/templates/_helpers.tpl @@ -38,11 +38,12 @@ caCert: {{ .Values.webhookTLS.caCert }} cert: {{ .Values.webhookTLS.cert }} key: {{ .Values.webhookTLS.key }} {{- else -}} -{{- $ca := genCA "aws-gateway-controller-ca" 36500 -}} +{{- $certValidityDays := int (default 36500 .Values.webhookTLS.validityDays) -}} +{{- $ca := genCA "aws-gateway-controller-ca" $certValidityDays -}} {{- $serviceDefaultName:= printf "webhook-service.%s.svc" .Release.Namespace -}} {{- $secretName := "webhook-cert" -}} {{- $altNames := list ($serviceDefaultName) (printf "%s.cluster.local" $serviceDefaultName) -}} -{{- $cert := genSignedCert $serviceDefaultName nil $altNames 36500 $ca -}} +{{- $cert := genSignedCert $serviceDefaultName nil $altNames $certValidityDays $ca -}} caCert: {{ $ca.Cert | b64enc }} cert: {{ $cert.Cert | b64enc }} key: {{ $cert.Key | b64enc }} diff --git a/helm/templates/cluster-role-controller.yaml b/helm/templates/cluster-role-controller.yaml index f0314c2f..644eb437 100644 --- a/helm/templates/cluster-role-controller.yaml +++ b/helm/templates/cluster-role-controller.yaml @@ -96,19 +96,9 @@ rules: resources: - pods verbs: - - create - - delete - get - list - - patch - - update - watch -- apiGroups: - - "" - resources: - - pods/finalizers - verbs: - - update - apiGroups: - "" resources: diff --git a/helm/templates/deployment.yaml b/helm/templates/deployment.yaml index e6761db6..6eb2c1b8 100644 --- a/helm/templates/deployment.yaml +++ b/helm/templates/deployment.yaml @@ -52,8 +52,10 @@ spec: ports: - name: http containerPort: {{ .Values.deployment.containerPort }} + {{- if .Values.webhookEnabled }} - name: webhook-server containerPort: 9443 + {{- end }} resources: {{- toYaml .Values.resources | nindent 10 }} livenessProbe: @@ -75,9 +77,11 @@ spec: - ALL readOnlyRootFilesystem: true volumeMounts: + {{- if .Values.webhookEnabled }} - mountPath: /etc/webhook-cert name: webhook-cert readOnly: true + {{- end }} env: - name: REGION value: {{ .Values.awsRegion | quote }} @@ -104,10 +108,12 @@ spec: terminationGracePeriodSeconds: 10 volumes: + {{- if .Values.webhookEnabled }} - name: webhook-cert secret: defaultMode: 420 secretName: webhook-cert + {{- end }} nodeSelector: {{ toYaml .Values.deployment.nodeSelector | nindent 8 }} {{ if .Values.deployment.tolerations -}} tolerations: {{ toYaml .Values.deployment.tolerations | nindent 8 }} diff --git a/helm/templates/webhook.yaml b/helm/templates/webhook.yaml index e8047fe3..5b565751 100644 --- a/helm/templates/webhook.yaml +++ b/helm/templates/webhook.yaml @@ -1,3 +1,4 @@ +{{- if .Values.webhookEnabled }} {{ $tls := fromYaml ( include "aws-gateway-controller.webhookTLS" . ) }} --- apiVersion: admissionregistration.k8s.io/v1 @@ -59,4 +60,5 @@ type: kubernetes.io/tls data: ca.crt: {{ $tls.caCert }} tls.crt: {{ $tls.cert }} - tls.key: {{ $tls.key }} \ No newline at end of file + tls.key: {{ $tls.key }} +{{- end }} diff --git a/helm/values.yaml b/helm/values.yaml index 475d0b8d..ed73198b 100644 --- a/helm/values.yaml +++ b/helm/values.yaml @@ -87,8 +87,13 @@ webhookEnabled: true disableTaggingServiceApi: false routeMaxConcurrentReconciles: -# TLS cert/key for the webhook. If specified, values must be base64 encoded +# TLS cert/key for the webhook. If specified, values must be base64 encoded. +# When not provided, Helm generates self-signed certs using webhookTLS.validityDays. webhookTLS: caCert: cert: - key: \ No newline at end of file + key: + # Validity in days for generated webhook certificates. + # Default preserves existing behavior for installed clusters. + # Set to 365 to opt in to annual certificate rotation. + validityDays: 36500 \ No newline at end of file diff --git a/pkg/config/controller_config.go b/pkg/config/controller_config.go index e16d50ef..b2913de2 100644 --- a/pkg/config/controller_config.go +++ b/pkg/config/controller_config.go @@ -45,7 +45,10 @@ var ServiceNetworkOverrideMode = false var RouteMaxConcurrentReconciles = 1 func ConfigInit() error { - sess, _ := session.NewSession() + sess, err := session.NewSession() + if err != nil { + return fmt.Errorf("failed to create aws session: %w", err) + } metadata := NewEC2Metadata(sess) return configInit(sess, metadata) } @@ -128,10 +131,16 @@ func getClusterName(sess *session.Session) (string, error) { return "", err } ec2Client := ec2.New(sess, &aws.Config{Region: aws.String(region)}) - tagReq := &ec2.DescribeTagsInput{Filters: []*ec2.Filter{{ - Name: aws.String("resource-id"), - Values: []*string{aws.String(instanceId)}, - }}} + tagReq := &ec2.DescribeTagsInput{Filters: []*ec2.Filter{ + { + Name: aws.String("resource-id"), + Values: []*string{aws.String(instanceId)}, + }, + { + Name: aws.String("key"), + Values: []*string{aws.String("aws:eks:cluster-name")}, + }, + }} tagRes, err := ec2Client.DescribeTags(tagReq) if err != nil { return "", err diff --git a/pkg/controllers/pod_controller.go b/pkg/controllers/pod_controller.go index f7cd1894..7072c04d 100644 --- a/pkg/controllers/pod_controller.go +++ b/pkg/controllers/pod_controller.go @@ -44,9 +44,8 @@ func RegisterPodController(log gwlog.Logger, mgr ctrl.Manager) error { return err } -//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=core,resources=pods,verbs=get;list;watch //+kubebuilder:rbac:groups=core,resources=pods/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=core,resources=pods/finalizers,verbs=update func (r *podReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { pod := &corev1.Pod{} diff --git a/scripts/gen-webhook-secret.sh b/scripts/gen-webhook-secret.sh index 12c4d675..658fb3ab 100755 --- a/scripts/gen-webhook-secret.sh +++ b/scripts/gen-webhook-secret.sh @@ -6,29 +6,30 @@ # Generates a certificate for use by the controller webhook # You can also manually re-create the webhook secret using your own PKI -TEMP_KEY=`mktemp` -TEMP_CERT=`mktemp` +TEMP_KEY="$(mktemp)" +TEMP_CERT="$(mktemp)" WEBHOOK_SVC_NAME=webhook-service WEBHOOK_NAME=aws-appnet-gwc-mutating-webhook WEBHOOK_NAMESPACE=aws-application-networking-system WEBHOOK_SECRET_NAME=webhook-cert +WEBHOOK_CERT_VALIDITY_DAYS="${WEBHOOK_CERT_VALIDITY_DAYS:-36500}" echo "Generating certificate for webhook" HOST=${WEBHOOK_SVC_NAME}.${WEBHOOK_NAMESPACE}.svc -openssl req -x509 -nodes -days 36500 -newkey rsa:2048 -keyout ${TEMP_KEY} -out ${TEMP_CERT} -subj "/CN=${HOST}/O=${HOST}" \ +openssl req -x509 -nodes -days "${WEBHOOK_CERT_VALIDITY_DAYS}" -newkey rsa:2048 -keyout "${TEMP_KEY}" -out "${TEMP_CERT}" -subj "/CN=${HOST}/O=${HOST}" \ -addext "subjectAltName = DNS:${HOST}, DNS:${HOST}.cluster.local" -CERT_B64=`cat $TEMP_CERT | base64` +CERT_B64="$(base64 < "${TEMP_CERT}")" echo "Recreating webhook secret" kubectl delete secret $WEBHOOK_SECRET_NAME --namespace $WEBHOOK_NAMESPACE -kubectl create secret tls $WEBHOOK_SECRET_NAME --namespace $WEBHOOK_NAMESPACE --cert=${TEMP_CERT} --key=${TEMP_KEY} +kubectl create secret tls $WEBHOOK_SECRET_NAME --namespace $WEBHOOK_NAMESPACE --cert="${TEMP_CERT}" --key="${TEMP_KEY}" echo "Patching webhook with new cert" kubectl patch mutatingwebhookconfigurations.admissionregistration.k8s.io $WEBHOOK_NAME \ --namespace $WEBHOOK_NAMESPACE --type='json' \ -p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value': '${CERT_B64}'}]" -rm $TEMP_KEY $TEMP_CERT +rm -f "${TEMP_KEY}" "${TEMP_CERT}" echo "Done" \ No newline at end of file