134

Use-case:

  1. Command-line application (which is deployed to a 3rd party machine) needs to be able to download a tarball copy of a private repo that belongs to an organization via the GitHub API (v3)

  2. Application should only be able to access this one private repo and no other repos with read-only permission.

I have been able to accomplish (1) by creating an authorization for the application after registering a client_id/secret on my github account. However, it does not seem that the tokens returned by the authorization allow read-only access to the repo nor are they restricted to one repo (e.g. one could potentially use the token to modify this repo along with others belonging to the organization).

Is it possible to restrict access via the proper scope? I don't see anything relevant in the API docs (https://developer.github.com/v3/oauth/#scopes).

Thomas Shields
  • 8,874
  • 5
  • 42
  • 77
Bounce2thaOunce
  • 1,341
  • 2
  • 8
  • 3
  • 5
    I'd be interested in something like this as well, minus the read-only constraint. The only thing that seemed relevant was the deploy keys you can create for a repository, but I'm not sure if you can use those via the API. It seems the only workaround here would be to add some dummy user that you use to login to the API as a collaborator to that repository. – Thomas Shields Jan 29 '15 at 10:46

5 Answers5

69

I don't believe you can restrict github OAuth tokens in that way. The github docs for OAuth say that

While Git over HTTP with OAuth reduces friction for some types of applications, keep in mind that unlike deploy keys, OAuth tokens work for any repository for which the user has access.

So while you can limit the scope of the token in terms of the types of activities, you can't limit it to a subset of repos.

Deploy keys can be restricted to a single repo, but allow write access.

The obvious tactic (as mentioned by Thomas) is to create a dummy account that represents the application. Given the goals of OAuth, this might be a better workflow in any case -- it'll let you easily change the permissions the app has as if it were in fact a user.

Github even mentions/endorses this strategy explicitly, calling them machine users.

Achyut
  • 703
  • 9
  • 19
starwed
  • 2,536
  • 2
  • 25
  • 39
  • 1
    While that works it's not convenient for an organization as you can't revoke a token for a specific user. – Willem D'Haeseleer Jul 19 '17 at 16:54
  • 18
    Deploy Keys are read/pull-only by default, optionaly you can give them write/push-access too. – nooitaf Sep 17 '17 at 21:42
  • 3
    but you can't use deploy keys in github actions if I understand it correctly, because you have to store private key somewhere. Would be great to create access token for a single repo, or deploy key, but with ability to use it in actions. – Maksim Nesterenko Apr 20 '21 at 11:54
  • @maksim-nesterenko There's an [ssh-agent action](https://github.com/marketplace/actions/webfactory-ssh-agent) with which you can set the ssh private key provided through GitHub secrets. – Stevan Jan 14 '22 at 22:26
18

Deploy keys are the way to go.

By default they don't allow write access and they are scoped to the specific repository (unlike the GitHub personal access token). So you can now generate a private/public key pair, set one as read/pull only deploy key on a single repository in GitHub and use the private key in your CI.

For instance run a bash script:

eval "$(ssh-agent -s)";
ssh-add <your private deploy key>;

Now your CI has rights to access private repo's during the build.

You can add a Deploy key by going to your repository on Github and then clicking Settings > Deploy keys > Add deploy key

Sebastiaan
  • 980
  • 9
  • 10
  • 3
    But they can have write access too, which makes them a perfect substitute for passwords (in case of a leak, the damage is limited to a single repo!) – krassowski Oct 14 '21 at 11:43
  • This is the best answer for me !!! Using _actions/checkout@v3_ action with _ssh-key_ parameter – Vincent Guyard Aug 13 '22 at 20:13
8

Update

Github finally introduced fine-grained personal access token, this (or Machine user) should be now a preferred method for granting selective access.

Read more: https://github.blog/changelog/2022-10-18-introducing-fine-grained-personal-access-tokens

Original answer

I wanted to have better access control in my Github Actions, but also have access to multiple repositories at the same time. And sure enough Deploy Keys are way to go. You can choose read/write permissions, but unfortunately you need a new pair for every new repository. Bellow I'll show you how I made it to work.

Let's assume you need to access 2 repositories from the third running Github Actions

  • Generate your keys
    ssh-keygen -N '' -t ed25519 -C "First Key Name" -f ./first_key
    ssh-keygen -N '' -t ed25519 -C "Second Key Name" -f ./second_key
    
    You don't need a passphrase as long as you'll remove keys from the filesystem after adding them to the secrets in the Github repository.
  • Add contents of *.pub files as Deploy keys of respective repositories (select write permissions if needed)
  • Add contents of first_key and second_key files to third repository's secrets as DEPLOY_KEY_FIRST and DEPLOY_KEY_SECOND respectively
  • Now you can remove generated key files - you don't want to keep them around anymore. You can always generate a new ones.
  • Setup workflows file
    name: Some automatic action
    
    on:
      # on push event
      push:
      # allow manual run
      workflow_dispatch:
    
    env:
      # sockets for multiple ssh agents (1 per key)
      SSH_AUTH_SOCK_FIRST: /tmp/ssh_agent_first.sock
      SSH_AUTH_SOCK_SECOND: /tmp/ssh_agent_second.sock
    
    jobs:
      build:
        runs-on: ubuntu-latest
        steps:
          # first step is to setup ssh agents
          - name: Setup SSH Agents
            run: |
              # load deploy keys from the secrets
              echo "${{ secrets.DEPLOY_KEY_FIRST }}" > ./ssh_key_first
              echo "${{ secrets.DEPLOY_KEY_SECOND }}" > ./ssh_key_second
    
              # set chmods (required to use keys)
              chmod 0600 ./ssh_key_*
    
              # start agents
              ssh-agent -a $SSH_AUTH_SOCK_FIRST > /dev/null
              ssh-agent -a $SSH_AUTH_SOCK_SECOND > /dev/null
    
              # add each key to their own ssh agent
              SSH_AUTH_SOCK=$SSH_AUTH_SOCK_FIRST ssh-add ./ssh_key_first
              SSH_AUTH_SOCK=$SSH_AUTH_SOCK_SECOND ssh-add ./ssh_key_second
    
              # you can now remove keys from the filesystem
              rm -f ./ssh_key_*
    
          # now you can use these keys in normal git commands
          - name: Clone first
            env:
              # assign relevant agent for this step
              SSH_AUTH_SOCK: ${{ env.SSH_AUTH_SOCK_FIRST }}
            run: |
              git clone git@github.com:user/first.git ./first
    
          - name: Clone second
            env:
              SSH_AUTH_SOCK: ${{ env.SSH_AUTH_SOCK_SECOND }}
            run: |
              git clone git@github.com:user/second.git ./second
    
  • Profit

Disclaimer

As you may guess above solution is not scaling up, and at some point you may consider setting up a Machine user (suggested in starwed's answer), which works best with organization account (even free) and gives access to Personal Access Tokens and OAuth.

Krzysiek
  • 2,494
  • 16
  • 20
1

This should now (Oct. 2022) be possible with:

Introducing fine-grained personal access tokens (Oct. 2022)

Today we're enabling fine-grained personal access tokens (PATs) in Public Beta for all user accounts on GitHub.com.
This new type of token gives developers and resource owners more control and visibility around token access.
Learn more about this new token type in today's blog post.

These new tokens offer many more permissions to choose from, must be scoped to a specific organization or account, and must expire.
Organization owners will also find new tools to manage tokens that can access their organization, and can require approval of those tokens before they may be used.

https://i0.wp.com/user-images.githubusercontent.com/1666363/196268191-dd27554f-746e-45cf-9b92-a62f1846cd9e.gif?ssl=1 -- PATsv2-light2

You can try out the new token creation flow, and provide feedback in our community discussion.

For more information, see "Creating a fine-grained personal access token".

This allows to select a specific repository, if you want to.

VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
  • I created a fine-grained token with read only access to "content", but when I try to clone it using the token, I get "remote: Write access to repository not granted." :( – chrismarx Oct 21 '22 at 20:22
  • @chrismarx That might be a bug to report to GitHub support: read access should be enough for cloning a repository. – VonC Oct 21 '22 at 20:35
  • yeah, switching back to a classic token worked just fine ‍♂️ – chrismarx Oct 21 '22 at 20:37
0

As the top answer has suggested this is currently not possible with GitHub.

It is not a direct answer to your question, but the reason I was looking to do the same thing was to abide by policy of only allowing TCP 443 outbound from a restricted zone.

GitHub accepts SSH on TCP 443 as documented here.

TLDR: If you modify your ssh from ssh://git@github.com to ssh://git@ssh.github.com:443 it should be successful.

To set github to use TCP443 in your SSH configuration file, edit the file at ~/.ssh/config, and add this section:

Host github.com
Hostname ssh.github.com
Port 443
User git

You can test that this works with:

$ ssh -T git@github.com
> Hi username! You've successfully authenticated, but GitHub does not
> provide shell access.
alphabet5
  • 1,043
  • 1
  • 11
  • 15