Least Privilege IAM Policy for JGit S3

Have you ever wondered how to get an encrypted private Git repository? Have you considered S3, but are worried about sharing credentials with collaborators? This article provides a concise, least privilege solution to both of these problems using IAM and JGit.

Some configurations are too sensitive to store in GitHub.  Some might argue that proprietary codebases are not well-served by storing them in a service with no audited controls of employee access or data protection.

There are many ways to slice this from running your own Gitolite server (as I once did) to running GitHub Enterprise or Gitlab.  It turns out there is one simpler low cost solution as well: JGit and S3.  However, there is a challenge in locking the access down appropriately to avoid risks to other resources in the same AWS Account.

There are a few good articles explaining how to use JGit with S3.  What I bring here is an explanation of the implications of using AWS Account level credentials or how to do least privilege.

Using AWS Account level credentials is a recipe for someone to misuse your account for their profit or hold your infrastructure at ransom.  In short, never ever use AWS Account level credentials for anything once you have gotten past initial account setup.  I will explain how to use AWS IAM to lock down access to you Git repositories in S3 so that the risks are mitigated.

What you will get in the end of this article is a set of privileges restricted to the minimum required for JGit and locked down to affecting specific repositories.

Locking Down Git S3 Access with AWS IAM

Create an IAM Policy called GitCommitters:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::AlainODea-projectname.git"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:GetObject",
                "s3:PutObject"
            ],
            "Resource": [
                "arn:aws:s3:::AlainODea-projectname.git/*"
            ],
            "Effect": "Allow"
        }
    ]
}

Create an IAM Policy called GitReaders:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::AlainODea-projectname.git"
            ],
            "Effect": "Allow"
        },
        {
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::AlainODea-projectname.git/*"
            ],
            "Effect": "Allow"
        }
    ]
}

Once you have this policy you can create an IAM Group called GitCommitters and attach the GitCommitters policy to it and another called GitReaders and attach the GitReaders policy to it.  Create IAM Users for each collaborator and assign each of them to the GitCommitters or GitReaders group.  Generate security keys for each collaborator and share them securely.

Rationale For Permissions Restrictions

I deliberately do NOT allow the ACLs to be set as this could inadvertently expose the content to public consumption if someone misconfigured their ~/.jgit_s3_private file.  If you want any collaborator to have the right to make a repo public at any time you need to add the s3:PutObjectAcl action to the list in the GitCommitters IAM policy.  I advise against that.

I also deliberately use an entire bucket as the ListBucket permission can't be scoped to subdirectories (S3 doesn't technically have directories at all).  The alternative is a general gitrepos bucket where all collaborators on all projects can see the other names of other projects.  This may or may not be a concern for you, but I wanted to make you aware of it so you can an informed decision.

Testing Your Setup

If you want to test this you'll need a Git repository to try.  In that Git repositories root run the following:

git remote add s3 amazon-s3://.jgit_s3_private@AlainODea-projectname.git/
random="$(dd if=/dev/urandom bs=64 count=1 | base64)"
cat > ~/.jgit_s3_private <<EOF
accesskey: $AWS_ACCESS_KEY
secretkey: $AWS_SECRET_KEY
password: $random
EOF
jgit push s3 refs/heads/master

That should result in output like the following if the permissions and keys are correct:

Counting objects:       1815
Finding sources:        100% (1815/1815)
Getting sizes:          100% (605/605)
Compressing objects:     99% (1717886/1718812)
Writing objects:        100% (1815/1815)
Put pack-97ad4a1..pack: 100% (65835/65835)
Put pack-97ad4a1..idx:  100% (50/50)
To amazon-s3://.jgit_s3_private@AlainODea-projectname.git/
 * [new branch]      master -> master

Caveats

There is still the risk of a collaborator uploading massive volumes of data to the S3 buckets for Git repos they can access.  Dueto limitations in the audit trails of S3 activity it would be difficult to determine who is doing this.

This is by no means a full solution for private Git hosting as it lacks the ability to reject history rewrites.  It will work for collaboration on small teams.  Code or configuration too sensitive to put in Github (like firewall configurations), but too small to justify a full private Git infrastructure deployment is a good fit for this approach.

See Also

For more details on how to use JGit with S3 see the following articles:

How to Use S3 as a Private Git Repository

Using jgit To Publish on Amazon S3