mirror of
https://github.com/containrrr/watchtower.git
synced 2026-01-19 07:16:10 +01:00
feat: move registry http calls to common client
This commit is contained in:
parent
9220b51665
commit
6c1dfecea3
7 changed files with 212 additions and 171 deletions
|
|
@ -1,117 +1,15 @@
|
|||
package auth
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/containrrr/watchtower/pkg/registry/helpers"
|
||||
"github.com/containrrr/watchtower/pkg/types"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// ChallengeHeader is the HTTP Header containing challenge instructions
|
||||
const ChallengeHeader = "WWW-Authenticate"
|
||||
|
||||
// GetToken fetches a token for the registry hosting the provided image
|
||||
func GetToken(ctx context.Context, container types.Container, registryAuth string) (string, error) {
|
||||
var err error
|
||||
var URL url.URL
|
||||
|
||||
if URL, err = GetChallengeURL(container.ImageName()); err != nil {
|
||||
return "", err
|
||||
}
|
||||
logrus.WithField("URL", URL.String()).Debug("Building challenge URL")
|
||||
|
||||
var req *http.Request
|
||||
if req, err = GetChallengeRequest(ctx, URL); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var res *http.Response
|
||||
if res, err = http.DefaultClient.Do(req); err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
v := res.Header.Get(ChallengeHeader)
|
||||
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"status": res.Status,
|
||||
"header": v,
|
||||
}).Debug("Got response to challenge request")
|
||||
|
||||
challenge := strings.ToLower(v)
|
||||
if strings.HasPrefix(challenge, "basic") {
|
||||
if registryAuth == "" {
|
||||
return "", fmt.Errorf("no credentials available")
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Basic %s", registryAuth), nil
|
||||
}
|
||||
if strings.HasPrefix(challenge, "bearer") {
|
||||
return GetBearerHeader(ctx, challenge, container.ImageName(), registryAuth)
|
||||
}
|
||||
|
||||
return "", errors.New("unsupported challenge type from registry")
|
||||
}
|
||||
|
||||
// GetChallengeRequest creates a request for getting challenge instructions
|
||||
func GetChallengeRequest(ctx context.Context, URL url.URL) (*http.Request, error) {
|
||||
req, err := http.NewRequestWithContext(ctx, "GET", URL.String(), nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("Accept", "*/*")
|
||||
req.Header.Set("User-Agent", "Watchtower (Docker)")
|
||||
return req, nil
|
||||
}
|
||||
|
||||
// GetBearerHeader tries to fetch a bearer token from the registry based on the challenge instructions
|
||||
func GetBearerHeader(ctx context.Context, challenge string, img string, registryAuth string) (string, error) {
|
||||
if strings.Contains(img, ":") {
|
||||
img = strings.Split(img, ":")[0]
|
||||
}
|
||||
authURL, err := GetAuthURL(challenge, img)
|
||||
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var r *http.Request
|
||||
if r, err = http.NewRequestWithContext(ctx, "GET", authURL.String(), nil); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if registryAuth != "" {
|
||||
logrus.Debug("Credentials found.")
|
||||
logrus.Tracef("Credentials: %v", registryAuth)
|
||||
r.Header.Add("Authorization", fmt.Sprintf("Basic %s", registryAuth))
|
||||
} else {
|
||||
logrus.Debug("No credentials found.")
|
||||
}
|
||||
|
||||
var authResponse *http.Response
|
||||
if authResponse, err = http.DefaultClient.Do(r); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
body, _ := ioutil.ReadAll(authResponse.Body)
|
||||
tokenResponse := &types.TokenResponse{}
|
||||
|
||||
err = json.Unmarshal(body, tokenResponse)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Bearer %s", tokenResponse.Token), nil
|
||||
}
|
||||
|
||||
// GetAuthURL from the instructions in the challenge
|
||||
func GetAuthURL(challenge string, img string) (*url.URL, error) {
|
||||
loweredChallenge := strings.ToLower(challenge)
|
||||
|
|
|
|||
|
|
@ -1,14 +1,10 @@
|
|||
package auth_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/containrrr/watchtower/internal/actions/mocks"
|
||||
"github.com/containrrr/watchtower/pkg/registry/auth"
|
||||
|
||||
wtTypes "github.com/containrrr/watchtower/pkg/types"
|
||||
|
|
@ -34,37 +30,13 @@ func SkipIfCredentialsEmpty(credentials *wtTypes.RegistryCredentials, fn func())
|
|||
}
|
||||
}
|
||||
|
||||
var ctx = context.Background()
|
||||
|
||||
var GHCRCredentials = &wtTypes.RegistryCredentials{
|
||||
Username: os.Getenv("CI_INTEGRATION_TEST_REGISTRY_GH_USERNAME"),
|
||||
Password: os.Getenv("CI_INTEGRATION_TEST_REGISTRY_GH_PASSWORD"),
|
||||
}
|
||||
|
||||
var _ = Describe("the auth module", func() {
|
||||
mockId := "mock-id"
|
||||
mockName := "mock-container"
|
||||
mockImage := "ghcr.io/k6io/operator:latest"
|
||||
mockCreated := time.Now()
|
||||
mockDigest := "ghcr.io/k6io/operator@sha256:d68e1e532088964195ad3a0a71526bc2f11a78de0def85629beb75e2265f0547"
|
||||
|
||||
mockContainer := mocks.CreateMockContainerWithDigest(
|
||||
mockId,
|
||||
mockName,
|
||||
mockImage,
|
||||
mockCreated,
|
||||
mockDigest)
|
||||
|
||||
When("getting an auth url", func() {
|
||||
It("should parse the token from the response",
|
||||
SkipIfCredentialsEmpty(GHCRCredentials, func() {
|
||||
creds := fmt.Sprintf("%s:%s", GHCRCredentials.Username, GHCRCredentials.Password)
|
||||
token, err := auth.GetToken(ctx, mockContainer, creds)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(token).NotTo(Equal(""))
|
||||
}),
|
||||
)
|
||||
|
||||
It("should create a valid auth url object based on the challenge header supplied", func() {
|
||||
input := `bearer realm="https://ghcr.io/token",service="ghcr.io",scope="repository:user/image:pull"`
|
||||
expected := &url.URL{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue