GitHub Actions Sudo Privileges and Security Orchestration

The concept of elevated privileges, specifically the use of sudo (superuser do), represents a critical intersection of operational utility and security vulnerability within the GitHub ecosystem. In the context of GitHub Actions, sudo functionality manifests in three distinct architectural layers: the administrative layer of the GitHub.com web interface, the execution layer of GitHub Actions runners interacting with remote targets via SSH, and the security hardening layer of the runner environment itself. Each of these layers manages the transition from a standard user context to a privileged context, balancing the need for system-level modifications against the risk of unauthorized account takeover or system compromise.

GitHub Web Interface Sudo Mode and Administrative Security

GitHub implements a specialized security state known as sudo mode to protect sensitive account operations. This mechanism is designed to ensure that an active session does not automatically grant the ability to perform high-impact changes, which prevents "session hijacking" or "shoulder surfing" from resulting in a total account compromise.

The technical implementation of sudo mode requires the user to re-authenticate even if they are already signed into their account. This creates a secondary layer of verification that triggers specifically when the system detects a request to perform a sensitive action. These sensitive actions are defined as those that could potentially allow a new person or an external system to gain unauthorized access to the account.

The following table delineates the specific actions that trigger the requirement for sudo mode authentication:

Sensitive Action Potential Risk Impact
Modification of associated email address Potential for account recovery hijacking
Authorization of third-party applications Granting external API access to private data
Addition of a new SSH key Establishing a permanent backdoor for Git access
Creation of a PAT (Personal Access Token) or application Creating programmatic credentials for account access

Once a user successfully authenticates, the session enters sudo mode, which allows for the performance of these sensitive actions without repeated authentication prompts for a limited window. This window is governed by a two-hour session timeout period. To prevent the session from expiring during an active administrative window, GitHub implements a reset mechanism where any sensitive action performed within that window restarts the two-hour timer.

The authentication methods available to confirm access for sudo mode are diverse, catering to different security postures. Users can utilize their standard password, or for enhanced security, they can employ:

  • Passkeys (requires a registered passkey on the account)
  • Security keys (U2F/FIDO2)
  • GitHub Mobile prompts
  • 2FA (Two-Factor Authentication) codes

SSH Sudo Execution within GitHub Actions Workflows

A common operational challenge for DevOps engineers using GitHub Actions is the execution of sudo commands on remote targets, such as Virtual Machines (VMs) or Virtual Desktop Infrastructure (VDI) instances. When a GitHub Actions runner attempts to execute a command requiring root privileges over an SSH connection, it often encounters a failure due to the lack of an interactive terminal.

In a standard terminal environment, the sudo command prompts the user for a password via a TTY (teletype). However, GitHub Actions runners execute commands in a non-interactive shell. This results in a specific failure message: sudo: a terminal is required to read the password; either use the -S option to read from standard input or configure an askpass helper.

The technical root of this issue is that the sudo utility expects a mechanism to securely collect the password from the user, but the pseudo-terminal used by the GitHub Runner does not provide the necessary interactive interface. Even if SSH configuration is set to use key-based authentication, which handles the initial connection to the server, the subsequent transition to root via sudo still requires a password unless the remote user is configured as NOPASSWD in the sudoers file.

For those attempting to automate root updates or system configurations on a guest OS (such as a .VDI running on a self-hosted runner server through a bridged network), the following technical constraints and solutions apply:

  • The use of sshpass is often employed to provide the password non-interactively, although this introduces security risks by exposing credentials in the script.
  • To resolve the "terminal required" error, the -S option must be used with sudo. This tells sudo to read the password from the standard input (stdin) rather than from the terminal device.
  • An alternative is the configuration of an askpass helper, which is a program that sudo can call to retrieve the password.
  • Network configurations such as NAT and port forwarding (e.g., mapping host port 2222 to guest port 22) allow the runner to reach the VM, but do not bypass the requirement for a password when executing sudo inside the guest OS.

Security Hardening and the Disable-Sudo Policy

To reduce the attack surface of the build environment, security-focused tools like Harden-Runner provide mechanisms to restrict the capabilities of the runner user. One such feature is the disable-sudo policy, which is designed to prevent GitHub Actions workflows from escalating privileges.

The technical implementation of the disable-sudo policy involves the removal of the runner user from the sudoers file. This ensures that even if a malicious actor successfully injects code into a workflow, they cannot use the sudo command to gain root access to the underlying runner host.

A typical implementation of this security control in a workflow file looks as follows:

yaml steps: - uses: step-security/harden-runner@v2 with: disable-sudo: true egress-policy: audit

Despite the effectiveness of removing the user from the sudoers file, security researchers identified a critical bypass mechanism designated as CVE-2025-32955. This vulnerability allows an attacker to regain root access if the runner user still has access to the Docker or containerd sockets.

The technical progression of the CVE-2025-32955 bypass is as follows:

  1. Initial Access: The attacker gains the ability to run malicious code on the runner via a supply chain attack (e.g., targeting tj-actions) or a Pwn Request vulnerability.
  2. Socket Exploitation: The attacker leverages existing access to the dockerd or containerd socket.
  3. Privileged Containerization: The attacker starts a privileged container.
  4. Host Mounting: The attacker mounts the host's root filesystem into the privileged container.
  5. Root Escalation: Through the mounted filesystem, the attacker can modify system files or restore sudo capabilities, effectively achieving root access and defeating the disable-sudo control.

The severity of this vulnerability is rated as 6.0 Medium. It specifically affects GitHub-hosted runners or ephemeral VM-based self-hosted runners where the runner user has Docker access. It is important to note that this issue does not affect Kubernetes-based Actions Runner Controller (ARC) implementations of Harden-Runner.

Advanced Mitigation: Transitioning to disable-sudo-and-containers

To address the bypass identified in CVE-2025-32955, a more comprehensive policy was introduced: disable-sudo-and-containers. This policy recognizes that as long as Docker is available, the disable-sudo restriction is insufficient for a defense-in-depth strategy.

The disable-sudo-and-containers policy implements three layers of restriction:

  • Sudo Restriction: It maintains the removal of the user from the sudoers file to block standard privilege escalation paths.
  • Socket Removal: It removes all access to the Docker and containerd sockets, preventing the creation of privileged containers.
  • Software Uninstallation: It completely uninstalls Docker from the runner environment to eliminate the tool entirely.

By removing the Docker runtime, the attack vector used to bypass the original security control is completely eliminated. Users are strongly advised to use this enhanced policy for any GitHub Actions job that does not explicitly require the use of containers.

Conclusion

The management of sudo privileges within GitHub Actions is a complex balance between operational necessity and security integrity. On the administrative side, GitHub utilizes a time-bound sudo mode to protect critical account settings from unauthorized modification. In the operational side of CI/CD, the challenge of non-interactive shells requires specific flags like sudo -S to allow for remote root execution via SSH. Finally, in the realm of infrastructure security, the evolution from disable-sudo to disable-sudo-and-containers demonstrates the necessity of closing all potential escalation paths, including those provided by container sockets. For organizations utilizing self-hosted or hardened runners, the removal of Docker access is the only definitive way to ensure that the restriction of sudo privileges cannot be bypassed by an attacker who has gained code execution capabilities on the runner.

Sources

  1. GitHub Docs - Sudo Mode
  2. Level1Techs Forum - Sudo commands via SSH to VM using GitHub Actions
  3. Step Security Blog - Evolving Harden-Runner: Disable Sudo Policy for Improved Runner Security

Related Posts