k8s/iam: add cloudnativepg postgres and vault + authentik
- PostgreSQL 3-replica HA with pgvector - Vault S3 storage backend (MinIO) - Authentik federated OIDC provider - Vault auto-unseal via postStart hook
This commit is contained in:
@@ -0,0 +1,8 @@
|
||||
# Copy to ~/.authentik/.env and fill in your values.
|
||||
# Port-forward Authentik first: kubectl port-forward svc/authentik-server -n iam 7000:80
|
||||
|
||||
AUTHENTIK_BASE_URL=http://localhost:7000
|
||||
APP_SLUG=go-example
|
||||
OIDC_CLIENT_ID=go-example
|
||||
OIDC_CLIENT_SECRET=your-client-secret-here
|
||||
REDIRECT_URL=http://localhost:8080/callback
|
||||
@@ -0,0 +1,14 @@
|
||||
module homelab/go-example-oidc
|
||||
|
||||
go 1.22
|
||||
|
||||
require (
|
||||
github.com/coreos/go-oidc/v3 v3.11.0
|
||||
github.com/joho/godotenv v1.5.1
|
||||
golang.org/x/oauth2 v0.24.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
|
||||
golang.org/x/crypto v0.25.0 // indirect
|
||||
)
|
||||
@@ -0,0 +1,20 @@
|
||||
github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI=
|
||||
github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
|
||||
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||
golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE=
|
||||
golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
@@ -0,0 +1,90 @@
|
||||
// Minimal OIDC Authorization Code flow against Authentik.
|
||||
//
|
||||
// Setup:
|
||||
// 1. In Authentik create a provider + app (slug "go-example", redirect URI http://localhost:8080/callback).
|
||||
// 2. Fill in ~/.authentik/.env (see .env.example).
|
||||
// 3. go mod tidy && go run .
|
||||
// 4. Open http://localhost:8080/login
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/coreos/go-oidc/v3/oidc"
|
||||
"github.com/joho/godotenv"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// oauthState is a fixed random value for this process — good enough for a local demo.
|
||||
// In production, generate a per-request random state and store it in a cookie.
|
||||
var oauthState = "homelab-oidc-example"
|
||||
|
||||
func main() {
|
||||
// Load ~/.authentik/.env; shell env vars already set take precedence.
|
||||
home, _ := os.UserHomeDir()
|
||||
godotenv.Load(filepath.Join(home, ".authentik", ".env"))
|
||||
|
||||
ctx := context.Background()
|
||||
|
||||
// go-oidc discovers the token endpoint, auth endpoint, and JWKS URI automatically
|
||||
// from Authentik's /.well-known/openid-configuration.
|
||||
issuer := os.Getenv("AUTHENTIK_BASE_URL") + "/application/o/" + os.Getenv("APP_SLUG")
|
||||
provider, err := oidc.NewProvider(ctx, issuer)
|
||||
if err != nil {
|
||||
log.Fatalf("OIDC discovery failed (%s): %v", issuer, err)
|
||||
}
|
||||
|
||||
cfg := &oauth2.Config{
|
||||
ClientID: os.Getenv("OIDC_CLIENT_ID"),
|
||||
ClientSecret: os.Getenv("OIDC_CLIENT_SECRET"),
|
||||
RedirectURL: os.Getenv("REDIRECT_URL"),
|
||||
Endpoint: provider.Endpoint(),
|
||||
Scopes: []string{oidc.ScopeOpenID, "email", "profile"},
|
||||
}
|
||||
verifier := provider.Verifier(&oidc.Config{ClientID: cfg.ClientID})
|
||||
|
||||
// /login — redirect the browser to Authentik's authorization endpoint
|
||||
http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r, cfg.AuthCodeURL(oauthState), http.StatusFound)
|
||||
})
|
||||
|
||||
// /callback — Authentik redirects here with ?code=...&state=...
|
||||
http.HandleFunc("/callback", func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("state") != oauthState {
|
||||
http.Error(w, "state mismatch", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
// Exchange the authorization code for tokens
|
||||
token, err := cfg.Exchange(ctx, r.URL.Query().Get("code"))
|
||||
if err != nil {
|
||||
http.Error(w, "token exchange: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Verify the ID token signature against Authentik's JWKS, then extract claims
|
||||
rawID, _ := token.Extra("id_token").(string)
|
||||
idToken, err := verifier.Verify(ctx, rawID)
|
||||
if err != nil {
|
||||
http.Error(w, "id_token verify: "+err.Error(), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
var claims map[string]any
|
||||
idToken.Claims(&claims)
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
enc := json.NewEncoder(w)
|
||||
enc.SetIndent("", " ")
|
||||
enc.Encode(claims)
|
||||
})
|
||||
|
||||
fmt.Println("open http://localhost:8080/login")
|
||||
log.Fatal(http.ListenAndServe(":8080", nil))
|
||||
}
|
||||
Reference in New Issue
Block a user