1
0
mirror of synced 2026-10-24 12:59:22 +03:00
Files
cosign/pkg/providers/github/github.go
Eugene Kalinin e9af93ff29 fix(providers): clearer errors when GitHub OIDC token request fails (#5062)
The GitHub Actions provider decoded the response body without checking the
status code, so a failing OIDC endpoint surfaced as a raw json error:

  fetching ambient OIDC credentials: invalid character 'u' looking for
  beginning of value

Check the status code first and report it along with a truncated body, and
wrap decode failures with the provider name:

  fetching ambient OIDC credentials: identity token request to provider
  github-actions failed with status 502 Bad Gateway: "upstream connect
  error or disconnect/reset before headers"

Fixes #4438

Signed-off-by: Eugene Kalinin <e.v.kalinin@gmail.com>
2026-08-18 09:09:02 -07:00

118 lines
3.4 KiB
Go

//
// Copyright 2021 The Sigstore Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package github
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"strings"
"time"
"github.com/sigstore/cosign/v3/pkg/cosign/env"
"github.com/sigstore/cosign/v3/pkg/providers"
)
const (
// Deprecated: use `env.VariableGitHubRequestToken` instead
RequestTokenEnvKey = env.VariableGitHubRequestToken
// Deprecated: use `env.VariableGitHubRequestURL` instead
RequestURLEnvKey = env.VariableGitHubRequestURL
// providerName is the name this provider is registered under.
providerName = "github-actions"
// maxErrorBodyBytes limits how much of a failed response body is quoted back in the error.
maxErrorBodyBytes = 256
)
func init() {
providers.Register(providerName, &githubActions{})
}
type githubActions struct{}
var _ providers.Interface = (*githubActions)(nil)
// Enabled implements providers.Interface
func (ga *githubActions) Enabled(_ context.Context) bool {
if env.Getenv(env.VariableGitHubRequestToken) == "" {
return false
}
if env.Getenv(env.VariableGitHubRequestURL) == "" {
return false
}
return true
}
// Provide implements providers.Interface
func (ga *githubActions) Provide(ctx context.Context, audience string) (string, error) {
url := env.Getenv(env.VariableGitHubRequestURL) + "&audience=" + audience
req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return "", err
}
// May be replaced by a different client if we hit HTTP_1_1_REQUIRED.
client := http.DefaultClient
// Retry up to 3 times.
for i := 0; ; i++ {
req.Header.Set("Authorization", "bearer "+env.Getenv(env.VariableGitHubRequestToken))
resp, err := client.Do(req)
if err != nil {
if i == 2 {
return "", err
}
// This error isn't exposed by net/http, and retrying this with the
// DefaultClient will fail because it will just use HTTP2 again.
// I don't know why go doesn't do this for us.
if strings.Contains(err.Error(), "HTTP_1_1_REQUIRED") {
http1transport := http.DefaultTransport.(*http.Transport).Clone()
http1transport.ForceAttemptHTTP2 = false
client = &http.Client{
Transport: http1transport,
}
}
fmt.Fprintf(os.Stderr, "error fetching GitHub OIDC token (will retry): %v\n", err)
time.Sleep(time.Second)
continue
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(io.LimitReader(resp.Body, maxErrorBodyBytes))
return "", fmt.Errorf("identity token request to provider %s failed with status %s: %q",
providerName, resp.Status, strings.TrimSpace(string(body)))
}
var payload struct {
Value string `json:"value"`
}
decoder := json.NewDecoder(resp.Body)
if err := decoder.Decode(&payload); err != nil {
return "", fmt.Errorf("invalid identity token response from provider %s: %w", providerName, err)
}
return payload.Value, nil
}
}