-
Notifications
You must be signed in to change notification settings - Fork 0
feat: implement the resolve endpoint #18
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
Changes from all commits
a07e2af
c38070b
897cb7e
79798e2
519ebb2
1f66449
aa14b26
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,7 @@ SERVER_READ_TIMEOUT=5s | |
| SERVER_WRITE_TIMEOUT=30s | ||
| SERVER_IDLE_TIMEOUT=120s | ||
| SERVER_SHUTDOWN_TIMEOUT=10s | ||
| SERVER_PUBLIC_URL=http://localhost:8080 | ||
|
|
||
| # Postgres | ||
| POSTGRES_USER=postgres | ||
|
|
@@ -24,6 +25,12 @@ DISCORD_CLIENT_ID= | |
| DISCORD_CLIENT_SECRET= | ||
| DISCORD_REDIRECT_URI=http://localhost:8080/callback | ||
|
|
||
| # Crypto | ||
| TOKEN_ENCRYPTION_KEY= # 32 bytes, base64: openssl rand -base64 32 | ||
|
|
||
| # GitHub | ||
| GITHUB_OIDC_AUDIENCE= | ||
|
Comment on lines
+31
to
+32
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should be removed. If ever being planned to be used on our Go-backend, it should be dynamically loaded from GitHub environmental variables (secrets), as we can't load any variables from
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. yeah i mean technically a secrets manager would be used to load secrets.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Replace it with dynamic loading and mark this solved.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Uhh actually the aud is anyway present in the jwt token under the aud field i believe so this aint really a secret, although we do have to do this secrets manager for stuff like postgres password and discord client secret etc. |
||
|
|
||
| # Goose | ||
| GOOSE_DRIVER=postgres | ||
| GOOSE_MIGRATION_DIR=internal/storage/migrations | ||
|
|
||
Large diffs are not rendered by default.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| // Package github verifies the OIDC tokens GitHub Actions issues to workflow runs. | ||
| package github |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,70 @@ | ||
| package github | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "strconv" | ||
|
|
||
| "github.com/coreos/go-oidc/v3/oidc" | ||
| "github.com/sonolink/arbiterer/internal/config" | ||
| ) | ||
|
|
||
| const ( | ||
| issuerURL = "https://token.actions.githubusercontent.com" | ||
| jwksURL = issuerURL + "/.well-known/jwks" | ||
| ) | ||
|
|
||
| // Verifier checks OIDC tokens GitHub Actions issues to workflow runs. | ||
| type Verifier struct { | ||
| verifier *oidc.IDTokenVerifier | ||
| } | ||
|
|
||
| // NewVerifier builds a Verifier that trusts tokens issued by GitHub Actions for the | ||
| // configured audience. | ||
| func NewVerifier(ctx context.Context, cfg config.GitHub) *Verifier { | ||
| keySet := oidc.NewRemoteKeySet(ctx, jwksURL) | ||
| verifier := oidc.NewVerifier( | ||
| issuerURL, | ||
| keySet, | ||
| &oidc.Config{ | ||
| ClientID: cfg.OIDCAudience, | ||
| SupportedSigningAlgs: []string{oidc.RS256}, | ||
| }, | ||
| ) | ||
| return &Verifier{verifier: verifier} | ||
| } | ||
|
|
||
| // Claims holds the parts of a verified token the application acts on. | ||
| type Claims struct { | ||
| RepositoryID int64 | ||
| } | ||
|
|
||
| // tokenClaims mirrors the claims GitHub Actions puts in an OIDC token. Numeric ids | ||
| // arrive as strings. | ||
| type tokenClaims struct { | ||
| RepositoryID string `json:"repository_id"` | ||
| } | ||
|
|
||
| func (tc tokenClaims) claims() (*Claims, error) { | ||
| repositoryID, err := strconv.ParseInt(tc.RepositoryID, 10, 64) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("github: parsing repository id %q: %w", tc.RepositoryID, err) | ||
| } | ||
|
|
||
| return &Claims{RepositoryID: repositoryID}, nil | ||
| } | ||
|
|
||
| // Verify checks a GitHub Actions OIDC token and returns the claims it carries. | ||
| func (v *Verifier) Verify(ctx context.Context, rawIDToken string) (*Claims, error) { | ||
| idToken, err := v.verifier.Verify(ctx, rawIDToken) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("github: verifying token: %w", err) | ||
| } | ||
|
|
||
| var tc tokenClaims | ||
| if err := idToken.Claims(&tc); err != nil { | ||
| return nil, fmt.Errorf("github: decoding claims: %w", err) | ||
| } | ||
|
|
||
| return tc.claims() | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,2 @@ | ||
| // Package secrets encrypts values at rest using AES-GCM. | ||
| package secrets |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| package secrets | ||
|
|
||
| import ( | ||
| "crypto/aes" | ||
| "crypto/cipher" | ||
| "crypto/rand" | ||
| "errors" | ||
| "fmt" | ||
| ) | ||
|
|
||
| // Sealer encrypts and decrypts secrets with AES-GCM. | ||
| type Sealer struct { | ||
| aead cipher.AEAD | ||
| } | ||
|
|
||
| // NewSealer builds a Sealer from the given key. | ||
| func NewSealer(key []byte) (*Sealer, error) { | ||
| block, err := aes.NewCipher(key) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("secrets: creating cipher: %w", err) | ||
| } | ||
|
|
||
| aead, err := cipher.NewGCM(block) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("secrets: creating gcm: %w", err) | ||
| } | ||
|
|
||
| return &Sealer{aead: aead}, nil | ||
| } | ||
|
|
||
| // Seal encrypts plaintext, prepending a fresh nonce so the result can | ||
| // be stored as it stands. | ||
| func (s *Sealer) Seal(plaintext, additionalData []byte) ([]byte, error) { | ||
| nonce := make([]byte, s.aead.NonceSize()) | ||
| if _, err := rand.Read(nonce); err != nil { | ||
| return nil, fmt.Errorf("secrets: generating nonce: %w", err) | ||
| } | ||
|
|
||
| return s.aead.Seal(nonce, nonce, plaintext, additionalData), nil | ||
| } | ||
|
|
||
| // Open decrypts a value produced by Seal. | ||
| func (s *Sealer) Open(sealed, additionalData []byte) ([]byte, error) { | ||
| nonceSize := s.aead.NonceSize() | ||
| if len(sealed) < nonceSize+s.aead.Overhead() { | ||
| return nil, errors.New("secrets: sealed value too short") | ||
| } | ||
|
|
||
| nonce, ciphertext := sealed[:nonceSize], sealed[nonceSize:] | ||
|
|
||
| plaintext, err := s.aead.Open(nil, nonce, ciphertext, additionalData) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("secrets: opening: %w", err) | ||
| } | ||
|
|
||
| return plaintext, nil | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| package server | ||
|
|
||
| import ( | ||
| "net/http" | ||
| ) | ||
|
|
||
| // problemDetails is an RFC 9457 error response. The type member is omitted, | ||
| // which the RFC defines as equivalent to "about:blank". | ||
| type problemDetails struct { | ||
| Title string `json:"title"` | ||
| Status int `json:"status"` | ||
| Detail string `json:"detail,omitempty"` | ||
| Instance string `json:"instance,omitempty"` | ||
| } | ||
|
|
||
| // writeProblem sends an RFC 9457 problem response describing a failed request. | ||
| func (s *Server) writeProblem(w http.ResponseWriter, r *http.Request, status int, detail string) { | ||
| s.write(w, status, contentTypeProblem, problemDetails{ | ||
| Title: http.StatusText(status), | ||
| Status: status, | ||
| Detail: detail, | ||
| Instance: r.URL.Path, | ||
| }) | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.