Repository navigation
Conversation
|
@codex security review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
|
@codex security review |
🛡️ Codex Security ReviewSecurity review completed. No security issues were found in this pull request. Reviewed commit: Only the user who started this review can view the report in Codex. ℹ️ About Codex security reviews in GitHubThis is an experimental Codex feature. Security reviews are triggered when:
Once complete, Codex will leave suggestions, or a comment if no findings are found. |
a6266fa to
a2bf347
Compare
|
@codex security review |
|
This security review couldn’t start because the paying account has exhausted its Codex allowance and has insufficient available credits. The payer can check their balance and add credits in the Codex usage dashboard, then try again. If you do not manage the paying account, contact this repository's admins. |
a2bf347 to
302048d
Compare
ca5df0f to
b728aec
Compare
302048d to
445a987
Compare
|
@codex security review |
|
This security review couldn’t start because the paying account has exhausted its Codex allowance and has insufficient available credits. The payer can check their balance and add credits in the Codex usage dashboard, then try again. If you do not manage the paying account, contact this repository's admins. |
b728aec to
c2b9c25
Compare
445a987 to
dfdfe6d
Compare
c2b9c25 to
3c9fe72
Compare
…e up The entrypoint said k3s's StartVPN checks the backend state and skips its own tailscale up once the daemon is running. It does not: on every start it runs tailscale up with --reset, adding only --login-server and the --vpn-auth extraArgs. The identity survives, but every other preference is reset, so a Tailscale setting a node needs has to be a tailscaled flag or an extraArgs entry.
A node container on Compose's bridge network sits behind the Docker host's NAT, so its Tailscale client can offer its peers no address they can reach and every pair of nodes is relayed, even on one LAN. github_runner_cluster_network_mode host merges docker-compose.host-network.yml last, which puts the server and agent services on the host's network and empties the port list; the client then offers the host's own addresses and nodes that share a network connect directly, falling back to a relay otherwise. bridge stays the default. Nothing k3s uses changes. In host mode the role checks that the API server answers on the host's loopback before it uses the kubeconfig there, since no port is published.
…ir own A host_network scenario in the mesh integration test gives each node a Docker daemon of its own, a docker:dind container at a fixed address on the shared test network standing in for a VM with an address on the LAN, and runs the role with github_runner_cluster_network_mode host against a Headscale server the test starts. Besides the usual cluster checks, every node's Tailscale client must reach every other directly at that daemon's address, the node containers must publish no port, and a second run must restart nothing. The workflow runs it on pull requests.
… node Before the role changes anything on a host it checks the node's network mode, and with github_runner_cluster_network_mode host that the Docker host has an address on github_runner_cluster_lan_cidr, read from a throwaway container on the host network, and no Tailscale device of another client there: the host's own on a Linux machine with no VM, or a second node on the same Docker host. Moving a running node between the bridge and the host network recreates its container, so the role first reads the cluster through each server of the inventory in turn, this one first, and refuses unless the other servers whose node is Ready and whose API server reports etcd healthy still form a majority, and no ARC runner pod is on the node. Whatever cannot be told refuses the move, saying why: no server that answers, servers without their role labels, a missing or unreachable server, one the inventory does not hold, or no server in the inventory at all. Only one node may move per run. Every check only reads.
… bridged Colima VM
dfdfe6d to
f549691
Compare
Stacked on #80 (version pins), which it needs: the Compose renders here set both pinned versions, and the role's checks run after the version checks.
Lets cluster nodes that share a LAN reach each other over it directly, with the mesh's relays as the fallback, without changing anything k3s uses. The role adds a network mode and the checks around it; moving a node onto a Docker VM with its own LAN address is a documented runbook, not automation.
Today every node's k3s container sits on Compose's bridge network behind the Docker host's NAT, and on macOS behind the Docker VM's too. So the Tailscale client inside it (k3s
--vpn-auth) has no address to offer its peers, and every pair of nodes is relayed, even on one LAN.Changes
github_runner_cluster_network_mode(bridge, the default, orhost).hostmergesdocker-compose.host-network.ymllast, putting thek3sandk3s-agentservices on the Docker host's network with no published ports. The node's Tailscale client then offers the host's own addresses. Node IP, etcd peer URLs, TLS SANs and the mesh identity are unchanged. In host mode the role checks that the API server answers on the host's loopback before using the kubeconfig there.--checktoo:bridgeorhost;github_runner_cluster_lan_cidr, and the Docker host must have an address in it, read from a throwaway container on the host network;github_runner_cluster_cluster_read_timeoutas a whole and as kubectl's--request-timeout, etcd's included, using the first that answers, and refuses unless the other servers whose node is Ready and whose API server reports/readyz/etcdok (read through each server's container on its host) still form a majority, and no unfinished ARC runner pod is on the node. Whatever cannot be told refuses, saying why: no server that answers (each named with its reason), no server in the inventory at all (an agentbootstrap.shjoins to an outside cluster), an unreachable Docker daemon, servers without their role labels, a missing or unreachable server, a server the inventory does not hold. Only one node may move per run, since each host's checks see the others as healthy before any is recreated.etcdctl, moving leadership off the node, an etcd snapshot, cordon and drain, carrying the node password, the bridged interface (and that it can change on a later restart, seen with a test profile only),COLIMA_HOME,--activate=falseand sizing, volumes created with Compose's labels, moving the engine first on the bridge network and then the network through the role, so the guard runs on the real move, identity checks after each (InternalIP, etcd member IDs and peer URLs, mesh device, no node-password event, the kubeconfig rewritten through the new VM's mount), starting the profile at login (whatbrew servicescovers and what it does not), and the rollback limits.StartVPNdoes:tailscale up ... --reseton every start.tailscaledUDP port: the client offers its real local endpoint whatever port it chose, and the mesh scenario reaches every peer directly on random ports.Tests
tests/unit/test_cluster_network.pyrenders the Compose project in both modes.tests/unit/test_cluster_guard.pycovers the guard: quorum for three and five servers, etcd unhealthy or unreachable, servers without labels, servers missing from the node list or from the inventory, runner pods, and LAN address matching.tests/unit/test_host_network_role.pyrunstasks/host_network.ymlagainst a stand-indocker: the mode check, the LAN and Tailscale device checks, a healthy move (including under--check), refusals for a not-Ready server, an unhealthy etcd, unlabelled servers, a runner pod and two nodes moving at once, a read through another server when this one cannot list the cluster, a refusal naming each server when none can, an agent reading through the servers of a group with no bootstrap host, an agent whose servers are outside the inventory, an unreachable daemon, and the skips for a node already on its network, stopped or missing. It also checks fromtasks/main.ymlthat the loopback API wait runs only on the host network and that the checks come before any change.tests/mesh/run.sh host_network(Linux only) runs each node in adocker:dinddaemon of its own, standing in for a VM with a LAN address: the usual cluster checks, every node reaching every other directly at its daemon's address, no published port, and a second run restarting nothing. Then the role must refuse to move two running nodes to the bridge network in one run without restarting anything, and move one node to the bridge network and back through its guard against the real cluster, the node keeping its mesh address and the cluster its three etcd members.--check,set -ein it, the loopback API wait's condition and the mode check.Not tested here
These need a bridged Colima VM on a real host: the runbook end to end, Lima forwarding the API server's port to the host's loopback, and the address probe on a real bridged VM. The role does not read which server leads etcd, so leadership is moved by hand in the runbook.