-
Notifications
You must be signed in to change notification settings - Fork 6
fix: bootstrap trusted kubelet serving certificates #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -128,13 +128,37 @@ Manager flags worth knowing (edit `config/manager/manager.yaml` `args`): | |
| | Flag | Default | Meaning | | ||
| |---|---|---| | ||
| | `--kubelet-bind-address` | `:10250` | Where the kubelet log endpoint listens — the address the API server proxies `kubectl logs` to. Set it empty to disable the endpoint, which disables logs and nothing else. | | ||
| | `--kubelet-serving-tls-bootstrap` | `true` | Request a certificate for the advertised Pod IP from the `kubernetes.io/kubelet-serving` signer. Until it is approved and issued, the endpoint retains its self-signed fallback. Disable this only when the API server does not verify kubelet serving certificates. | | ||
| | `--kubelet-client-ca` | *(empty)* | PEM bundle of CAs whose client certificates are accepted on that port. **Empty means client certificates are not verified**, so anything able to reach port 10250 can read the logs of any Pod on Nebula's virtual nodes. Set it to your API server's kubelet client CA to require mTLS, or keep the port closed with a NetworkPolicy. The default is open because which CA signs that client cert is not portable — kubeadm uses the cluster CA, EKS/GKE their own — so requiring it by default would break logs on managed control planes. | | ||
|
|
||
| The endpoint needs `POD_IP` (projected via `fieldRef` in `config/manager/manager.yaml`) | ||
| because virtual nodes advertise the leader's Pod IP, not a Service. Running the manager | ||
| off-cluster leaves it unset, and logs degrade to unsupported. See | ||
| [kubelet-api.md](kubelet-api.md). | ||
|
|
||
| The Kubernetes signer does not approve kubelet-serving requests itself. On a cluster | ||
| without a dedicated approver, inspect and approve Nebula's request after each manager | ||
| Pod recreation and certificate renewal: | ||
|
|
||
| ```bash | ||
| CSR=$(kubectl get csr \ | ||
| -l app.kubernetes.io/name=nebula,app.kubernetes.io/component=kubelet-serving-certificate \ | ||
| --sort-by=.metadata.creationTimestamp -o name | tail -n1) | ||
|
|
||
| # Confirm the requested IP SAN matches the manager Pod IP before approving it. | ||
| kubectl get csr "$CSR" -o jsonpath='{.spec.request}' \ | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not right, no need to specify the resource type for $CSR. |
||
| | openssl base64 -d -A | openssl req -text -noout | ||
| kubectl -n nebula-system get pod -l control-plane=controller-manager -o wide | ||
|
|
||
| kubectl certificate approve "$CSR" | ||
| kubectl -n nebula-system logs deploy/nebula-controller-manager \ | ||
| | grep 'installed trusted kubelet serving certificate' | ||
| ``` | ||
|
|
||
| An installation with an external CSR approver should restrict it to requests that | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Have you tried yourself, I tested this by following the instructions here but seems still error: |
||
| match Nebula's ServiceAccount, `system:nodes` organization, manager Pod identity, and | ||
| current Pod IP. Nebula intentionally receives no permission to approve certificates. | ||
|
|
||
| --- | ||
|
|
||
| ## Manual deployment | ||
|
|
@@ -187,6 +211,10 @@ kubectl -n nebula-system logs deploy/nebula-controller-manager | grep -i provide | |
| # Virtual nodes exist, one per registered provider. | ||
| kubectl get nodes -l nebula.inftyai.com/provider | ||
|
|
||
| # Kubelet serving CSR is signed (required by control planes that verify kubelet TLS). | ||
| kubectl get csr \ | ||
| -l app.kubernetes.io/name=nebula,app.kubernetes.io/component=kubelet-serving-certificate | ||
|
|
||
| # Webhook TLS is wired: the caBundle matches the serving cert Secret. | ||
| diff <(kubectl get secret nebula-webhook-server-cert -n nebula-system -o jsonpath='{.data.tls\.crt}') \ | ||
| <(kubectl get mutatingwebhookconfiguration nebula-mutating-webhook-configuration \ | ||
|
|
@@ -197,7 +225,12 @@ diff <(kubectl get secret nebula-webhook-server-cert -n nebula-system -o jsonpat | |
| A pool referencing an unregistered provider shows it plainly: | ||
|
|
||
| ```bash | ||
| kubectl get nodepool <name> -o jsonpath='{.status.conditions}' | ||
| kubectl get nodepools | ||
| # NAME STATUS STRATEGY PROVIDERS AGE | ||
| # gpu-pool False Ordered modal,aws 2m | ||
|
|
||
| # Inspect the condition reason and message when STATUS is False. | ||
| kubectl get nodepool <name> -o jsonpath='{.status.conditions[?(@.type=="Ready")]}' | ||
| # Ready=False / UnknownProvider means that provider's creds are missing or wrong. | ||
| ``` | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| # NodePool status printer column | ||
|
|
||
| ## Context | ||
|
|
||
| `NodePool.status.conditions` already reports whether a pool can be used. The | ||
| controller owns a standard `Ready` condition and sets it to `True` for a valid | ||
| pool or `False` when an environment-dependent validation, such as provider | ||
| registration, fails. However, the default `kubectl get nodepools` table does not | ||
| show that signal, so operators must request the full object or write a JSONPath. | ||
|
|
||
| ## Decision | ||
|
|
||
| Add a `Status` CRD printer column whose JSONPath selects the status of the | ||
| `Ready` condition: | ||
|
|
||
| ```text | ||
| .status.conditions[?(@.type=="Ready")].status | ||
| ``` | ||
|
|
||
| The column is derived directly by the Kubernetes API server when it renders the | ||
| table. No duplicate status field or controller change is introduced. This keeps | ||
| the condition as the single source of truth and uses the standard condition | ||
| values `True`, `False`, and `Unknown`. | ||
|
|
||
| The column appears before policy details so pool health is visible immediately: | ||
|
|
||
| ```text | ||
| NAME STATUS STRATEGY PROVIDERS AGE | ||
| gpu-pool True Ordered modal,runpod 2m | ||
| ``` | ||
|
|
||
| Before the controller has written the `Ready` condition, the table cell has no | ||
| value. This is preferable to manufacturing a fourth status value because absence | ||
| already means the controller has not observed the object. | ||
|
|
||
| ## Compatibility and rollout | ||
|
|
||
| This is an additive change to `additionalPrinterColumns`; the stored and served | ||
| resource schema is unchanged. Existing clients that read `NodePool` objects are | ||
| unaffected. Installing the regenerated CRD is sufficient to enable the column | ||
| for existing pools, and the next `kubectl get` uses their existing conditions. | ||
|
|
||
| ## Verification | ||
|
|
||
| Generation is checked into `config/crd/bases`. Regenerating the manifests keeps | ||
| the CRD printer column aligned with the marker in `nodepool_types.go`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's disable this by default.