This module implements the AKS Secure TLS Bootstrap client — a single-shot CLI invoked on an AKS agent node to obtain a kubelet client credential, replacing the upstream Kubernetes TLS bootstrap token-based flow with one rooted in Azure VM attestation and AAD (Entra ID).
For repo-wide context, see the root README.
When invoked, the client:
- Checks the existing kubeconfig. If
--kubeconfigalready points to a valid (and optionally authorized) kubeconfig, the client exits successfully without contacting the bootstrap server. - Acquires an AAD access token. Uses the kubelet MSI defined in the cloud provider config (or
--user-assigned-identity-idoverride) via IMDS for the AAD audience configured by--aad-resource. - Opens a gRPC connection to the bootstrap server identified by
--apiserver-fqdn, pinned to the cluster CA at--cluster-ca-file, with the configured--tls-min-versionand ALPN--next-protovalue, authenticated by the AAD token. - Fetches VM instance data from IMDS to derive the VM's Azure Resource ID.
GetNonce— requests a fresh nonce from the server.- Fetches attested data from IMDS, signed over the nonce.
- Generates a kubelet client CSR and matching private key on disk under
--cert-dir. GetCredential— submits the CSR + attested data; receives a signed kubelet client certificate.- Writes a kubeconfig at
--kubeconfigreferencing the new cert/key, suitable for direct consumption by the kubelet.
Each phase has its own configurable timeout and is recorded in the bootstrap telemetry trace emitted as a guest-agent event.
cmd/client/main.go CLI entrypoint, flag/config parsing, telemetry emission
internal/
bootstrap/ Bootstrap protocol orchestration
bootstrap.go / client.go Top-level Bootstrap() and step-by-step orchestrator
auth.go AAD token acquisition (azidentity) + gRPC credentials
config.go CLI/JSON config schema, defaults, validation
csr.go Kubelet client CSR + private key generation
grpc.go gRPC dial options, retry, ALPN setup
event.go Guest-agent event payload writer
errors.go Typed bootstrap errors and classification
build/ Version stamping (-ldflags injects build.version)
cloud/ Azure cloud provider config loader
http/ Shared HTTP client with retries
imds/ IMDS client (instance + attested data)
kubeconfig/ Kubeconfig generation + existing-kubeconfig validation
log/ zap logger wiring (file + console, verbose mode)
telemetry/ Span/trace tracking emitted in bootstrap result
testutil/ Cert/key fixtures used in tests
hack/
upload.sh Cross-build + upload to Azure Storage
linux/install.sh Node-side installer (Linux) (FOR DEV PURPOSES ONLY)
windows/install.ps1 Node-side installer (Windows) (FOR DEV PURPOSES ONLY)
- Go 1.24+ (matches
go.mod) makegolangci-lintfor local linting (CI uses .github/workflows/client-golangci-lint.yaml)
make test # go test ./...
make test-coverage # produces coverage_raw.out
make generate # regenerates gomock-based mocks (installs mockgen into bin/)
make build # OS=linux ARCH=amd64 (default), CGO disabled
make build OS=linux ARCH=arm64
make build OS=windows ARCH=amd64 EXTENSION=.exe
make build-all # builds all supported OS/ARCH combos
make build-prod # CGO_ENABLED=1 GOEXPERIMENT=systemcrypto for FIPS-compliant buildsThe VERSION value is derived from git describe and stamped into the binary via -ldflags against internal/build/build.go; pass VERSION=... to override.
Interface mocks live alongside the package they mock under mocks/ subdirectories (e.g. internal/imds/mocks/mock_imds.go). They are generated by go.uber.org/mock/mockgen via //go:generate directives; run make generate after changing an interface.
The client depends on the gRPC stubs generated from ../service/proto. When the proto contract changes:
- Update the proto files and run
make generatein ../service. - Tag and release a new version of the
servicemodule. - Bump the dependency in this module's go.mod and adjust call sites under internal/bootstrap/.