Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

allow user to pass gh-app-key directly instead of filename #1706

Merged
merged 5 commits into from
Aug 18, 2021
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 18 additions & 6 deletions cmd/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ const (
GHTokenFlag = "gh-token"
GHUserFlag = "gh-user"
GHAppIDFlag = "gh-app-id"
GHAppKeyFlag = "gh-app-key"
GHAppKeyFileFlag = "gh-app-key-file"
GHAppSlugFlag = "gh-app-slug"
GHOrganizationFlag = "gh-org"
Expand Down Expand Up @@ -192,6 +193,10 @@ var stringFlags = map[string]stringFlag{
GHTokenFlag: {
description: "GitHub token of API user. Can also be specified via the ATLANTIS_GH_TOKEN environment variable.",
},
GHAppKeyFlag: {
description: "The GitHub App's private key",
defaultValue: "",
},
GHAppKeyFileFlag: {
description: "A path to a file containing the GitHub App's private key",
defaultValue: "",
Expand Down Expand Up @@ -634,12 +639,19 @@ func (s *ServerCmd) validate(userConfig server.UserConfig) error {

// The following combinations are valid.
// 1. github user and token set
// 2. gitlab user and token set
// 3. bitbucket user and token set
// 4. azuredevops user and token set
// 5. any combination of the above
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GHAppIDFlag, GHAppKeyFileFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag)
if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) || ((userConfig.GithubAppID == 0) != (userConfig.GithubAppKey == "")) || ((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) || ((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) || ((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "")) {
// 2. github app ID and (key file set or key set)
// 3. gitlab user and token set
// 4. bitbucket user and token set
// 5. azuredevops user and token set
// 6. any combination of the above
vcsErr := fmt.Errorf("--%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s or --%s/--%s must be set", GHUserFlag, GHTokenFlag, GHAppIDFlag, GHAppKeyFileFlag, GHAppIDFlag, GHAppKeyFlag, GitlabUserFlag, GitlabTokenFlag, BitbucketUserFlag, BitbucketTokenFlag, ADUserFlag, ADTokenFlag)
if ((userConfig.GithubUser == "") != (userConfig.GithubToken == "")) || ((userConfig.GitlabUser == "") != (userConfig.GitlabToken == "")) || ((userConfig.BitbucketUser == "") != (userConfig.BitbucketToken == "")) || ((userConfig.AzureDevopsUser == "") != (userConfig.AzureDevopsToken == "")) {
return vcsErr
}
if (userConfig.GithubAppID != 0) && ((userConfig.GithubAppKey == "") && (userConfig.GithubAppKeyFile == "")) {
return vcsErr
}
if (userConfig.GithubAppID == 0) && ((userConfig.GithubAppKey != "") || (userConfig.GithubAppKeyFile != "")) {
return vcsErr
}
// At this point, we know that there can't be a single user/token without
Expand Down
25 changes: 21 additions & 4 deletions cmd/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (

homedir "github.com/mitchellh/go-homedir"
"github.com/runatlantis/atlantis/server"
"github.com/runatlantis/atlantis/server/events/vcs/fixtures"
"github.com/runatlantis/atlantis/server/logging"
. "github.com/runatlantis/atlantis/testing"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -75,6 +76,7 @@ var testFlags = map[string]interface{}{
GHTokenFlag: "token",
GHUserFlag: "user",
GHAppIDFlag: int64(0),
GHAppKeyFlag: "",
GHAppKeyFileFlag: "",
GHAppSlugFlag: "atlantis",
GHOrganizationFlag: "",
Expand Down Expand Up @@ -350,7 +352,7 @@ func TestExecute_ValidateSSLConfig(t *testing.T) {
}

func TestExecute_ValidateVCSConfig(t *testing.T) {
expErr := "--gh-user/--gh-token or --gh-app-id/--gh-app-key-file or --gitlab-user/--gitlab-token or --bitbucket-user/--bitbucket-token or --azuredevops-user/--azuredevops-token must be set"
expErr := "--gh-user/--gh-token or --gh-app-id/--gh-app-key-file or --gh-app-id/--gh-app-key or --gitlab-user/--gitlab-token or --bitbucket-user/--bitbucket-token or --azuredevops-user/--azuredevops-token must be set"
cases := []struct {
description string
flags map[string]interface{}
Expand Down Expand Up @@ -404,12 +406,19 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
true,
},
{
"just github app key set",
"just github app key file set",
map[string]interface{}{
GHAppKeyFileFlag: "key.pem",
},
true,
},
{
"just github app key set",
map[string]interface{}{
GHAppKeyFlag: fixtures.GithubPrivateKey,
},
true,
},
{
"just gitlab user set",
map[string]interface{}{
Expand Down Expand Up @@ -464,13 +473,21 @@ func TestExecute_ValidateVCSConfig(t *testing.T) {
false,
},
{
"github app and key set and should be successful",
"github app and key file set and should be successful",
map[string]interface{}{
GHAppIDFlag: "1",
GHAppKeyFileFlag: "key.pem",
},
false,
},
{
"github app and key set and should be successful",
map[string]interface{}{
GHAppIDFlag: "1",
GHAppKeyFlag: fixtures.GithubPrivateKey,
},
false,
},
{
"gitlab user and gitlab token set and should be successful",
map[string]interface{}{
Expand Down Expand Up @@ -572,7 +589,7 @@ func TestExecute_GithubUser(t *testing.T) {
func TestExecute_GithubApp(t *testing.T) {
t.Log("Should remove the @ from the github username if it's passed.")
c := setup(map[string]interface{}{
GHAppKeyFileFlag: "key.pem",
GHAppKeyFlag: fixtures.GithubPrivateKey,
GHAppIDFlag: "1",
RepoAllowlistFlag: "*",
}, t)
Expand Down
2 changes: 1 addition & 1 deletion runatlantis.io/docs/access-credentials.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ Available in Atlantis versions **newer** than 0.13.0.
- Create a file with the contents of the GitHub App Key, e.g. `atlantis-app-key.pem`
- Restart Atlantis with new flags: `atlantis server --gh-app-id <your id> --gh-app-key-file atlantis-app-key.pem --gh-webhook-secret <your secret> --write-git-creds --repo-allowlist 'github.com/your-org/*' --atlantis-url https://$ATLANTIS_HOST`.

NOTE: You can also create a config file instead of using flags. See [Server Configuration](/docs/server-configuration.html#config-file).
NOTE: Instead of using a file for the GitHub App Key you can also pass the key value directly using `--gh-app-key`. You can also create a config file instead of using flags. See [Server Configuration](/docs/server-configuration.html#config-file).

::: warning
Only a single installation per GitHub App is supported at the moment.
Expand Down
7 changes: 7 additions & 0 deletions runatlantis.io/docs/server-configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,13 @@ Values are chosen in this order:
```
Path to a GitHub App PEM encoded private key file. If set, GitHub authentication will be performed as [an installation](https://developer.github.com/v3/apps/installations/).

- ### `--gh-app-key`
```bash
export PRIVATE_KEY=$(cat path/to/app-key.pem)
atlantis server --gh-app-key=$PRIVATE_KEY
dhaven marked this conversation as resolved.
Show resolved Hide resolved
```
The PEM encoded private key for the GitHub App.
dhaven marked this conversation as resolved.
Show resolved Hide resolved

* ### `--gitlab-hostname`
```bash
atlantis server --gitlab-hostname="my.gitlab.enterprise.com"
Expand Down
7 changes: 1 addition & 6 deletions server/events/github_app_working_dir_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,19 +31,14 @@ func TestClone_GithubAppNoneExisting(t *testing.T) {
TestingOverrideHeadCloneURL: fmt.Sprintf("file://%s", repoDir),
}

tmpDir, cleanup3 := DirStructure(t, map[string]interface{}{
"key.pem": fixtures.GithubPrivateKey,
})
defer cleanup3()

defer disableSSLVerification()()
testServer, err := fixtures.GithubAppTestServer(t)
Ok(t, err)

gwd := &events.GithubAppWorkingDir{
WorkingDir: wd,
Credentials: &vcs.GithubAppCredentials{
KeyPath: fmt.Sprintf("%v/key.pem", tmpDir),
Key: []byte(fixtures.GithubPrivateKey),
AppID: 1,
Hostname: testServer,
},
Expand Down
6 changes: 3 additions & 3 deletions server/events/vcs/github_credentials.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func (c *GithubUserCredentials) GetToken() (string, error) {
// GithubAppCredentials implements GithubCredentials for github app installation token flow.
type GithubAppCredentials struct {
AppID int64
KeyPath string
Key []byte
Hostname string
apiURL *url.URL
installationID int64
Expand Down Expand Up @@ -128,7 +128,7 @@ func (c *GithubAppCredentials) getInstallationID() (int64, error) {

tr := http.DefaultTransport
// A non-installation transport
t, err := ghinstallation.NewAppsTransportKeyFromFile(tr, c.AppID, c.KeyPath)
t, err := ghinstallation.NewAppsTransport(tr, c.AppID, c.Key)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -163,7 +163,7 @@ func (c *GithubAppCredentials) transport() (*ghinstallation.Transport, error) {
}

tr := http.DefaultTransport
itr, err := ghinstallation.NewKeyFromFile(tr, c.AppID, installationID, c.KeyPath)
itr, err := ghinstallation.New(tr, c.AppID, installationID, c.Key)
if err == nil {
apiURL := c.getAPIURL()
itr.BaseURL = strings.TrimSuffix(apiURL.String(), "/")
Expand Down
17 changes: 2 additions & 15 deletions server/events/vcs/github_credentials_test.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package vcs_test

import (
"fmt"
"testing"

"github.com/runatlantis/atlantis/server/events/vcs"
Expand All @@ -21,15 +20,9 @@ func TestGithubClient_GetUser_AppSlug(t *testing.T) {
tempSecrets, err := anonClient.ExchangeCode("good-code")
Ok(t, err)

tmpDir, cleanup := DirStructure(t, map[string]interface{}{
"key.pem": tempSecrets.Key,
})
defer cleanup()
keyPath := fmt.Sprintf("%v/key.pem", tmpDir)

appCreds := &vcs.GithubAppCredentials{
AppID: tempSecrets.ID,
KeyPath: keyPath,
Key: []byte(fixtures.GithubPrivateKey),
Hostname: testServer,
AppSlug: "some-app",
}
Expand All @@ -51,15 +44,9 @@ func TestGithubClient_AppAuthentication(t *testing.T) {
tempSecrets, err := anonClient.ExchangeCode("good-code")
Ok(t, err)

tmpDir, cleanup := DirStructure(t, map[string]interface{}{
"key.pem": tempSecrets.Key,
})
defer cleanup()
keyPath := fmt.Sprintf("%v/key.pem", tmpDir)

appCreds := &vcs.GithubAppCredentials{
AppID: tempSecrets.ID,
KeyPath: keyPath,
Key: []byte(fixtures.GithubPrivateKey),
Hostname: testServer,
}
_, err = vcs.NewGithubClient(testServer, appCreds, logging.NewNoopLogger(t))
Expand Down
17 changes: 15 additions & 2 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"encoding/json"
"flag"
"fmt"
"io/ioutil"
"log"
"net/http"
"net/url"
Expand Down Expand Up @@ -156,10 +157,22 @@ func NewServer(userConfig UserConfig, config Config) (*Server, error) {
User: userConfig.GithubUser,
Token: userConfig.GithubToken,
}
} else if userConfig.GithubAppID != 0 {
} else if userConfig.GithubAppID != 0 && userConfig.GithubAppKeyFile != "" {
privateKey, err := ioutil.ReadFile(userConfig.GithubAppKeyFile)
if err != nil {
return nil, err
}
githubCredentials = &vcs.GithubAppCredentials{
AppID: userConfig.GithubAppID,
Key: privateKey,
Hostname: userConfig.GithubHostname,
AppSlug: userConfig.GithubAppSlug,
}
githubAppEnabled = true
} else if userConfig.GithubAppID != 0 && userConfig.GithubAppKey != "" {
githubCredentials = &vcs.GithubAppCredentials{
AppID: userConfig.GithubAppID,
KeyPath: userConfig.GithubAppKey,
Key: []byte(userConfig.GithubAppKey),
Hostname: userConfig.GithubHostname,
AppSlug: userConfig.GithubAppSlug,
}
Expand Down
3 changes: 2 additions & 1 deletion server/user_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ type UserConfig struct {
GithubWebhookSecret string `mapstructure:"gh-webhook-secret"`
GithubOrg string `mapstructure:"gh-org"`
GithubAppID int64 `mapstructure:"gh-app-id"`
GithubAppKey string `mapstructure:"gh-app-key-file"`
GithubAppKey string `mapstructure:"gh-app-key"`
GithubAppKeyFile string `mapstructure:"gh-app-key-file"`
GithubAppSlug string `mapstructure:"gh-app-slug"`
GitlabHostname string `mapstructure:"gitlab-hostname"`
GitlabToken string `mapstructure:"gitlab-token"`
Expand Down