-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 7aaca82
Showing
9 changed files
with
447 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
name: Build | ||
|
||
on: | ||
push: | ||
branches: [ main ] | ||
pull_request: | ||
branches: [ main ] | ||
|
||
jobs: | ||
build: | ||
runs-on: ubuntu-latest | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Set up Go | ||
uses: actions/setup-go@v4 | ||
with: | ||
go-version: '1.23' | ||
cache: true | ||
|
||
- name: Verify dependencies | ||
run: go mod verify | ||
|
||
- name: Build | ||
run: go build -v ./... | ||
|
||
- name: Run tests | ||
run: go test -v ./... | ||
|
||
- name: Run golangci-lint | ||
uses: golangci/golangci-lint-action@v3 | ||
with: | ||
version: latest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
name: Release | ||
|
||
on: | ||
push: | ||
tags: [ 'v*' ] | ||
|
||
env: | ||
PLATFORMS: "linux darwin windows" | ||
ARCH: "amd64 arm64" | ||
jobs: | ||
docker: | ||
name: Release docker image | ||
runs-on: ubuntu-latest | ||
if: github.event_name == 'push' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/tags/v')) | ||
permissions: | ||
contents: read | ||
packages: write | ||
|
||
steps: | ||
- uses: actions/checkout@v4 | ||
|
||
- name: Docker meta | ||
id: meta | ||
uses: docker/metadata-action@v5 | ||
with: | ||
images: ghcr.io/${{ github.repository }} | ||
tags: | | ||
type=ref,event=branch | ||
type=ref,event=pr | ||
type=semver,pattern={{version}} | ||
type=semver,pattern={{major}}.{{minor}} | ||
type=sha | ||
- name: Set up QEMU | ||
uses: docker/setup-qemu-action@v3 | ||
|
||
- name: Set up Docker Buildx | ||
uses: docker/setup-buildx-action@v3 | ||
|
||
- name: Login to GitHub Container Registry | ||
uses: docker/login-action@v3 | ||
with: | ||
registry: ghcr.io | ||
username: ${{ github.actor }} | ||
password: ${{ secrets.GITHUB_TOKEN }} | ||
|
||
- name: Build and push | ||
uses: docker/build-push-action@v5 | ||
with: | ||
context: . | ||
platforms: linux/amd64,linux/arm64 | ||
push: true | ||
tags: ${{ steps.meta.outputs.tags }} | ||
labels: ${{ steps.meta.outputs.labels }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.idea/ | ||
.vscode/ | ||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# Build stage | ||
FROM --platform=$BUILDPLATFORM golang:1.22-alpine AS builder | ||
|
||
WORKDIR /app | ||
|
||
# Install necessary build tools | ||
RUN apk add --no-cache git | ||
|
||
# Copy go mod files | ||
COPY go.mod ./ | ||
COPY go.sum ./ | ||
|
||
# Download dependencies | ||
RUN go mod download | ||
|
||
# Copy source code | ||
COPY . . | ||
|
||
# Build arguments for multi-arch support | ||
ARG TARGETARCH | ||
ARG TARGETOS | ||
|
||
# Build the application | ||
RUN CGO_ENABLED=0 GOOS=$TARGETOS GOARCH=$TARGETARCH go build \ | ||
-ldflags="-w -s" \ | ||
-o /whodidthat-controller | ||
|
||
# Final stage | ||
FROM gcr.io/distroless/static:nonroot | ||
|
||
WORKDIR / | ||
|
||
# Copy the binary from builder | ||
COPY --from=builder /whodidthat-controller . | ||
|
||
# Expose the port | ||
EXPOSE 8443 | ||
|
||
# Container runs as nonroot (uid:65532, gid:65532) by default in distroless | ||
ENTRYPOINT ["/whodidthat-controller"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
# WhoDidThat Controller | ||
|
||
A Kubernetes admission controller that automatically tracks resource creation and modification by adding user information labels. | ||
|
||
## Overview | ||
|
||
WhoDidThat Controller is a Kubernetes mutating admission webhook that adds audit labels to resources, tracking who created and last modified them. It adds the following labels: | ||
- `audit.k8s.io/created-by`: The user who created the resource | ||
- `audit.k8s.io/last-modified-by`: The user who last modified the resource | ||
|
||
## Features | ||
|
||
- Tracks resource creation and modification events | ||
- Adds user information as Kubernetes labels | ||
- Supports all Kubernetes resource types | ||
- Multi-architecture support (AMD64 and ARM64) | ||
- Minimal footprint using distroless container image | ||
|
||
## Prerequisites | ||
|
||
- Kubernetes cluster 1.16+ | ||
- kubectl configured to access your cluster | ||
- cert-manager (recommended for TLS certificate management) | ||
|
||
## Installation | ||
|
||
### Using Helm (Recommended) | ||
|
||
1. Add the repository: | ||
```bash | ||
helm repo add whodidthat https://x0ddf.github.io/whodidthat-controller | ||
``` | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package controllers | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"strings" | ||
|
||
admissionv1 "k8s.io/api/admission/v1" | ||
"k8s.io/apimachinery/pkg/runtime" | ||
"k8s.io/apimachinery/pkg/runtime/serializer" | ||
) | ||
|
||
const ( | ||
CreatedByLabel = "audit.k8s.io/created-by" | ||
ModifiedByLabel = "audit.k8s.io/last-modified-by" | ||
) | ||
|
||
type AdmissionController struct { | ||
decoder runtime.Decoder | ||
} | ||
|
||
func NewAdmissionController() *AdmissionController { | ||
return &AdmissionController{ | ||
decoder: serializer.NewCodecFactory(runtime.NewScheme()).UniversalDeserializer(), | ||
} | ||
} | ||
|
||
func (ac *AdmissionController) Handle(w http.ResponseWriter, r *http.Request) { | ||
// Parse the AdmissionReview | ||
admissionReview := &admissionv1.AdmissionReview{} | ||
if err := json.NewDecoder(r.Body).Decode(admissionReview); err != nil { | ||
http.Error(w, fmt.Sprintf("could not parse admission review: %v", err), http.StatusBadRequest) | ||
return | ||
} | ||
|
||
// Get the requesting user | ||
userName := ExtractUserMeta(admissionReview.Request) | ||
|
||
// Create patch operations | ||
var patches []map[string]interface{} | ||
|
||
// Handle different operations | ||
switch admissionReview.Request.Operation { | ||
case admissionv1.Create: | ||
patches = append(patches, map[string]interface{}{ | ||
"op": "add", | ||
"path": "/metadata/labels/" + escapeJSONPointer(CreatedByLabel), | ||
"value": userName, | ||
}) | ||
case admissionv1.Update: | ||
patches = append(patches, map[string]interface{}{ | ||
"op": "add", | ||
"path": "/metadata/labels/" + escapeJSONPointer(ModifiedByLabel), | ||
"value": userName, | ||
}) | ||
} | ||
|
||
// Create the patch bytes | ||
patchBytes, err := json.Marshal(patches) | ||
if err != nil { | ||
http.Error(w, fmt.Sprintf("could not marshal patch: %v", err), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
// Create the admission response | ||
admissionResponse := &admissionv1.AdmissionResponse{ | ||
UID: admissionReview.Request.UID, | ||
Allowed: true, | ||
Patch: patchBytes, | ||
PatchType: func() *admissionv1.PatchType { | ||
pt := admissionv1.PatchTypeJSONPatch | ||
return &pt | ||
}(), | ||
} | ||
|
||
// Return the admission review | ||
admissionReview.Response = admissionResponse | ||
resp, err := json.Marshal(admissionReview) | ||
if err != nil { | ||
http.Error(w, fmt.Sprintf("could not marshal response: %v", err), http.StatusInternalServerError) | ||
return | ||
} | ||
|
||
w.Header().Set("Content-Type", "application/json") | ||
w.Write(resp) | ||
} | ||
|
||
// escapeJSONPointer escapes / in label names for JSON Pointer compliance | ||
func escapeJSONPointer(s string) string { | ||
return strings.ReplaceAll(s, "/", "~1") | ||
} | ||
|
||
func ExtractUserMeta(request *admissionv1.AdmissionRequest) string { | ||
username := "" | ||
extraInfo := request.UserInfo.Extra | ||
if v, ok := extraInfo["username"]; ok && len(v) > 0 { | ||
username = v.String() | ||
} | ||
if v, ok := extraInfo["sessionName"]; ok && len(v) > 0 { | ||
username = v.String() | ||
} | ||
if v, ok := extraInfo["arn"]; ok && len(v) > 0 { | ||
username = v.String() | ||
} | ||
if len(username) == 0 { | ||
username = request.UserInfo.Username | ||
} | ||
return username | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
module github.com/x0ddf/whodidthat-controller | ||
|
||
go 1.23.6 | ||
|
||
toolchain go1.23.6 | ||
|
||
require ( | ||
k8s.io/api v0.32.1 | ||
k8s.io/apimachinery v0.32.1 | ||
) | ||
|
||
require ( | ||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect | ||
github.com/go-logr/logr v1.4.2 // indirect | ||
github.com/gogo/protobuf v1.3.2 // indirect | ||
github.com/google/gofuzz v1.2.0 // indirect | ||
github.com/json-iterator/go v1.1.12 // indirect | ||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||
github.com/modern-go/reflect2 v1.0.2 // indirect | ||
github.com/x448/float16 v0.8.4 // indirect | ||
golang.org/x/net v0.34.0 // indirect | ||
golang.org/x/text v0.22.0 // indirect | ||
gopkg.in/inf.v0 v0.9.1 // indirect | ||
gopkg.in/yaml.v2 v2.4.0 // indirect | ||
k8s.io/klog/v2 v2.130.1 // indirect | ||
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect | ||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect | ||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect | ||
sigs.k8s.io/yaml v1.4.0 // indirect | ||
) |
Oops, something went wrong.