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

Add ghe config option #559

Merged
merged 5 commits into from
Oct 8, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ cmd/allstar/allstar
*.pem
.vscode
allstar.ref
.idea/
31 changes: 23 additions & 8 deletions operator.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,27 @@ conditions on enforcement actions, ex: pinging an issue twice at the same time.
## Configuration via Environment Variables

Allstar supports various operator configuration options which can be set via environment variables:
|Name|Description|Default|
|----|----|----|
|APP_ID|The application ID of the created GitHub App.||
|PRIVATE_KEY|The raw value of the private key for the GitHub App. KEY_SECRET must be set to "direct".||
|KEY_SECRET|The name of a secret containing a private key.||
|DO_NOTHING_ON_OPT_OUT|Boolean flag which defines if allstar should do nothing and skip the corresponding checks when a repository is opted out.|false|
|ALLSTAR_LOG_LEVEL|The minimum logging level that allstar should use when emitting logs. Acceptable values are: panic ; fatal ; error ; warn ; info ; debug ; trace|info|
|NOTICE_PING_DURATION_HOURS|The duration (in hours) to wait between pinging notice actions, such as updating a GitHub issue.|24|

| Name | Description | Default |
|----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------|---------|
| APP_ID | The application ID of the created GitHub App. ||
| PRIVATE_KEY | The raw value of the private key for the GitHub App. KEY_SECRET must be set to "direct". ||
| KEY_SECRET | The name of a secret containing a private key. ||
| ALLSTAR_GHE_URL | The URL of the GitHub Enterprise instance to use. Leave empty to use github.com ||
| DO_NOTHING_ON_OPT_OUT | Boolean flag which defines if allstar should do nothing and skip the corresponding checks when a repository is opted out. | false |
| ALLSTAR_LOG_LEVEL | The minimum logging level that allstar should use when emitting logs. Acceptable values are: panic ; fatal ; error ; warn ; info ; debug ; trace | info |
| NOTICE_PING_DURATION_HOURS | The duration (in hours) to wait between pinging notice actions, such as updating a GitHub issue. | 24 |

## Self-hosted GitHub Enterprise specifics

In case you want to operate Allstar with a self-hosted GitHub Enterprise instance, you need to set the `ALLSTAR_GHE_URL` environment variable to the URL of your GitHub Enterprise instance URL.
The different API endpoints for API and upload are appended automatically.

Example:

Given, your GHE instance URL is "https://my-ghe.example.com", you need to set the following environment variables:

```shell
export ALLSTAR_GHE_URL="https://my-ghe.example.com"
export GH_HOST="my-ghe.example.com" # This is used by the Scorecard dependency. Might result in errors if not set.
```
5 changes: 5 additions & 0 deletions pkg/config/operator/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ const setKeySecret = "gcpsecretmanager://projects/allstar-ossf/secrets/allstar-p

var KeySecret string

// GitHubEnterpriseUrl allows to configure the usage a GitHub enterprise instance
var GitHubEnterpriseUrl string

// OrgConfigRepo is the name of the expected org-level repo to contain config.
const OrgConfigRepo = ".allstar"

Expand Down Expand Up @@ -126,6 +129,8 @@ func setVars() {
KeySecret = setKeySecret
}

GitHubEnterpriseUrl = osGetenv("ALLSTAR_GHE_URL")

doNothingOnOptOutStr := osGetenv("DO_NOTHING_ON_OPT_OUT")
doNothingOnOptOut, err := strconv.ParseBool(doNothingOnOptOutStr)
if err == nil {
Expand Down
9 changes: 9 additions & 0 deletions pkg/config/operator/operator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func TestSetVars(t *testing.T) {
Name string
AppID string
KeySecret string
GitHubEnterpriseUrl string
NoticePingDurationHrs string
PrivateKey string
DoNothingOnOptOut string
Expand All @@ -42,6 +43,7 @@ func TestSetVars(t *testing.T) {
Name: "NoVars",
AppID: "",
KeySecret: "",
GitHubEnterpriseUrl: "",
DoNothingOnOptOut: "",
ExpAppID: setAppID,
ExpKeySecret: setKeySecret,
Expand All @@ -53,6 +55,7 @@ func TestSetVars(t *testing.T) {
Name: "SetVars",
AppID: "123",
KeySecret: "asdf",
GitHubEnterpriseUrl: "https://ghe.example.com",
DoNothingOnOptOut: "true",
ExpAppID: 123,
ExpKeySecret: "asdf",
Expand Down Expand Up @@ -169,6 +172,9 @@ func TestSetVars(t *testing.T) {
if in == "KEY_SECRET" {
return test.KeySecret
}
if in == "ALLSTAR_GHE_URL" {
return test.GitHubEnterpriseUrl
}
if in == "DO_NOTHING_ON_OPT_OUT" {
return test.DoNothingOnOptOut
}
Expand All @@ -190,6 +196,9 @@ func TestSetVars(t *testing.T) {
if diff := cmp.Diff(test.ExpKeySecret, KeySecret); diff != "" {
t.Errorf("Unexpected results. (-want +got):\n%s", diff)
}
if diff := cmp.Diff(test.GitHubEnterpriseUrl, GitHubEnterpriseUrl); diff != "" {
t.Errorf("Unexpected results. (-want +got):\n%s", diff)
}
if diff := cmp.Diff(test.ExpDoNothingOnOptOut, DoNothingOnOptOut); diff != "" {
t.Errorf("Unexpected results. (-want +got):\n%s", diff)
}
Expand Down
47 changes: 41 additions & 6 deletions pkg/ghclients/ghclients.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package ghclients
import (
"context"
"net/http"
"strings"

"github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-github/v59/github"
Expand Down Expand Up @@ -92,19 +93,53 @@ func (g *GHClients) Get(i int64) (*github.Client, error) {
}

var tr http.RoundTripper
var err error
if i == 0 {
tr, err = ghinstallationNewAppsTransport(ctr, operator.AppID, g.key)
appTransport, err := ghinstallationNewAppsTransport(ctr, operator.AppID, g.key)
if err != nil {
return nil, err
}
// other than clien.WithEnterpriseUrls, setting the BaseUrl plainly, we need to ensure the /api/v3 ending
if operator.GitHubEnterpriseUrl != "" {
appTransport.BaseURL = fullEnterpriseApiUrl(operator.GitHubEnterpriseUrl)
}
tr = appTransport
} else {
tr, err = ghinstallationNew(ctr, operator.AppID, i, g.key)
ghiTransport, err := ghinstallationNew(ctr, operator.AppID, i, g.key)
if err != nil {
return nil, err
}
if operator.GitHubEnterpriseUrl != "" {
ghiTransport.BaseURL = fullEnterpriseApiUrl(operator.GitHubEnterpriseUrl)
}
tr = ghiTransport

}
if err != nil {
return nil, err

c := github.NewClient(&http.Client{Transport: tr})
if operator.GitHubEnterpriseUrl != "" {
newC, err := c.WithEnterpriseURLs(operator.GitHubEnterpriseUrl, operator.GitHubEnterpriseUrl)
if err != nil {
return nil, err
}
c = newC
}
g.clients[i] = github.NewClient(&http.Client{Transport: tr})

g.clients[i] = c
return g.clients[i], nil
}

// fullEnterpriseApiUrl ensures the base url is in the correct format for GitHub Enterprise usage
func fullEnterpriseApiUrl(baseUrl string) string {
if !strings.HasSuffix(baseUrl, "/") {
baseUrl += "/"
}
if !strings.HasSuffix(baseUrl, "api/v3/") {
baseUrl += "api/v3/"
}

return baseUrl
}

func getKeyFromSecretReal(ctx context.Context, keySecretVal string) ([]byte, error) {
v, err := runtimevar.OpenVariable(ctx, keySecretVal)
if err != nil {
Expand Down
56 changes: 56 additions & 0 deletions pkg/ghclients/ghclients_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

"github.com/bradleyfalzon/ghinstallation/v2"
"github.com/google/go-cmp/cmp"
"github.com/ossf/allstar/pkg/config/operator"
)

func TestGet(t *testing.T) {
Expand Down Expand Up @@ -132,3 +133,58 @@ func TestGetKey(t *testing.T) {
})
}
}

func TestGitHubEnterpriseClient(t *testing.T) {
tests := []struct {
Name string
GitHubEnterpriseUrl string
installId int64
expectedBaseUrl string
expectedUploadUrl string
}{
{
Name: "ZeroAppId",
GitHubEnterpriseUrl: "https://ghezero.example.com",
installId: 0,
expectedBaseUrl: "https://ghezero.example.com/api/v3/",
expectedUploadUrl: "https://ghezero.example.com/api/uploads/",
},
{
Name: "NonZeroAppId",
GitHubEnterpriseUrl: "https://ghenonzero.example.com",
installId: 123,
expectedBaseUrl: "https://ghenonzero.example.com/api/v3/",
expectedUploadUrl: "https://ghenonzero.example.com/api/uploads/",
},
{
Name: "NoGitHubEnterpriseUrl",
GitHubEnterpriseUrl: "",
installId: 0,
expectedBaseUrl: "https://api.github.com/",
expectedUploadUrl: "https://uploads.github.com/",
},
}

for _, test := range tests {
t.Run(test.Name, func(t *testing.T) {
operator.GitHubEnterpriseUrl = test.GitHubEnterpriseUrl

ghc, err := NewGHClients(context.Background(), http.DefaultTransport)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}

c, err := ghc.Get(test.installId)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}

if c.BaseURL.String() != test.expectedBaseUrl {
t.Errorf("Did not read GitHub instance URL from operator config.\nExpected %s, got %s", test.expectedBaseUrl, c.BaseURL.String())
}
if c.UploadURL.String() != test.expectedUploadUrl {
t.Errorf("Did not read GitHub upload URL from operator config\nExpected %s, got %s", test.expectedUploadUrl, c.UploadURL.String())
}
})
}
}
9 changes: 8 additions & 1 deletion pkg/policies/security/security.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"fmt"

"github.com/ossf/allstar/pkg/config"
"github.com/ossf/allstar/pkg/config/operator"
"github.com/ossf/allstar/pkg/policydef"

"github.com/google/go-github/v59/github"
Expand Down Expand Up @@ -97,7 +98,13 @@ func (s Security) Name() string {
// configuration stored in the org/repo, implementing policydef.Policy.Check()
func (s Security) Check(ctx context.Context, c *github.Client, owner,
repo string) (*policydef.Result, error) {
v4c := githubv4.NewClient(c.Client())

var v4c v4client
if operator.GitHubEnterpriseUrl == "" {
v4c = githubv4.NewClient(c.Client())
} else {
v4c = githubv4.NewEnterpriseClient(operator.GitHubEnterpriseUrl+"/api/graphql", c.Client())
}
return check(ctx, c, v4c, owner, repo)
}

Expand Down
Loading