forked from runatlantis/atlantis
-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Rotate Github App Token outside of Atlantis commands (runatlant…
…is#3208) Co-authored-by: nitrocode <[email protected]> Co-authored-by: PePe Amengual <[email protected]>
- Loading branch information
Showing
18 changed files
with
376 additions
and
61 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,7 @@ atlantis | |
atlantis.env | ||
*.act | ||
package-lock.json | ||
Dockerfile.local | ||
|
||
# gitreleaser | ||
dist/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,6 +5,7 @@ import ( | |
"testing" | ||
|
||
. "github.com/petergtz/pegomock" | ||
pegomock "github.com/petergtz/pegomock" | ||
"github.com/runatlantis/atlantis/server/events" | ||
eventMocks "github.com/runatlantis/atlantis/server/events/mocks" | ||
"github.com/runatlantis/atlantis/server/events/models" | ||
|
@@ -57,6 +58,8 @@ func TestClone_GithubAppNoneExisting(t *testing.T) { | |
} | ||
|
||
func TestClone_GithubAppSetsCorrectUrl(t *testing.T) { | ||
pegomock.RegisterMockTestingT(t) | ||
|
||
workingDir := eventMocks.NewMockWorkingDir() | ||
|
||
credentials := vcsMocks.NewMockGithubCredentials() | ||
|
@@ -82,8 +85,9 @@ func TestClone_GithubAppSetsCorrectUrl(t *testing.T) { | |
headRepo := baseRepo | ||
|
||
modifiedBaseRepo := baseRepo | ||
modifiedBaseRepo.CloneURL = "https://x-access-token:[email protected]/runatlantis/atlantis.git" | ||
modifiedBaseRepo.SanitizedCloneURL = "https://x-access-token:<redacted>@github.com/runatlantis/atlantis.git" | ||
// remove credentials from both urls since we want to use the credential store | ||
modifiedBaseRepo.CloneURL = "https://github.com/runatlantis/atlantis.git" | ||
modifiedBaseRepo.SanitizedCloneURL = "https://github.com/runatlantis/atlantis.git" | ||
|
||
When(credentials.GetToken()).ThenReturn("token", nil) | ||
When(workingDir.Clone(logger, modifiedBaseRepo, models.PullRequest{BaseRepo: modifiedBaseRepo}, "default", []string{})).ThenReturn( | ||
|
@@ -92,5 +96,7 @@ func TestClone_GithubAppSetsCorrectUrl(t *testing.T) { | |
|
||
_, success, _ := ghAppWorkingDir.Clone(logger, headRepo, models.PullRequest{BaseRepo: baseRepo}, "default", []string{}) | ||
|
||
workingDir.VerifyWasCalledOnce().Clone(logger, modifiedBaseRepo, models.PullRequest{BaseRepo: modifiedBaseRepo}, "default") | ||
|
||
Assert(t, success == true, "clone url mutation error") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
package vcs | ||
|
||
import ( | ||
"time" | ||
|
||
"github.com/pkg/errors" | ||
"github.com/runatlantis/atlantis/server/logging" | ||
"github.com/runatlantis/atlantis/server/scheduled" | ||
) | ||
|
||
// GitCredsTokenRotator continuously tries to rotate the github app access token every 30 seconds and writes the ~/.git-credentials file | ||
type GitCredsTokenRotator interface { | ||
Run() | ||
GenerateJob() (scheduled.JobDefinition, error) | ||
} | ||
|
||
type githubAppTokenRotator struct { | ||
log logging.SimpleLogging | ||
githubCredentials GithubCredentials | ||
githubHostname string | ||
homeDirPath string | ||
} | ||
|
||
func NewGithubAppTokenRotator( | ||
log logging.SimpleLogging, | ||
githubCredentials GithubCredentials, | ||
githubHostname string, | ||
homeDirPath string) GitCredsTokenRotator { | ||
|
||
return &githubAppTokenRotator{ | ||
log: log, | ||
githubCredentials: githubCredentials, | ||
githubHostname: githubHostname, | ||
homeDirPath: homeDirPath, | ||
} | ||
} | ||
|
||
// make sure interface is implemented correctly | ||
var _ GitCredsTokenRotator = (*githubAppTokenRotator)(nil) | ||
|
||
func (r *githubAppTokenRotator) GenerateJob() (scheduled.JobDefinition, error) { | ||
|
||
return scheduled.JobDefinition{ | ||
Job: r, | ||
Period: 30 * time.Second, | ||
}, r.rotate() | ||
} | ||
|
||
func (r *githubAppTokenRotator) Run() { | ||
err := r.rotate() | ||
if err != nil { | ||
// at least log the error message here, as we want to notify the that user that the key rotation wasn't successful | ||
r.log.Err(err.Error()) | ||
} | ||
} | ||
|
||
func (r *githubAppTokenRotator) rotate() error { | ||
r.log.Debug("Refreshing git tokens for Github App") | ||
|
||
token, err := r.githubCredentials.GetToken() | ||
if err != nil { | ||
return errors.Wrap(err, "Getting github token") | ||
} | ||
r.log.Debug("token %s", token) | ||
|
||
// https://developer.github.com/apps/building-github-apps/authenticating-with-github-apps/#http-based-git-access-by-an-installation | ||
if err := WriteGitCreds("x-access-token", token, r.githubHostname, r.homeDirPath, r.log, true); err != nil { | ||
return errors.Wrap(err, "Writing ~/.git-credentials file") | ||
} | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
package vcs_test | ||
|
||
import ( | ||
"fmt" | ||
"os" | ||
"path/filepath" | ||
"testing" | ||
"time" | ||
|
||
"github.com/runatlantis/atlantis/server/events/vcs" | ||
"github.com/runatlantis/atlantis/server/events/vcs/testdata" | ||
"github.com/runatlantis/atlantis/server/logging" | ||
. "github.com/runatlantis/atlantis/testing" | ||
) | ||
|
||
func Test_githubAppTokenRotator_GenerateJob(t *testing.T) { | ||
defer disableSSLVerification()() | ||
testServer, err := testdata.GithubAppTestServer(t) | ||
Ok(t, err) | ||
|
||
anonCreds := &vcs.GithubAnonymousCredentials{} | ||
anonClient, err := vcs.NewGithubClient(testServer, anonCreds, vcs.GithubConfig{}, logging.NewNoopLogger(t)) | ||
Ok(t, err) | ||
tempSecrets, err := anonClient.ExchangeCode("good-code") | ||
Ok(t, err) | ||
type fields struct { | ||
githubCredentials vcs.GithubCredentials | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
credsFileWritten bool | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Should write .git-credentials file on start", | ||
fields: fields{&vcs.GithubAppCredentials{ | ||
AppID: tempSecrets.ID, | ||
Key: []byte(testdata.GithubPrivateKey), | ||
Hostname: testServer, | ||
}}, | ||
credsFileWritten: true, | ||
wantErr: false, | ||
}, | ||
{ | ||
name: "Should return an error if pem data is missing or wrong", | ||
fields: fields{&vcs.GithubAppCredentials{ | ||
AppID: tempSecrets.ID, | ||
Key: []byte("some bad formatted pem key"), | ||
Hostname: testServer, | ||
}}, | ||
credsFileWritten: false, | ||
wantErr: true, | ||
}, | ||
{ | ||
name: "Should return an error if app id is missing or wrong", | ||
fields: fields{&vcs.GithubAppCredentials{ | ||
AppID: 3819, | ||
Key: []byte(testdata.GithubPrivateKey), | ||
Hostname: testServer, | ||
}}, | ||
credsFileWritten: false, | ||
wantErr: true, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
tmpDir := t.TempDir() | ||
r := vcs.NewGithubAppTokenRotator(logging.NewNoopLogger(t), tt.fields.githubCredentials, testServer, tmpDir) | ||
got, err := r.GenerateJob() | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("githubAppTokenRotator.GenerateJob() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if tt.credsFileWritten { | ||
credsFileContent := fmt.Sprintf(`https://x-access-token:some-token@%s`, testServer) | ||
actContents, err := os.ReadFile(filepath.Join(tmpDir, ".git-credentials")) | ||
Ok(t, err) | ||
Equals(t, credsFileContent, string(actContents)) | ||
} | ||
Equals(t, 30*time.Second, got.Period) | ||
}) | ||
} | ||
} |
2 changes: 1 addition & 1 deletion
2
server/events/git_cred_writer.go → server/events/vcs/git_cred_writer.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
package events | ||
package vcs | ||
|
||
import ( | ||
"fmt" | ||
|
Oops, something went wrong.