Orchestrating Private GitHub Actions: Authentication Strategies and Workflow Integration

Implementing custom automation logic within a continuous integration and continuous deployment pipeline often requires tools that are not available in the public ecosystem. While GitHub Actions provides a robust marketplace for community-contributed workflows, enterprise environments and highly specific project requirements frequently necessitate the use of private repositories to house sensitive or proprietary action code. The ability to reference and execute these private actions is not automatic; it requires a deliberate configuration of authentication mechanisms and workspace management. Developers must navigate the constraints of repository visibility to ensure that custom JavaScript, Go, or Docker-based actions can be securely fetched, validated, and executed within the ephemeral environment of a GitHub runner.

The Architecture of Private Actions

The fundamental challenge in using private GitHub Actions is that the GitHub runner environment does not inherently possess the credentials required to access private repositories owned by the same user or organization, nor does it have access to private repositories owned by different entities. To bridge this gap, developers utilize specialized checkout actions that loop through a list of private repositories and clone them into the job's workspace. This process allows the workflow to treat the private code as a local action, referencing it via a relative path rather than a public marketplace identifier.

The daspn/private-actions-checkout action is a primary tool for this purpose. It is designed to simplify the usage of custom private actions and promote code reuse by iterating through a specified list of repositories. The action supports authentication via GitHub Apps and multiple SSH keys, making it suitable for complex enterprise environments. It is tested and confirmed to work on ubuntu-latest and macos-latest runners. As of the current implementation status, there is no support for Windows runners, which restricts its usage to Unix-based environments.

When creating a workflow file in the .github/workflows directory, the primary objective is to ensure that the custom action is available on the job's workspace before it is invoked. This is achieved by specifying a checkout_base_path, typically .github/actions, where the private repositories are cloned. Once cloned, the action can be referenced using the relative path ./.github/actions/<action-name>.

Authentication Mechanisms

Authentication is the critical component of retrieving private actions. The Git protocol requires valid credentials to access private repositories. The private-actions-checkout action supports two primary authentication methods: SSH keys and GitHub Apps. The choice between these methods often depends on the security policies of the organization and the specific use case.

SSH Key Authentication

SSH keys are a traditional and widely supported method for authenticating Git operations. The workflow requires the SSH private key to be stored securely in the repository's secrets. The action can accept a single SSH key or multiple keys if the workflow needs to pull actions from repositories owned by different users or organizations.

For a single SSH key scenario, the workflow defines the key in the ssh_private_key input. The action uses this key to authenticate with GitHub and clone the specified repositories.

yaml name: 'Single SSH key workflow' on: push jobs: example: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Private actions checkout uses: daspn/private-actions-checkout@v2 with: actions_list: '["githubuser/my-private-action-1@v1", "githubuser/my-private-action-2@v1"]' checkout_base_path: ./.github/actions ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY }}

In scenarios where actions are distributed across multiple user accounts or organizations, multiple SSH keys can be used. Each checkout step can specify a different SSH private key secret, allowing the runner to authenticate against different sources.

yaml name: 'Multiple SSH Keys workflow example' on: push jobs: example: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Checking out private actions from github_user uses: daspn/private-actions-checkout@v2 with: actions_list: '["github_user/my-private-action-1@v1", "github_user/my-private-action-2@v1"]' checkout_base_path: ./.github/actions ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY_1 }} - name: Checking out private actions from another_github_user uses: daspn/private-actions-checkout@v2 with: actions_list: '["another_github_user/my-private-action-3@v1", "another_github_user/my-private-action-4@v1"]' checkout_base_path: ./.github/actions ssh_private_key: ${{ secrets.SSH_PRIVATE_KEY_2 }}

It is also possible to configure the SSH agent using a third-party action, such as webfactory/[email protected], and then invoke the private actions checkout without explicitly passing the SSH key to the checkout action. In this case, the checkout action assumes that valid SSH credentials are already available in the runner's environment.

yaml name: 'No SSH Key example workflow' on: push jobs: example: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - uses: webfactory/[email protected] with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} - name: Private actions checkout uses: daspn/private-actions-checkout@v2 with: actions_list: '["githubuser/my-private-action-1@v1", "githubuser/my-private-action-2@v1"]' checkout_base_path: ./.github/actions

GitHub App Authentication

For enterprise environments, using a GitHub App is often recommended over SSH keys. This approach eliminates the need for a dedicated "machine user" account and allows for more granular permission management. A GitHub App can be configured with specific repository permissions, such as "Contents: read," and installed on the account or organization that owns the private actions.

When using a GitHub App, the workflow must provide the app_id and app_private_key as secrets. The private-actions-checkout action uses these credentials to authenticate and fetch the private repositories.

yaml name: 'Example workflow with GitHub App' on: push jobs: example: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Private actions checkout uses: daspn/private-actions-checkout@v2 with: actions_list: '["githubuser/my-private-action-1@v1", "githubuser/my-private-action-2@v1"]' checkout_base_path: ./.github/actions app_id: ${{ secrets.APP_ID }} app_private_key: ${{ secrets.APP_PRIVATE_KEY }}

The action also includes a configure_git option. When set to true, it configures Git to allow subsequent steps in the workflow to have authenticated access to other private repositories that are authorized to the GitHub App. This is particularly useful for workflows that need to fetch dependencies from other private sources after the initial action checkout.

Configuration Parameters

The private-actions-checkout action relies on several configuration parameters to function correctly. Understanding these parameters is essential for proper implementation.

  • actions_list: This is an optional input that specifies the list of private actions to checkout. It must be a JSON array, and each entry must match the format owner/repo@ref. For example, ["githubuser/my-private-action-1@v1"]. This parameter can be an empty array if no actions are required, although that would render the step unnecessary.
  • checkout_base_path: This optional parameter defines the directory where the custom actions will be checked out. The standard convention is to use .github/actions to keep the actions organized within the GitHub directory structure.
  • ssh_private_key: This input accepts the SSH private key secret, such as ${{ secrets.SSH_PRIVATE_KEY }}. It is used when authenticating via SSH.
  • app_id and app_private_key: These inputs are used for GitHub App authentication. The app_id is the ID of the GitHub App, and the app_private_key is the private key associated with the App.
  • configure_git: A boolean flag that, when set to true, configures Git to allow authenticated access to other private repositories in subsequent steps.

Building and Deploying Custom Actions

Creating a private action involves more than just writing the code; it requires building and packaging the action so that it can be executed by the GitHub runner. The process varies depending on the language and type of the action.

JavaScript Actions

For JavaScript actions, the official guidance recommends placing the action in a subfolder of .github/actions within the repository. This approach is beneficial for keeping the code in the same repository as the workflow, which is ideal for highly specific tasks that are not intended for reuse elsewhere. For instance, a developer might create a private JavaScript action to compose social media announcements from blog post data, a task that is too specific to warrant a public action.

To build a JavaScript action, Node.js 12.x is required. The build process involves installing the necessary dependencies and compiling the code using a bundler like ncc. The following commands illustrate the build process:

bash npm i -g @zeit/ncc npm i npm run build

This process updates the dist/index.js and dist/cleanup/index.js files, which are the entry points for the action. The action is then referenced in the workflow using the local path.

yaml - name: 'Using custom private action 1' uses: ./.github/actions/my-private-action-1 with: some_arg: test

Go Actions

For actions written in Go, the workflow must set up the Go environment before building and running the code. The private-actions-checkout action can be used with the configure_git option to ensure that the Go build process can access other private dependencies authorized to the GitHub App.

yaml name: 'Example workflow for Go action' on: push jobs: example: runs-on: ubuntu-18.04 steps: - uses: actions/checkout@v2 - name: Private actions checkout uses: daspn/private-actions-checkout@v2 with: app_id: ${{ secrets.APP_ID }} app_private_key: ${{ secrets.APP_PRIVATE_KEY }} configure_git: true - name: Set up Go uses: actions/setup-go@v2 with: go-version: 1.15 - name: Build run: go build -v .

Validation and Execution

After the private actions are checked out, it is prudent to validate that they are present in the workspace. This can be done using a simple shell command to list the contents of the checkout directory.

bash ls -lR ./.github/actions

Once validated, the actions can be invoked using the uses keyword with the relative path. The workflow can then pass inputs to the action using the with block.

yaml - name: 'Using custom private action 2' uses: ./.github/actions/my-private-action-2

Operational Considerations

Using private actions on GitHub imposes certain operational constraints. GitHub Actions on private repositories are subject to limits on free minutes per month and storage for artifacts. Developers must be mindful of these limits when designing their workflows. Additionally, the private-actions-checkout action is not certified by GitHub, which means it is maintained by the community and may not receive the same level of support or security auditing as official GitHub actions. However, its functionality is well-documented and widely used for its specific use case.

The decision to use a GitHub App versus SSH keys often comes down to security policy and scalability. GitHub Apps are generally preferred for enterprise environments because they do not require a machine user account and allow for more precise permission control. SSH keys are simpler to set up for individual developers or small teams but may become difficult to manage as the number of repositories and actions grows.

Conclusion

The ability to integrate private actions into GitHub workflows is a critical capability for developers who need to automate proprietary or sensitive processes. By leveraging tools like daspn/private-actions-checkout, developers can securely fetch and execute custom code from private repositories using SSH keys or GitHub Apps. This approach ensures that sensitive code remains private while still benefiting from the automation capabilities of GitHub Actions. The key to successful implementation lies in proper authentication configuration, careful management of secrets, and a clear understanding of the workflow structure. As the ecosystem of private actions continues to grow, these patterns will become increasingly important for maintaining robust and secure CI/CD pipelines.

Sources

  1. Private Actions Checkout
  2. Implementing Private JavaScript GitHub Action
  3. How to use Github Actions on Private Repository?

Related Posts