Documentation
¶
Overview ¶
Package ggcr provides authentication integration between Chainguard identity tokens and the go-containerregistry library for accessing the cgr.dev container registry.
Overview ¶
This package implements the authn.Keychain interface from go-containerregistry, enabling seamless authentication to cgr.dev using Chainguard identity tokens. It handles the token exchange flow using STS (Security Token Service) to convert base tokens into Chainguard-specific access tokens.
Features ¶
- authn.Keychain implementation for cgr.dev registry authentication
- Automatic token exchange using Chainguard STS
- Support for any OAuth2 token source as the base credential
- Token reuse to minimize exchange operations
- Registry-aware authentication (only authenticates for cgr.dev)
Usage ¶
The primary entry point is the Keychain function, which creates a keychain from an identity UIDP and a base token source:
import (
"context"
"chainguard.dev/sdk/auth/ggcr"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"google.golang.org/api/idtoken"
)
func pullImage(ctx context.Context, identity string) error {
// Create a token source using ambient GCP credentials
ts, err := idtoken.NewTokenSource(ctx, "https://cgr.dev")
if err != nil {
return err
}
// Create the keychain for the specified identity
kc := ggcr.Keychain(identity, ts)
// Use the keychain to pull images
ref, err := name.ParseReference("cgr.dev/my/image:latest")
if err != nil {
return err
}
img, err := remote.Image(ref, remote.WithAuthFromKeychain(kc))
if err != nil {
return err
}
// Work with the image...
return nil
}
Integration Patterns ¶
The package integrates with go-containerregistry's remote operations:
- remote.Image: Pull container images
- remote.Write: Push container images
- remote.Index: Pull multi-platform image indexes
- remote.Layer: Pull individual layers
All remote operations accept the keychain via remote.WithAuthFromKeychain(kc).
For advanced use cases where you already have an OAuth2 token source configured with the complete token exchange flow, use TokenSourceKeychain directly:
kc := ggcr.TokenSourceKeychain(myConfiguredTokenSource)
Authentication Flow ¶
The authentication flow works as follows:
- Base token source provides an initial token (e.g., GCP ID token)
- STS exchanges the base token for a Chainguard access token
- Token is cached and reused until expiration
- Keychain provides the token to go-containerregistry as HTTP Basic auth
- Registry validates the token and grants access
The keychain only provides credentials when the target registry is cgr.dev. For other registries, it returns anonymous authentication, allowing go-containerregistry to fall back to other authentication methods.
Package ggcr provides a go-containerregistry authn.Keychain for the cgr.dev registry.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Keychain ¶
func Keychain(identity string, base oauth2.TokenSource) authn.Keychain
Keychain returns an authn.Keychain used to authorize requests to the cgr.dev registry using go-containerregistry.
It takes the identity UIDP to assume, and a token source to obtain the token to exchange.
This can be used with google.golang.org/api/idtoken.NewTokenSource to exchange ambient GCP credentials for Chainguard tokens:
ts, err := idtoken.NewTokenSource(ctx, "https://cgr.dev")
kc := ggcr.Keychain("my-identity", ts)
This keychain can then be used to pull images from the cgr.dev registry:
img, err := remote.Image("cgr.dev/my/image", remote.WithAuth(kc))
Example ¶
ExampleKeychain demonstrates how to create a keychain for authenticating to cgr.dev using a Chainguard identity and a base token source.
package main
import (
"context"
"fmt"
"chainguard.dev/sdk/auth/ggcr"
"github.com/chainguard-dev/clog"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"google.golang.org/api/idtoken"
)
func main() {
ctx := context.Background()
// Create a token source using ambient GCP credentials
ts, err := idtoken.NewTokenSource(ctx, "https://cgr.dev")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Create the keychain for a specific Chainguard identity
kc := ggcr.Keychain("my-identity-uidp", ts)
// Use the keychain to pull an image from cgr.dev
ref, err := name.ParseReference("cgr.dev/my/image:latest")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
img, err := remote.Image(ref, remote.WithAuthFromKeychain(kc))
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Get the image digest
digest, err := img.Digest()
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
fmt.Printf("Pulled image with digest: %s\n", digest)
}
Output:
Example (PushImage) ¶
ExampleKeychain_pushImage demonstrates using the keychain to push an image to cgr.dev.
package main
import (
"context"
"fmt"
"chainguard.dev/sdk/auth/ggcr"
"github.com/chainguard-dev/clog"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"google.golang.org/api/idtoken"
)
func main() {
ctx := context.Background()
// Create a token source
ts, err := idtoken.NewTokenSource(ctx, "https://cgr.dev")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Create the keychain
kc := ggcr.Keychain("my-identity-uidp", ts)
// Pull an image from one location
srcRef, err := name.ParseReference("cgr.dev/source/image:latest")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
img, err := remote.Image(srcRef, remote.WithAuthFromKeychain(kc))
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Push it to another location
dstRef, err := name.ParseReference("cgr.dev/destination/image:latest")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
if err := remote.Write(dstRef, img, remote.WithAuthFromKeychain(kc)); err != nil {
clog.FatalContextf(ctx, "%v", err)
}
fmt.Println("Image pushed successfully")
}
Output:
Example (WithContext) ¶
ExampleKeychain_withContext demonstrates using the keychain with a context for timeout and cancellation support.
package main
import (
"context"
"chainguard.dev/sdk/auth/ggcr"
"github.com/chainguard-dev/clog"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"google.golang.org/api/idtoken"
)
func main() {
ctx := context.Background()
// Create a token source
ts, err := idtoken.NewTokenSource(ctx, "https://cgr.dev")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Create the keychain
kc := ggcr.Keychain("my-identity-uidp", ts)
// Use with context for operations
ref, err := name.ParseReference("cgr.dev/my/image:latest")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
img, err := remote.Image(ref, remote.WithAuthFromKeychain(kc), remote.WithContext(ctx))
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Work with the image
_ = img
}
Output:
func TokenSourceKeychain ¶ added in v0.1.33
func TokenSourceKeychain(ts oauth2.TokenSource) authn.Keychain
TokenSourceKeychain returns an authn.Keychain that uses the provided oauth2.TokenSource to obtain tokens.
Example ¶
ExampleTokenSourceKeychain demonstrates how to create a keychain from a pre-configured OAuth2 token source.
package main
import (
"context"
"chainguard.dev/sdk/auth/ggcr"
"github.com/chainguard-dev/clog"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote"
"golang.org/x/oauth2"
)
func main() {
ctx := context.Background()
// Create a custom token source (this example uses a static token,
// but in practice you would use a properly configured OAuth2 source)
ts := oauth2.StaticTokenSource(&oauth2.Token{
AccessToken: "my-chainguard-token",
})
// Create the keychain directly from the token source
kc := ggcr.TokenSourceKeychain(ts)
// Use the keychain with go-containerregistry operations
ref, err := name.ParseReference("cgr.dev/my/image:latest")
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
img, err := remote.Image(ref, remote.WithAuthFromKeychain(kc))
if err != nil {
clog.FatalContextf(ctx, "%v", err)
}
// Work with the image
_ = img
}
Output:
Types ¶
This section is empty.