Skip to content

Commit 7fe604e

Browse files
authored
feat(auth): add local development environment with Kind and Keycloak for OIDC (containers#354)
* Initial KinD setup Signed-off-by: Matthias Wessendorf <[email protected]> * Initial Keycloak container setup Signed-off-by: Matthias Wessendorf <[email protected]> * Adding an initial realm setup Signed-off-by: Matthias Wessendorf <[email protected]> * Adding OIDC issuer and realm updates, adding cert-manager and handling self-signed certificates Signed-off-by: Matthias Wessendorf <[email protected]> * Updates to script b/c of invalid auth config Signed-off-by: Matthias Wessendorf <[email protected]> * Adjusting ports and better support for mac/podman Signed-off-by: Matthias Wessendorf <[email protected]> * Addressing review comments: * do not expose all internal tasks, just keep the important targets documents * remove the keycloak-forward * move binaries for dev tools to _output * generate a configuration TOML file into the _output folder Signed-off-by: Matthias Wessendorf <[email protected]> --------- Signed-off-by: Matthias Wessendorf <[email protected]>
1 parent 0c78a1e commit 7fe604e

File tree

11 files changed

+1163
-0
lines changed

11 files changed

+1163
-0
lines changed

Makefile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,3 +113,43 @@ lint: golangci-lint ## Lint the code
113113
.PHONY: update-readme-tools
114114
update-readme-tools: ## Update the README.md file with the latest toolsets
115115
go run ./internal/tools/update-readme/main.go README.md
116+
117+
##@ Tools
118+
119+
.PHONY: tools
120+
tools: ## Install all required tools (kind) to ./_output/bin/
121+
@echo "Checking and installing required tools to ./_output/bin/ ..."
122+
@if [ -f _output/bin/kind ]; then echo "[OK] kind already installed"; else echo "Installing kind..."; $(MAKE) -s kind; fi
123+
@echo "All tools ready!"
124+
125+
##@ Local Development
126+
127+
.PHONY: local-env-setup
128+
local-env-setup: ## Setup complete local development environment with Kind cluster
129+
@echo "========================================="
130+
@echo "Kubernetes MCP Server - Local Setup"
131+
@echo "========================================="
132+
$(MAKE) tools
133+
$(MAKE) kind-create-cluster
134+
$(MAKE) keycloak-install
135+
$(MAKE) build
136+
@echo ""
137+
@echo "========================================="
138+
@echo "Local environment ready!"
139+
@echo "========================================="
140+
@echo ""
141+
@echo "Configuration file generated:"
142+
@echo " _output/config.toml"
143+
@echo ""
144+
@echo "Run the MCP server with:"
145+
@echo " ./$(BINARY_NAME) --port 8080 --config _output/config.toml"
146+
@echo ""
147+
@echo "Or run with MCP inspector:"
148+
@echo " npx @modelcontextprotocol/inspector@latest \$$(pwd)/$(BINARY_NAME) --config _output/config.toml"
149+
150+
.PHONY: local-env-teardown
151+
local-env-teardown: ## Tear down the local Kind cluster
152+
$(MAKE) kind-delete-cluster
153+
154+
# Include build configuration files
155+
-include build/*.mk

build/keycloak.mk

Lines changed: 448 additions & 0 deletions
Large diffs are not rendered by default.

build/kind.mk

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Kind cluster management
2+
3+
KIND_CLUSTER_NAME ?= kubernetes-mcp-server
4+
5+
# Detect container engine (docker or podman)
6+
CONTAINER_ENGINE ?= $(shell command -v docker 2>/dev/null || command -v podman 2>/dev/null)
7+
8+
.PHONY: kind-create-certs
9+
kind-create-certs:
10+
@if [ ! -f _output/cert-manager-ca/ca.crt ]; then \
11+
echo "Creating placeholder CA certificate for bind mount..."; \
12+
./hack/generate-placeholder-ca.sh; \
13+
else \
14+
echo "✅ Placeholder CA already exists"; \
15+
fi
16+
17+
.PHONY: kind-create-cluster
18+
kind-create-cluster: kind kind-create-certs
19+
@# Set KIND provider for podman on Linux
20+
@if [ "$(shell uname -s)" != "Darwin" ] && echo "$(CONTAINER_ENGINE)" | grep -q "podman"; then \
21+
export KIND_EXPERIMENTAL_PROVIDER=podman; \
22+
fi; \
23+
if $(KIND) get clusters 2>/dev/null | grep -q "^$(KIND_CLUSTER_NAME)$$"; then \
24+
echo "Kind cluster '$(KIND_CLUSTER_NAME)' already exists, skipping creation"; \
25+
else \
26+
echo "Creating Kind cluster '$(KIND_CLUSTER_NAME)'..."; \
27+
$(KIND) create cluster --name $(KIND_CLUSTER_NAME) --config dev/config/kind/cluster.yaml; \
28+
echo "Adding ingress-ready label to control-plane node..."; \
29+
kubectl label node $(KIND_CLUSTER_NAME)-control-plane ingress-ready=true --overwrite; \
30+
echo "Installing nginx ingress controller..."; \
31+
kubectl apply -f dev/config/ingress/nginx-ingress.yaml; \
32+
echo "Waiting for ingress controller to be ready..."; \
33+
kubectl wait --namespace ingress-nginx --for=condition=ready pod --selector=app.kubernetes.io/component=controller --timeout=90s; \
34+
echo "✅ Ingress controller ready"; \
35+
echo "Installing cert-manager..."; \
36+
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.16.2/cert-manager.yaml; \
37+
echo "Waiting for cert-manager to be ready..."; \
38+
kubectl wait --namespace cert-manager --for=condition=available deployment/cert-manager --timeout=120s; \
39+
kubectl wait --namespace cert-manager --for=condition=available deployment/cert-manager-cainjector --timeout=120s; \
40+
kubectl wait --namespace cert-manager --for=condition=available deployment/cert-manager-webhook --timeout=120s; \
41+
echo "✅ cert-manager ready"; \
42+
echo "Creating cert-manager ClusterIssuer..."; \
43+
sleep 5; \
44+
kubectl apply -f dev/config/cert-manager/selfsigned-issuer.yaml; \
45+
echo "✅ ClusterIssuer created"; \
46+
echo "Adding /etc/hosts entry for Keycloak in control plane..."; \
47+
if command -v docker >/dev/null 2>&1 && docker ps --filter "name=$(KIND_CLUSTER_NAME)-control-plane" --format "{{.Names}}" | grep -q "$(KIND_CLUSTER_NAME)-control-plane"; then \
48+
docker exec $(KIND_CLUSTER_NAME)-control-plane bash -c 'grep -q "keycloak.127-0-0-1.sslip.io" /etc/hosts || echo "127.0.0.1 keycloak.127-0-0-1.sslip.io" >> /etc/hosts'; \
49+
elif command -v podman >/dev/null 2>&1 && podman ps --filter "name=$(KIND_CLUSTER_NAME)-control-plane" --format "{{.Names}}" | grep -q "$(KIND_CLUSTER_NAME)-control-plane"; then \
50+
podman exec $(KIND_CLUSTER_NAME)-control-plane bash -c 'grep -q "keycloak.127-0-0-1.sslip.io" /etc/hosts || echo "127.0.0.1 keycloak.127-0-0-1.sslip.io" >> /etc/hosts'; \
51+
fi; \
52+
echo "✅ /etc/hosts entry added"; \
53+
fi
54+
55+
.PHONY: kind-delete-cluster
56+
kind-delete-cluster: kind
57+
@# Set KIND provider for podman on Linux
58+
@if [ "$(shell uname -s)" != "Darwin" ] && echo "$(CONTAINER_ENGINE)" | grep -q "podman"; then \
59+
export KIND_EXPERIMENTAL_PROVIDER=podman; \
60+
fi; \
61+
$(KIND) delete cluster --name $(KIND_CLUSTER_NAME)

build/tools.mk

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Tools
2+
3+
# Platform detection
4+
OS := $(shell uname -s | tr '[:upper:]' '[:lower:]')
5+
ARCH := $(shell uname -m | tr '[:upper:]' '[:lower:]')
6+
ifeq ($(ARCH),x86_64)
7+
ARCH = amd64
8+
endif
9+
ifeq ($(ARCH),aarch64)
10+
ARCH = arm64
11+
endif
12+
13+
KIND = _output/bin/kind
14+
KIND_VERSION = v0.30.0
15+
$(KIND):
16+
@mkdir -p _output/bin
17+
GOBIN=$(PWD)/_output/bin go install sigs.k8s.io/kind@$(KIND_VERSION)
18+
19+
.PHONY: kind
20+
kind: $(KIND) ## Download kind locally if necessary
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
apiVersion: cert-manager.io/v1
2+
kind: ClusterIssuer
3+
metadata:
4+
name: selfsigned-issuer
5+
spec:
6+
selfSigned: {}
7+
---
8+
apiVersion: cert-manager.io/v1
9+
kind: Certificate
10+
metadata:
11+
name: selfsigned-ca
12+
namespace: cert-manager
13+
spec:
14+
isCA: true
15+
commonName: selfsigned-ca
16+
secretName: selfsigned-ca-secret
17+
privateKey:
18+
algorithm: ECDSA
19+
size: 256
20+
issuerRef:
21+
name: selfsigned-issuer
22+
kind: ClusterIssuer
23+
group: cert-manager.io
24+
---
25+
apiVersion: cert-manager.io/v1
26+
kind: ClusterIssuer
27+
metadata:
28+
name: selfsigned-ca-issuer
29+
spec:
30+
ca:
31+
secretName: selfsigned-ca-secret

0 commit comments

Comments
 (0)