This release narrows the RBAC of two addons: system-upgrade-controller and redis-operator. lo provision now stops on a busy kubehz platform instead of a silent skip. The kubehz heartbeat works again on clusters with more than about 18 nodes. Read the behaviour changes and the upgrade steps before you update.
Behaviour changes
The
system-upgrade-controlleraddon no longer binds the controller to cluster-admin. The Rancher chart binds the ServiceAccountcattle-system/system-upgrade-controllertocluster-admin. The addon now deletes that ClusterRoleBinding from its render and binds four roles instead, each named with the prefixlok8s-: a ClusterRole and a Role (cattle-system) with the calls the controller makes, a Role indefaultfor the events on nodes, and a ClusterRole for the cordon and drain steps of a Plan whose Job runs as the controller's ServiceAccount. The ServiceAccount can no longer change RBAC, impersonate, request tokens for other ServiceAccounts, read Secrets outsidecattle-system, or write outside Nodes, pods (delete and evict), Jobs, Leases, events, the Plan status and CRDs. It can still read every Secret incattle-system, start pods there through Jobs (privileged ones too, unless an admission policy stops them), change every Node, and evict or delete any pod. Root on a node stays in reach through such a Job. A Plan's upgrade container that runs assystem-upgrade-controllerand calls the Kubernetes API for more than cordon and drain now getsforbidden. Give that Plan a ServiceAccount of its own with the grants it needs, or bootstrap the new opt-in addon (below). The rules, what the ServiceAccount can still do, and which Plan feature needs which drain rule: system-upgrade-controller.The addon has no
namespace:transformer any more. Its objects name their namespace themselves (the chart hardcodescattle-system); the Role for the node events lives indefault. A modified copy of the addon that relied on the transformer must setmetadata.namespaceon its own objects.The
system-upgrade-controlleraddon pins its images by digest. The controller israncher/system-upgrade-controller:v0.20.1@sha256:aaf4dbf6…, the kubectl image of the cordon and drain containers israncher/kuberlr-kubectl:v8.1.1@sha256:74ab36f8…. The tags are the ones the chart pinned before, so the Deployment rolls once and runs the same code. A private mirror set throughglobal.cattle.systemDefaultRegistrymust keep the digests.The
redis-operatoraddon no longer aggregates intoadmin, and the operator runs with narrowed RBAC. The chart's ClusterRoleredis-operatorgrants create and delete on namespaces, Secrets, ConfigMaps, pods andpods/execin every namespace, and its labelrbac.authorization.k8s.io/aggregate-to-admincopies those rules into the built-in ClusterRoleadmin: every subject with a RoleBinding toadmincould delete its own namespace. The addon now deletes the chart's ClusterRole and ClusterRoleBinding from its render and binds three roles, each named with the prefixlok8s-: a ClusterRole with the calls the operator makes for the Redis CRs (Secrets get only, no namespaces, no ConfigMaps), a ClusterRole withpods/execcreate that only a RedisCluster needs, and a Role inredis-operatorfor leader election. No object of the addon carries anaggregate-to-*label. The operator can still create StatefulSets, read Secrets by name, patch pods and delete PVCs in every namespace. The rules, their sources and what the token can still do: redis-operator.pods/execstays enabled by default in theredis-operatoraddon. The bindinglok8s-redis-operator-execkeeps a RedisCluster working after the update, so the exec grant is not reduced until you remove it: the token can still run any command in any pod. Only a RedisCluster needs exec (creating the cluster, scaling, rebalancing); Redis, RedisReplication and RedisSentinel do not. A cluster without a RedisCluster can remove it: delete the binding document from the ejectedrbac.yaml, apply, then runkubectl delete clusterrolebinding lok8s-redis-operator-exec(the bootstrap does not prune). The oldClusterRole/redis-operatorgrants exec too, until you delete it (see Upgrade). A RedisCluster added later needs the binding back first. The steps and the checks: pods/exec. A by-name switch is #316; alo doctorwarning is #315.A busy kubehz platform stops
lo provisioninstead of a silent skip (#312). When the kubehz api answers a registration with503or429andRetry-After(for exampleREGISTRATION_UNAVAILABLEwhile its rate limit store is down), lo waits and tries again: at most 3 tries, at most 60 seconds for each wait. Until now lo dropped the answer, tried the other register modes, and continued without the registration. Now, if the platform still refuses after the third try,lo provisionstops with status 1. What stops: the provision, after the infrastructure is up and before the bootstrap. What is left: a running cluster without itsspec.bootstrapaddons and without the platform registration. To recover, runlo provision --bootstrapwhen the platform is available again. It registers the cluster and then applies the bootstrap; it does not provision the infrastructure again.lo kubehz registerstops the same way and names itself as the command to run again. A refusal that lo does not try again (a503withoutRetry-After, for example) keeps the old path, and its warning now names the status and the code. Both implementations behave the same. Details: When the platform is busy.
Fixes
- kubehz heartbeat agent: the node list is one call (#317). The heartbeat CronJob made three
kubectl get nodecalls per node. At about 18 nodes a run passed the job's 120 s deadline, and the job ended before the heartbeat went out: the cluster sent no more heartbeats, and nothing reported an error. The agent now reads all nodes with onekubectl get --raw /api/v1/nodescall undertimeout 30, so the number of calls does not grow with the cluster. The node cap was 20 and is now 500, the limit of the platform's heartbeat API. A larger cluster sends its first 500 nodes, and the agent writes the total to its log. The CronJob's limits change: CPU from 200m to 1000m (at 200m eachkubectlstart was throttled) and memory from 128Mi to 256Mi (the pod parses the node list). The requests stay at 50m and 64Mi. Details: the heartbeat agent.
New
Opt-in addon
system-upgrade-controller-cluster-admin(not recommended). It applies the chart's ClusterRoleBinding to cluster-admin again, unchanged:yamlspec: bootstrap: - system-upgrade-controller - system-upgrade-controller-cluster-admin: dependsOn: [system-upgrade-controller]With it, the controller and every Plan Job that runs as its ServiceAccount can do anything in the cluster again. To opt out, remove the entry and also run
kubectl delete clusterrolebinding system-upgrade-controller: the bootstrap applies without prune, so the binding stays otherwise.Opt-in addon
redis-operator-chart-rbac(not recommended). It binds the rules of the chart's ClusterRole to the operator again, under the namelok8s-redis-operator-chart-rbacand without the labelaggregate-to-admin:yamlspec: bootstrap: - redis-operator - redis-operator-chart-rbac: dependsOn: [redis-operator]To opt out, remove the entry and also run
kubectl delete clusterrolebinding lok8s-redis-operator-chart-rbacandkubectl delete clusterrole lok8s-redis-operator-chart-rbac.
Upgrade
- First, update the pins. Pin
lo, thekustomize-secretplugin and the.lok8senv ref tov0.12.0, then runb update. Do this before the steps below:lo assets diffandlo assets updatecompare against the files inside theloyou run. From a release older than v0.11.1, follow the notes of each release in between first. - Check what the
.lok8ssync changed. With b's defaultreplacestrategy, a non-interactiveb updateof an env ref that syncs.lok8s/writes the new addon files over.lok8s/addons/<name>/, also over a copy you changed. Rungit diff .lok8s/addons/before the bootstrap and put your own changes back. To keep local changes during the sync, useb update --strategy=merge(a three-way merge) instead. - Both addons: update both copies, apply the bootstrap once, then do the deletes. The steps below are per addon, and each one says to apply the bootstrap. When you update both addons, one apply covers both.
system-upgrade-controller: update the ejected copy. Without that sync,loejects an addon into.lok8s/addons/<name>/on first use and never overwrites it, so a project that bootstrappedsystem-upgrade-controllerbefore keeps the old version.lo assets diff addons/system-upgrade-controllershows the changed files.lo assets update addons/system-upgrade-controllerapplies them when your copy is untouched. A modified copy takesrbac.yaml, thepatches:block, the kustomization without itsnamespace:line and the image pins by hand.- Then delete the old binding once. The bootstrap applies without prune, so a cluster that ran the addon before keeps
ClusterRoleBinding/system-upgrade-controllerand with it cluster-admin. Apply the bootstrap first (it adds the new bindings), then runkubectl delete clusterrolebinding system-upgrade-controller. A cluster that opts back in skips this step: the opt-in addon owns that binding. - Then prove it. Restart the controller (
kubectl -n cattle-system rollout restart deployment/system-upgrade-controller), run one Plan that does what your Plans do (cordon, drain, Secrets), and look forforbiddenin the controller log. Until now cluster-admin hid any call the new roles miss. Thekubectl auth can-ichecks are in the upgrade note. redis-operator: update the ejected copy.lo assets diff addons/redis-operatorshows the changed files;lo assets update addons/redis-operatorapplies them when your copy is untouched. A copy without a.lo-originmarker needs--forceonce the diff shows only this release's changes. A modified copy takesrbac.yamland thepatches:block by hand.redis-operator: then delete the old ClusterRole and binding once. The bootstrap applies without prune, so a cluster that ran the addon before keepsClusterRole/redis-operator(and with it the rules inadmin) andClusterRoleBinding/redis-operator. Apply the bootstrap first (it adds the new bindings), then runkubectl delete clusterrolebinding redis-operatorandkubectl delete clusterrole redis-operator.adminloses the copied rules asynchronously: the delete queues the clusterrole-aggregation controller. Polladminuntil no copied rule is left, and check the other ClusterRoles that aggregate into it (the guide has the commands). Restart the operator (kubectl -n redis-operator rollout restart deployment/redis-operator) and look forforbiddenin its log. The checks, and how to drop thepods/execbinding on a cluster without a RedisCluster, are in the upgrade note.- kubehz heartbeat: run
lo kubehz deployonce on each cluster wherelo kubehz deployinstalled the agent withagent: cronjob(the default) (lo use <domain>first). It applies the new CronJob: the one-call node list and the new limits. Until then the cluster runs the old heartbeat, which can pass its deadline at about 18 nodes. Withagent: operatorthe CronJob stops before the node list, so the old one cannot hit this deadline. A deploy there does no harm.