{"id":36578878,"url":"https://github.com/alam0rt/tailscale-sidecar-injector","last_synced_at":"2026-01-12T07:40:50.491Z","repository":{"id":97135171,"uuid":"571087664","full_name":"alam0rt/tailscale-sidecar-injector","owner":"alam0rt","description":null,"archived":false,"fork":false,"pushed_at":"2025-04-23T03:36:13.000Z","size":83,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-05-01T08:46:37.121Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alam0rt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2022-11-27T05:30:21.000Z","updated_at":"2025-04-30T22:57:09.000Z","dependencies_parsed_at":null,"dependency_job_id":"442372af-49ac-4126-a50d-5f9f9acd8ab0","html_url":"https://github.com/alam0rt/tailscale-sidecar-injector","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/alam0rt/tailscale-sidecar-injector","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alam0rt%2Ftailscale-sidecar-injector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alam0rt%2Ftailscale-sidecar-injector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alam0rt%2Ftailscale-sidecar-injector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alam0rt%2Ftailscale-sidecar-injector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alam0rt","download_url":"https://codeload.github.com/alam0rt/tailscale-sidecar-injector/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alam0rt%2Ftailscale-sidecar-injector/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28336639,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-12T06:09:07.588Z","status":"ssl_error","status_checked_at":"2026-01-12T06:05:18.301Z","response_time":98,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-01-12T07:40:49.475Z","updated_at":"2026-01-12T07:40:50.486Z","avatar_url":"https://github.com/alam0rt.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tailscale-sidecar-injector\n\nThis is a simple [Kubernetes admission webhook](https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/). It is meant to be used as a validating and mutating admission webhook only and does not support any controller logic. It has been developed as a simple Go web service without using any framework or boilerplate such as kubebuilder.\n\nThis project is aimed at illustrating how to build a fully functioning admission webhook in the simplest way possible. Most existing examples found on the web rely on heavy machinery using powerful frameworks, yet fail to illustrate how to implement a lightweight webhook that can do much needed actions such as rejecting a pod for compliance reasons, or inject helpful environment variables.\n\nFor readability, this project has been stripped of the usual production items such as: observability instrumentation, release scripts, redundant deployment configurations, etc. As such, it is not meant to use as-is in a production environment. This project is, in fact, a simplified fork of a system used accross all Kubernetes production environments at Slack.\n\n## Installation\nThis project can fully run locally and includes automation to deploy a local Kubernetes cluster (using Kind).\n\n### Requirements\n* Docker\n* kubectl\n* [Kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation)\n* Go \u003e=1.16 (optional)\n\n## Usage\n### Create Cluster\nFirst, we need to create a Kubernetes cluster:\n```\n❯ make cluster\n\n🔧 Creating Kubernetes cluster...\nkind create cluster --config dev/manifests/kind/kind.cluster.yaml\nCreating cluster \"kind\" ...\n ✓ Ensuring node image (kindest/node:v1.21.1) 🖼\n ✓ Preparing nodes 📦\n ✓ Writing configuration 📜\n ✓ Starting control-plane 🕹️\n ✓ Installing CNI 🔌\n ✓ Installing StorageClass 💾\nSet kubectl context to \"kind-kind\"\nYou can now use your cluster with:\n\nkubectl cluster-info --context kind-kind\n\nHave a nice day! 👋\n```\n\nMake sure that the Kubernetes node is ready:\n```\n❯ kubectl get nodes\nNAME                 STATUS   ROLES                  AGE     VERSION\nkind-control-plane   Ready    control-plane,master   3m25s   v1.21.1\n```\n\nAnd that system pods are running happily:\n```\n❯ kubectl -n kube-system get pods\nNAME                                         READY   STATUS    RESTARTS   AGE\ncoredns-558bd4d5db-thwvj                     1/1     Running   0          3m39s\ncoredns-558bd4d5db-w85ks                     1/1     Running   0          3m39s\netcd-kind-control-plane                      1/1     Running   0          3m56s\nkindnet-84slq                                1/1     Running   0          3m40s\nkube-apiserver-kind-control-plane            1/1     Running   0          3m54s\nkube-controller-manager-kind-control-plane   1/1     Running   0          3m56s\nkube-proxy-4h6sj                             1/1     Running   0          3m40s\nkube-scheduler-kind-control-plane            1/1     Running   0          3m54s\n```\n\n### Deploy Admission Webhook\nTo configure the cluster to use the admission webhook and to deploy said webhook, simply run:\n```\n❯ make deploy\n\n📦 Building simple-kubernetes-webhook Docker image...\ndocker build -t simple-kubernetes-webhook:latest .\n[+] Building 14.3s (13/13) FINISHED\n...\n\n📦 Pushing admission-webhook image into Kind's Docker daemon...\nkind load docker-image simple-kubernetes-webhook:latest\nImage: \"simple-kubernetes-webhook:latest\" with ID \"sha256:46b8603bcc11a8fa1825190d3ed99c099096395b22a709e13ec6e7ae2f54014d\" not yet present on node \"kind-control-plane\", loading...\n\n⚙️  Applying cluster config...\nkubectl apply -f dev/manifests/cluster-config/\nnamespace/apps created\nmutatingwebhookconfiguration.admissionregistration.k8s.io/simple-kubernetes-webhook.acme.com created\nvalidatingwebhookconfiguration.admissionregistration.k8s.io/simple-kubernetes-webhook.acme.com created\n\n🚀 Deploying simple-kubernetes-webhook...\nkubectl apply -f dev/manifests/webhook/\ndeployment.apps/simple-kubernetes-webhook created\nservice/simple-kubernetes-webhook created\nsecret/simple-kubernetes-webhook-tls created\n```\n\nThen, make sure the admission webhook pod is running (in the `default` namespace):\n```\n❯ kubectl get pods\nNAME                                        READY   STATUS    RESTARTS   AGE\nsimple-kubernetes-webhook-77444566b7-wzwmx   1/1     Running   0          2m21s\n```\n\nYou can stream logs from it:\n```\n❯ make logs\n\n🔍 Streaming simple-kubernetes-webhook logs...\nkubectl logs -l app=simple-kubernetes-webhook -f\ntime=\"2021-09-03T04:59:10Z\" level=info msg=\"Listening on port 443...\"\ntime=\"2021-09-03T05:02:21Z\" level=debug msg=healthy uri=/health\n```\n\nAnd hit it's health endpoint from your local machine:\n```\n❯ curl -k https://localhost:8443/health\nOK\n```\n\n### Deploying pods\nDeploy a valid test pod that gets succesfully created:\n```\n❯ make pod\n\n🚀 Deploying test pod...\nkubectl apply -f dev/manifests/pods/lifespan-seven.pod.yaml\npod/lifespan-seven created\n```\nYou should see in the admission webhook logs that the pod got mutated and validated.\n\nDeploy a non valid pod that gets rejected:\n```\n❯ make bad-pod\n\n🚀 Deploying \"bad\" pod...\nkubectl apply -f dev/manifests/pods/bad-name.pod.yaml\nError from server: error when creating \"dev/manifests/pods/bad-name.pod.yaml\": admission webhook \"simple-kubernetes-webhook.acme.com\" denied the request: pod name contains \"offensive\"\n```\nYou should see in the admission webhook logs that the pod validation failed. It's possible you will also see that the pod was mutated, as webhook configurations are not ordered.\n\n## Testing\nUnit tests can be run with the following command:\n```\n$ make test\ngo test ./...\n?   \tgithub.com/slackhq/simple-kubernetes-webhook\t[no test files]\nok  \tgithub.com/slackhq/simple-kubernetes-webhook/pkg/admission\t0.611s\nok  \tgithub.com/slackhq/simple-kubernetes-webhook/pkg/mutation\t1.064s\nok  \tgithub.com/slackhq/simple-kubernetes-webhook/pkg/validation\t0.749s\n```\n\n## Admission Logic\nA set of validations and mutations are implemented in an extensible framework. Those happen on the fly when a pod is deployed and no further resources are tracked and updated (ie. no controller logic).\n\n### Validating Webhooks\n#### Implemented\n- [name validation](pkg/validation/name_validator.go): validates that a pod name doesn't contain any offensive string\n\n#### How to add a new pod validation\nTo add a new pod mutation, create a file `pkg/validation/MUTATION_NAME.go`, then create a new struct implementing the `validation.podValidator` interface.\n\n### Mutating Webhooks\n#### Implemented\n- [inject env](pkg/mutation/inject_env.go): inject environment variables into the pod such as `KUBE: true`\n- [minimum pod lifespan](pkg/mutation/minimum_lifespan.go): inject a set of tolerations used to match pods to nodes of a certain age, the tolerations injected are controlled via the `acme.com/lifespan-requested` pod label.\n\n#### How to add a new pod mutation\nTo add a new pod mutation, create a file `pkg/mutation/MUTATION_NAME.go`, then create a new struct implementing the `mutation.podMutator` interface.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falam0rt%2Ftailscale-sidecar-injector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falam0rt%2Ftailscale-sidecar-injector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falam0rt%2Ftailscale-sidecar-injector/lists"}