Skip to content

Commit

Permalink
remove emulator custom APIs (#210)
Browse files Browse the repository at this point in the history
  • Loading branch information
matheuscscp authored Jul 18, 2024
1 parent 1e7b1f6 commit 4c2d957
Show file tree
Hide file tree
Showing 17 changed files with 235 additions and 717 deletions.
45 changes: 22 additions & 23 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -124,45 +124,44 @@ test:
break; \
fi; \
done
make run-pod-test TEST_ID=${TEST_ID} HOST_NETWORK=false SERVICE_ACCOUNT_SUBJECT=system:serviceaccount:default:test
make run-pod-test TEST_ID=${TEST_ID} HOST_NETWORK=true SERVICE_ACCOUNT_SUBJECT=system:serviceaccount:kube-system:gke-metadata-server

.PHONY: run-pod-test
run-pod-test:
@if [ "${TEST_ID}" == "" ]; then echo "TEST_ID variable is required."; exit -1; fi
@if [ "${HOST_NETWORK}" == "" ]; then echo "HOST_NETWORK variable is required."; exit -1; fi
@if [ "${SERVICE_ACCOUNT_SUBJECT}" == "" ]; then echo "SERVICE_ACCOUNT_SUBJECT variable is required."; exit -1; fi
kubectl --context kind-kind -n default delete po test || true
sed "s|<TEST_ID>|${TEST_ID}|g" k8s/test-pod.yaml | \
sed "s|<GO_TEST_DIGEST>|$$(cat go-test-digest.txt)|g" | \
sed "s|<HOST_NETWORK>|${HOST_NETWORK}|g" | \
sed "s|<SERVICE_ACCOUNT_SUBJECT>|${SERVICE_ACCOUNT_SUBJECT}|g" | \
tee >(kubectl --context kind-kind apply -f -)
make start-test-pod HOST_NETWORK=false
make start-test-pod HOST_NETWORK=true
while : ; do \
sleep_secs=10; \
echo "Sleeping for $$sleep_secs secs and checking test Pod status..."; \
sleep $$sleep_secs; \
EXIT_CODE_1=$$(kubectl --context kind-kind -n default get po test -o jsonpath='{.status.containerStatuses[0].state.terminated.exitCode}'); \
EXIT_CODE_2=$$(kubectl --context kind-kind -n default get po test -o jsonpath='{.status.containerStatuses[1].state.terminated.exitCode}'); \
EXIT_CODE_1=$$(kubectl --context kind-kind -n default get po test-host-network-false -o jsonpath='{.status.containerStatuses[0].state.terminated.exitCode}'); \
EXIT_CODE_2=$$(kubectl --context kind-kind -n default get po test-host-network-true -o jsonpath='{.status.containerStatuses[0].state.terminated.exitCode}'); \
if [ -n "$$EXIT_CODE_1" ] && [ -n "$$EXIT_CODE_2" ]; then \
echo "Both containers exited"; \
echo "All containers exited"; \
break; \
fi; \
done; \
kubectl --context kind-kind -n kube-system describe $$(kubectl --context kind-kind -n kube-system get po -o name | grep gke); \
kubectl --context kind-kind -n default describe po test; \
kubectl --context kind-kind -n kube-system logs ds/gke-metadata-server; \
kubectl --context kind-kind -n default logs test -c init-gke-metadata-server; \
kubectl --context kind-kind -n default logs test -c test -f; \
kubectl --context kind-kind -n default logs test -c test-gcloud -f; \
echo "Container 'test' exit code: $$EXIT_CODE_1"; \
echo "Container 'test-gcloud' exit code: $$EXIT_CODE_2"; \
kubectl --context kind-kind -n default describe po test-host-network-false; \
kubectl --context kind-kind -n default logs test-host-network-false -c init-gke-metadata-server; \
kubectl --context kind-kind -n default logs test-host-network-false -c test -f; \
kubectl --context kind-kind -n default describe po test-host-network-true; \
kubectl --context kind-kind -n default logs test-host-network-true -c init-gke-metadata-server; \
kubectl --context kind-kind -n default logs test-host-network-true -c test -f; \
echo "Pod 'test-host-network-false' exit code: $$EXIT_CODE_1"; \
echo "Pod 'test-host-network-true' exit code: $$EXIT_CODE_2"; \
if [ "$$EXIT_CODE_1" != "0" ] || [ "$$EXIT_CODE_2" != "0" ]; then \
echo "Failure."; \
exit 1; \
fi; \
echo "Success."

.PHONY: start-test-pod
start-test-pod:
@if [ "${HOST_NETWORK}" == "" ]; then echo "HOST_NETWORK variable is required."; exit -1; fi
kubectl --context kind-kind -n default delete po test-host-network-${HOST_NETWORK} || true
sed "s|<POD_NAME>|test-host-network-${HOST_NETWORK}|g" k8s/test-pod.yaml | \
sed "s|<HOST_NETWORK>|${HOST_NETWORK}|g" | \
sed "s|<GO_TEST_DIGEST>|$$(cat go-test-digest.txt)|g" | \
tee >(kubectl --context kind-kind apply -f -)

.PHONY: helm-diff
helm-diff:
sed "s|<TEST_ID>|test|g" k8s/test-helm-values-watch.yaml | \
Expand Down
46 changes: 20 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@ automatically by Google in the `kube-system` namespace of GKE clusters that have
## Usage

Steps:
1. (Optional) Install [cert-manager](https://cert-manager.io/docs/installation/) in the cluster.
This dependency is used for bootstrapping a self-signed CA and TLS certificate for a `MutatingWebhook`
that adds required networking configurations to the user Pods. To opt-out of this feature, see
[The `iptables` rules](#the-iptables-rules) section.
1. Install [cert-manager](https://cert-manager.io/docs/installation/) in the cluster.
This dependency is used for bootstrapping self-signed CA and TLS certificates for a `MutatingWebhook`
that adds the required networking configuration to the user Pods.
2. Configure GCP Workload Identity Federation for Kubernetes.
3. Deploy `gke-metadata-server` in the cluster using the Workload Identity Provider full name,
obtained after step 2.
4. (Optional but highly recommended) Verify the supply chain signatures.
5. See [`./k8s/test-pod.yaml`](./k8s/test-pod.yaml) for an example of how to configure your Pods
4. See [`./k8s/test-pod.yaml`](./k8s/test-pod.yaml) for an example of how to configure your Pods
and their ServiceAccounts.
5. (Optional but highly recommended) Verify the image signatures to make sure you are
deploying authentic artifacts distributed by this project.

### Configure GCP Workload Identity Federation for Kubernetes

Expand Down Expand Up @@ -125,7 +125,7 @@ Here `{container_version}` is the app version, i.e. the field `.appVersion` at
[`./helm/gke-metadata-server/Chart.yaml`](./helm/gke-metadata-server/Chart.yaml). Check available releases
in the [GitHub Releases Page](https://github.com/matheuscscp/gke-metadata-server/releases).

### Verify the supply chain signatures
### Verify the image signatures

For verifying the images above use the [`cosign`](https://github.com/sigstore/cosign) CLI tool.

Expand Down Expand Up @@ -206,32 +206,26 @@ annotations: # or labels
Prefer using annotations since they are less impactful than labels to the cluster.
Unfortunately, as of July 2024, most cloud providers support customizing only labels
in node pool templates, and some don't even support any customization of labels or
annotations at all. It's up to you how you annotate your Nodes.
in node pool templates, and some don't even support this kind of customization at all.
It's up to you how you annotate/label your Nodes.
You may also simply assign a Google Service Account to the Kubernetes ServiceAccount
of the emulator and use it for all the Pods running on the host network of the cluster
through the Helm Chart value `config.googleServiceAccount`. *But be careful, try to
avoid using shared identities!*
of the emulator and use it for all the Pods of the cluster that are running on the
host network. This can be done through the Helm Chart value `config.googleServiceAccount`.
*But be careful and try to avoid using shared identities like this! This is obviously
dangerous!*

### The `iptables` rules

***Attention:*** The `iptables` rules installed in mutated Pods redirect outbound traffic
in the network namespace of the Pod targeting 169.254.169.254:80 to the emulator port.
If you are using similar tools or equivalent Workload Identity features of managed
Kubernetes from other clouds, *this configuration may have a direct conflict with other
such tools and features.* Especially when mutating Pods that will run on the host network,
*these `iptables` rules will be installed on the network namespace of the Node!*
***Attention:*** The `iptables` rules installed in the network namespace of mutated
Pods will redirect outbound traffic targeting `169.254.169.254:80` to the emulator port
on the Node. If you are using similar tools or equivalent Workload Identity features
of managed Kubernetes from other clouds, *this configuration may have a direct conflict
with other such tools or features.* It's a common practice among cloud providers using
this endpoint to implement such features. Especially when mutating Pods that will run
on the host network, *the rules will be installed on the network namespace of the Node!*
Please be sure to know what you are doing when using this tool inside complex environments.

An alternative to avoid messing with the network stack of your Pods is to add an init
container that downloads the Google Credential Configuration targeting the Node IP
address and emulator port directly rather than `metadata.google.internal:80` and runs
`gcloud auth login`, see the second container example in the [test Pod](./k8s/test-pod.yaml).
If you choose this alternative, you can disable the `MutatingWebhook` feature by setting
`config.mutatingWebhook.enabled` to `false` in the Helm Chart values. This will also
eliminate the need for the `cert-manager` dependency.

## Disclaimer

This project was not created by Google. Enterprise support from Google is
Expand Down
22 changes: 7 additions & 15 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ func newServerCommand() *cobra.Command {
defaultNodeServiceAccountName string
defaultNodeServiceAccountNamespace string
webhookInitNetworkImage string
enableWebhook bool
watchPods bool
watchPodsResyncPeriod time.Duration
watchPodsDisableFallback bool
Expand Down Expand Up @@ -234,24 +233,19 @@ func newServerCommand() *cobra.Command {
DefaultNodeServiceAccount: defaultNodeServiceAccount,
})

var webhookServer *webhook.Server
if enableWebhook {
webhookServer = webhook.New(ctx, webhook.ServerOptions{
ServerAddr: webhookAddr,
InitNetworkImage: webhookInitNetworkImage,
DaemonSetPort: strings.Split(serverAddr, ":")[1],
})
}
webhookServer := webhook.New(ctx, webhook.ServerOptions{
ServerAddr: webhookAddr,
InitNetworkImage: webhookInitNetworkImage,
DaemonSetPort: strings.Split(serverAddr, ":")[1],
})

ctx, cancel := waitForShutdown(ctx)
defer cancel()
if err := s.Shutdown(ctx); err != nil {
return fmt.Errorf("error in server graceful shutdown: %w", err)
}
if webhookServer != nil {
if err := webhookServer.Shutdown(ctx); err != nil {
return fmt.Errorf("error in webhook graceful shutdown: %w", err)
}
if err := webhookServer.Shutdown(ctx); err != nil {
return fmt.Errorf("error in webhook graceful shutdown: %w", err)
}

return nil
Expand All @@ -270,8 +264,6 @@ func newServerCommand() *cobra.Command {
"Namespace of the default service account to be used by pods running on the host network")
cmd.Flags().StringVar(&webhookInitNetworkImage, "webhook-init-network-image", "ghcr.io/matheuscscp/gke-metadata-server:0.6.0",
"Image to be used for the init container that sets up the network namespace for reaching the metadata server")
cmd.Flags().BoolVar(&enableWebhook, "enable-webhook", false,
"Whether or not to enable the webhook server (default false)")
cmd.Flags().BoolVar(&watchPods, "watch-pods", false,
"Whether or not to watch the pods running on the same node (default false)")
cmd.Flags().DurationVar(&watchPodsResyncPeriod, "watch-pods-resync-period", 10*time.Minute,
Expand Down
4 changes: 2 additions & 2 deletions helm/gke-metadata-server/Chart.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ type: application
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.
# Versions are expected to follow Semantic Versioning (https://semver.org/)
version: 0.7.3
version: 0.8.0

# This is the version number of the application being deployed. This version number should be
# incremented each time you make changes to the application. Versions are not expected to
# follow Semantic Versioning. They should reflect the version the application is using.
# It is recommended to use it with quotes.
appVersion: "0.7.3"
appVersion: "0.8.0"
15 changes: 4 additions & 11 deletions helm/gke-metadata-server/templates/daemonset.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,14 @@ spec:
{{- if .Values.config.serverAddr }}
- --server-addr={{ .Values.config.serverAddr }}
{{- end }}
{{- if .Values.config.mutatingWebhook.enabled }}
- --enable-webhook
- --webhook-addr={{ .Values.config.mutatingWebhook.serverAddr }}
{{- if .Values.config.webhookAddr }}
- --webhook-addr={{ .Values.config.webhookAddr }}
{{- end }}
{{- if .Values.image.digest }}
- --webhook-init-network-image={{ .Values.image.repository }}@{{ .Values.image.digest }}
{{- else }}
- --webhook-init-network-image={{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}
{{- end }}
{{- end }}
{{- if .Values.config.logLevel }}
- --log-level={{ .Values.config.logLevel }}
{{- end }}
Expand Down Expand Up @@ -116,11 +115,9 @@ spec:
- name: http
containerPort: {{ index (split ":" .Values.config.serverAddr) "_1" }}
protocol: TCP
{{- if .Values.config.mutatingWebhook.enabled }}
- name: webhook
containerPort: {{ index (split ":" .Values.config.mutatingWebhook.serverAddr) "_1" }}
containerPort: {{ index (split ":" .Values.config.webhookAddr) "_1" }}
protocol: TCP
{{- end }}
livenessProbe:
initialDelaySeconds: 3
httpGet:
Expand All @@ -136,17 +133,13 @@ spec:
volumeMounts:
- name: tmpfs
mountPath: /tmp
{{- if .Values.config.mutatingWebhook.enabled }}
- name: tls
mountPath: /etc/gke-metadata-server/certs
readOnly: true
{{- end }}
volumes:
- name: tmpfs
emptyDir:
medium: Memory
{{- if .Values.config.mutatingWebhook.enabled }}
- name: tls
secret:
secretName: gke-metadata-server-tls
{{- end }}
4 changes: 1 addition & 3 deletions helm/gke-metadata-server/templates/mutatingwebhook.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

{{- if .Values.config.mutatingWebhook.enabled }}
apiVersion: v1
kind: Service
metadata:
Expand All @@ -31,7 +30,7 @@ spec:
app: gke-metadata-server
ports:
- port: 443
targetPort: {{ index (split ":" .Values.config.mutatingWebhook.serverAddr) "_1" }}
targetPort: {{ index (split ":" .Values.config.webhookAddr) "_1" }}
protocol: TCP
---
apiVersion: admissionregistration.k8s.io/v1
Expand Down Expand Up @@ -118,4 +117,3 @@ spec:
name: gke-metadata-server-tls
kind: Issuer
group: cert-manager.io
{{- end }}
4 changes: 1 addition & 3 deletions helm/gke-metadata-server/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,7 @@ config:
googleServiceAccount: ""
logLevel: info # Log level. Accepted values: panic, fatal, error, warning, info, debug, trace
serverAddr: :8080 # Network address where the HTTP server will listen on.
mutatingWebhook:
enabled: true # Whether or not to enable the MutatingWebhook feature.
serverAddr: :8081 # Network address where the MutatingWebhook server will listen on.
webhookAddr: :8081 # Network address where the MutatingWebhook server will listen on.
watchPods:
enabled: true # Whether or not to watch and cache the Pods running on the same Node.
disableFallback: false # Whether or not to disable the simple fallback method for looking up Pods upon cache misses.
Expand Down
55 changes: 0 additions & 55 deletions internal/server/emu_apis.go

This file was deleted.

Loading

0 comments on commit 4c2d957

Please sign in to comment.