Implement autologin for GCP and Azure
Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com> Signed-off-by: Somtochi Onyekwere <somtochionyekwere@gmail.com>
This commit is contained in:
parent
86d5c2915c
commit
fcd6c921b6
|
|
@ -27,6 +27,10 @@ jobs:
|
|||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
# azure-sdk-for-go requires 1.16 or greater
|
||||
- uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: '1.17.x'
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
|
|
@ -46,6 +47,9 @@ import (
|
|||
"sigs.k8s.io/controller-runtime/pkg/controller"
|
||||
"sigs.k8s.io/controller-runtime/pkg/predicate"
|
||||
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
|
||||
"github.com/Azure/azure-sdk-for-go/sdk/azidentity"
|
||||
"github.com/aws/aws-sdk-go/aws"
|
||||
"github.com/aws/aws-sdk-go/aws/session"
|
||||
"github.com/aws/aws-sdk-go/service/ecr"
|
||||
|
|
@ -56,6 +60,7 @@ import (
|
|||
"github.com/fluxcd/pkg/runtime/predicates"
|
||||
|
||||
imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
|
||||
"github.com/fluxcd/image-reflector-controller/internal/azure"
|
||||
)
|
||||
|
||||
// These are intended to match the keys used in e.g.,
|
||||
|
|
@ -80,7 +85,9 @@ type ImageRepositoryReconciler struct {
|
|||
DatabaseReader
|
||||
}
|
||||
|
||||
AwsAutoLogin bool // automatically attempt to get credentials for images in ECR
|
||||
AwsAutoLogin bool // automatically attempt to get credentials for images in ECR
|
||||
GcpAutoLogin bool // automatically attempt to get credentials for images in GCP
|
||||
AzureAutoLogin bool // automatically attempt to get credentials for images in ACR
|
||||
}
|
||||
|
||||
type ImageRepositoryReconcilerOptions struct {
|
||||
|
|
@ -91,6 +98,12 @@ type dockerConfig struct {
|
|||
Auths map[string]authn.AuthConfig
|
||||
}
|
||||
|
||||
type gceToken struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
ExpiresIn int `json:"expires_in"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=image.toolkit.fluxcd.io,resources=imagerepositories,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=image.toolkit.fluxcd.io,resources=imagerepositories/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch
|
||||
|
|
@ -246,6 +259,53 @@ func getAwsECRLoginAuth(accountId, awsEcrRegion string) (authn.AuthConfig, error
|
|||
return authConfig, nil
|
||||
}
|
||||
|
||||
// getGCRLoginAuth obtains authentication for the image by
|
||||
// getting a token from the metadata API on GCP. This assumes that
|
||||
// the pod has right to pull the image which would be the case if it
|
||||
// is hosted on GCP. It works with both service account and workload identity
|
||||
// enabled clusters.
|
||||
func getGCRLoginAuth(ctx context.Context) (authn.AuthConfig, error) {
|
||||
var authConfig authn.AuthConfig
|
||||
const gcpDefaultTokenURL = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/token"
|
||||
|
||||
request, err := http.NewRequestWithContext(ctx, http.MethodGet, gcpDefaultTokenURL, nil)
|
||||
if err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
|
||||
request.Header.Add("Metadata-Flavor", "Google")
|
||||
|
||||
client := &http.Client{}
|
||||
response, err := client.Do(request)
|
||||
if err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
|
||||
if response.StatusCode != http.StatusOK {
|
||||
return authConfig, fmt.Errorf("unexpected status from metadata service: %s", response.Status)
|
||||
}
|
||||
|
||||
var accessToken gceToken
|
||||
decoder := json.NewDecoder(response.Body)
|
||||
if err := decoder.Decode(&accessToken); err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
|
||||
if _, err := io.Copy(io.Discard, response.Body); err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
|
||||
if err := response.Body.Close(); err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
|
||||
authConfig = authn.AuthConfig{
|
||||
Username: "oauth2accesstoken",
|
||||
Password: accessToken.AccessToken,
|
||||
}
|
||||
return authConfig, nil
|
||||
}
|
||||
|
||||
func (r *ImageRepositoryReconciler) scan(ctx context.Context, imageRepo *imagev1.ImageRepository, ref name.Reference) error {
|
||||
timeout := imageRepo.GetTimeout()
|
||||
ctx, cancel := context.WithTimeout(ctx, timeout)
|
||||
|
|
@ -297,6 +357,46 @@ func (r *ImageRepositoryReconciler) scan(ctx context.Context, imageRepo *imagev1
|
|||
} else {
|
||||
ctrl.LoggerFrom(ctx).Info("No image credentials secret referenced, and ECR authentication is not enabled. To enable, set the controller flag --aws-autologin-for-ecr")
|
||||
}
|
||||
} else if hostIsGoogleContainerRegistry(ref.Context().RegistryStr()) {
|
||||
if r.GcpAutoLogin {
|
||||
ctrl.LoggerFrom(ctx).Info("Logging in to GCP GCR for " + imageRepo.Spec.Image)
|
||||
authConfig, err := getGCRLoginAuth(ctx)
|
||||
if err != nil {
|
||||
ctrl.LoggerFrom(ctx).Info("error logging into GCP " + err.Error())
|
||||
imagev1.SetImageRepositoryReadiness(
|
||||
imageRepo,
|
||||
metav1.ConditionFalse,
|
||||
meta.ReconciliationFailedReason,
|
||||
err.Error(),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
auth := authn.FromConfig(authConfig)
|
||||
options = append(options, remote.WithAuth(auth))
|
||||
} else {
|
||||
ctrl.LoggerFrom(ctx).Info("No image credentials secret referenced, and GCR authentication is not enabled. To enable, set the controller flag --gcp-autologin-for-gcr")
|
||||
}
|
||||
} else if hostIsAzureContainerRegistry(ref.Context().RegistryStr()) {
|
||||
if r.AzureAutoLogin {
|
||||
ctrl.LoggerFrom(ctx).Info("Logging in to Azure ACR for " + imageRepo.Spec.Image)
|
||||
authConfig, err := getAzureLoginAuth(ctx, ref)
|
||||
if err != nil {
|
||||
ctrl.LoggerFrom(ctx).Info("error logging into ACR " + err.Error())
|
||||
imagev1.SetImageRepositoryReadiness(
|
||||
imageRepo,
|
||||
metav1.ConditionFalse,
|
||||
meta.ReconciliationFailedReason,
|
||||
err.Error(),
|
||||
)
|
||||
return err
|
||||
}
|
||||
|
||||
auth := authn.FromConfig(authConfig)
|
||||
options = append(options, remote.WithAuth(auth))
|
||||
} else {
|
||||
ctrl.LoggerFrom(ctx).Info("No image credentials secret referenced, and ACR authentication is not enabled. To enable, set the controller flag --azure-autologin-for-acr")
|
||||
}
|
||||
}
|
||||
|
||||
if imageRepo.Spec.CertSecretRef != nil {
|
||||
|
|
@ -591,3 +691,47 @@ func getURLHost(urlStr string) (string, error) {
|
|||
|
||||
return u.Host, nil
|
||||
}
|
||||
|
||||
// getAzureLoginAuth returns authentication for ACR. The details needed for authentication
|
||||
// are gotten from environment variable so there is not need to mount a host path.
|
||||
func getAzureLoginAuth(ctx context.Context, ref name.Reference) (authn.AuthConfig, error) {
|
||||
var authConfig authn.AuthConfig
|
||||
|
||||
cred, err := azidentity.NewDefaultAzureCredential(nil)
|
||||
if err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
armToken, err := cred.GetToken(ctx, policy.TokenRequestOptions{
|
||||
Scopes: []string{string(arm.AzurePublicCloud)},
|
||||
})
|
||||
if err != nil {
|
||||
return authConfig, err
|
||||
}
|
||||
|
||||
ex := azure.NewExchanger(ref.Context().RegistryStr())
|
||||
accessToken, err := ex.ExchangeACRAccessToken(string(armToken.Token))
|
||||
if err != nil {
|
||||
return authConfig, fmt.Errorf("error exchanging token: %w", err)
|
||||
}
|
||||
|
||||
return authn.AuthConfig{
|
||||
// this is the acr username used by Azure
|
||||
// See documentation: https://docs.microsoft.com/en-us/azure/container-registry/container-registry-authentication?tabs=azure-cli#az-acr-login-with---expose-token
|
||||
Username: "00000000-0000-0000-0000-000000000000",
|
||||
Password: accessToken,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// List from https://github.com/kubernetes/kubernetes/blob/v1.23.1/pkg/credentialprovider/azure/azure_credentials.go#L55
|
||||
func hostIsAzureContainerRegistry(host string) bool {
|
||||
for _, v := range []string{".azurecr.io", ".azurecr.cn", ".azurecr.de", ".azurecr.us"} {
|
||||
if strings.HasSuffix(host, v) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func hostIsGoogleContainerRegistry(host string) bool {
|
||||
return host == "gcr.io" || strings.HasSuffix(host, ".gcr.io") || strings.HasSuffix(host, "-docker.pkg.dev")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,13 +73,18 @@ repository. This secret is expected to be in the same format as for
|
|||
|
||||
For a publicly accessible image repository, you will not need to provide a `secretRef`.
|
||||
|
||||
#### ECR and EKS
|
||||
#### Automatic Authentication
|
||||
|
||||
When running in [<abbr title="Elastic Kubernetes Service">EKS</abbr>][EKS] and using [<abbr
|
||||
title="Elastic Container Registry">ECR</abbr>][ECR] to store images, you should be able to rely on
|
||||
the controller retrieving credentials automatically. The controller must be run with the flag
|
||||
`--aws-autologin-for-ecr` set for this to work. The advice under "Other platforms" below will also
|
||||
work for ECR.
|
||||
When running on any of the three major cloud providers and using their container registry to store images,
|
||||
you should be able to rely on the controller retrieving credentials automatically. The controllers must be run
|
||||
with the corresponding flag for each provider.
|
||||
|
||||
For [<abbr title="Elastic Kubernetes Service">EKS</abbr>][EKS] and [<abbr title="Elastic Container Registry">ECR</abbr>][ECR],
|
||||
the flag is `--aws-autologin-for-ecr`.
|
||||
For [<abbr title="Google Kubernetes Engine">GKE</abbr>][GKE] and [<abbr title="Google Container Registry">GCR</abbr>][GCR],
|
||||
the flag is `--gcp-autologin-for-gcr`.
|
||||
For [<abbr title="Azure Kubernetes Service">AKS</abbr>][AKS] and [<abbr title="Azure Container Registry">ACR</abbr>][ACR],
|
||||
the flag is `--azure-autologin-for-acr`.
|
||||
|
||||
#### Other platforms
|
||||
|
||||
|
|
@ -261,3 +266,7 @@ and reference it under `secretRef`.
|
|||
[sops-guide]: https://toolkit.fluxcd.io/guides/mozilla-sops/
|
||||
[EKS]: https://docs.aws.amazon.com/eks/latest/userguide/what-is-eks.html
|
||||
[ECR]: https://docs.aws.amazon.com/AmazonECR/latest/userguide/what-is-ecr.html
|
||||
[GKE]: https://cloud.google.com/kubernetes-engine/docs/concepts/kubernetes-engine-overview
|
||||
[GCR]: https://cloud.google.com/container-registry/docs/overview
|
||||
[AKS]: https://docs.microsoft.com/en-us/azure/aks/intro-kubernetes
|
||||
[ACR]: https://docs.microsoft.com/en-us/azure/container-registry/container-registry-intro
|
||||
|
|
|
|||
5
go.mod
5
go.mod
|
|
@ -5,6 +5,8 @@ go 1.17
|
|||
replace github.com/fluxcd/image-reflector-controller/api => ./api
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.20.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.12.0
|
||||
github.com/Masterminds/semver/v3 v3.1.1
|
||||
github.com/aws/aws-sdk-go v1.42.9
|
||||
github.com/dgraph-io/badger/v3 v3.2103.2
|
||||
|
|
@ -23,6 +25,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.1 // indirect
|
||||
cloud.google.com/go v0.97.0 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash v1.1.0 // indirect
|
||||
|
|
@ -61,6 +64,7 @@ require (
|
|||
github.com/nxadm/tail v1.4.8 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2-0.20210730191737-8e42a01fb1b7 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/prometheus/client_golang v1.11.0 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
|
|
@ -72,6 +76,7 @@ require (
|
|||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.19.1 // indirect
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa // indirect
|
||||
golang.org/x/net v0.0.0-20211209124913-491a49abca63 // indirect
|
||||
golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
|
||||
|
|
|
|||
18
go.sum
18
go.sum
|
|
@ -46,7 +46,14 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl
|
|||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible h1:KnPIugL51v3N3WwvaSmZbxukD1WuWXOiE9fRdu32f2I=
|
||||
github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.20.0 h1:KQgdWmEOmaJKxaUUZwHAYh12t+b+ZJf8q3friycK1kA=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.20.0/go.mod h1:ZPW/Z0kLCTdDZaDbYTetxc9Cxl/2lNqxYHYNOF2bti0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.12.0 h1:VBvHGLJbaY0+c66NZHdS9cgjHVYSH6DDa0XJMyrblsI=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.12.0/go.mod h1:GJzjM4SR9T0KyX5gKCVyz1ytD8FeWeUPCwtFCt1AyfE=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.1 h1:BUYIbDf/mMZ8945v3QkG3OuqGVyS4Iek0AOLwdRAYoc=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.8.1/go.mod h1:KLF4gFr6DcKFZwSuH8w8yEK6DpFl3LP5rhdvAb7Yz5I=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
|
|
@ -289,6 +296,7 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC
|
|||
github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
|
||||
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
|
||||
github.com/docker/cli v20.10.10+incompatible h1:kcbwdgWbrBOH8QwQzaJmyriHwF7XIl4HT1qh0HTRys4=
|
||||
github.com/docker/cli v20.10.10+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
|
||||
|
|
@ -627,6 +635,7 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
|
|||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
|
||||
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
|
||||
github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
|
|
@ -690,6 +699,9 @@ github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/9
|
|||
github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
|
||||
github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
@ -905,8 +917,11 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa h1:idItI2DDfCokpg0N51B2VtiLdJ4vAuXC9fnCb2gACo4=
|
||||
golang.org/x/crypto v0.0.0-20211108221036-ceb1ce70b4fa/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
|
|
@ -982,6 +997,7 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
|
|||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
|
|
@ -995,6 +1011,7 @@ golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96b
|
|||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
|
|
@ -1120,6 +1137,7 @@ golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBc
|
|||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
MIT License
|
||||
|
||||
Copyright (c) Microsoft Corporation.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE
|
||||
*/
|
||||
|
||||
/*
|
||||
Copyright 2022 The Flux 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.
|
||||
This package exchanges an ARM access token for an ACR access token on Azure
|
||||
It has been derived from
|
||||
https://github.com/Azure/msi-acrpull/blob/main/pkg/authorizer/token_exchanger.go
|
||||
since the project isn't actively maintained.
|
||||
*/
|
||||
|
||||
package azure
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
type tokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
RefreshToken string `json:"refresh_token"`
|
||||
Resource string `json:"resource"`
|
||||
TokenType string `json:"token_type"`
|
||||
}
|
||||
|
||||
type acrError struct {
|
||||
Code string `json:"code"`
|
||||
Message string `json:"message"`
|
||||
}
|
||||
|
||||
type Exchanger struct {
|
||||
acrFQDN string
|
||||
}
|
||||
|
||||
func NewExchanger(acrEndpoint string) *Exchanger {
|
||||
return &Exchanger{
|
||||
acrFQDN: acrEndpoint,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *Exchanger) ExchangeACRAccessToken(armToken string) (string, error) {
|
||||
exchangeUrl := fmt.Sprintf("https://%s/oauth2/exchange", e.acrFQDN)
|
||||
parsedURL, err := url.Parse(exchangeUrl)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
parameters := url.Values{}
|
||||
parameters.Add("grant_type", "access_token")
|
||||
parameters.Add("service", parsedURL.Hostname())
|
||||
parameters.Add("access_token", armToken)
|
||||
|
||||
resp, err := http.PostForm(exchangeUrl, parameters)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to send token exchange request: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
var errors []acrError
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err = decoder.Decode(&errors); err == nil {
|
||||
return "", fmt.Errorf("unexpected status code %d from exchnage request: errors:%s",
|
||||
resp.StatusCode, errors)
|
||||
}
|
||||
|
||||
return "", fmt.Errorf("unexpected status code %d from exchnage request", resp.StatusCode)
|
||||
}
|
||||
|
||||
var tokenResp tokenResponse
|
||||
decoder := json.NewDecoder(resp.Body)
|
||||
if err = decoder.Decode(&tokenResp); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return tokenResp.RefreshToken, nil
|
||||
}
|
||||
6
main.go
6
main.go
|
|
@ -70,6 +70,8 @@ func main() {
|
|||
storageValueLogFileSize int64
|
||||
concurrent int
|
||||
awsAutoLogin bool
|
||||
gcpAutoLogin bool
|
||||
azureAutoLogin bool
|
||||
)
|
||||
|
||||
flag.StringVar(&metricsAddr, "metrics-addr", ":8080", "The address the metric endpoint binds to.")
|
||||
|
|
@ -81,6 +83,8 @@ func main() {
|
|||
flag.Int64Var(&storageValueLogFileSize, "storage-value-log-file-size", 1<<28, "Set the database's memory mapped value log file size in bytes. Effective memory usage is about two times this size.")
|
||||
flag.IntVar(&concurrent, "concurrent", 4, "The number of concurrent resource reconciles.")
|
||||
flag.BoolVar(&awsAutoLogin, "aws-autologin-for-ecr", false, "(AWS) Attempt to get credentials for images in Elastic Container Registry, when no secret is referenced")
|
||||
flag.BoolVar(&gcpAutoLogin, "gcp-autologin-for-gcr", false, "(GCP) Attempt to get credentials for images in Google Container Registry, when no secret is referenced")
|
||||
flag.BoolVar(&azureAutoLogin, "azure-autologin-for-acr", false, "(Azure) Attempt to get credentials for images in Azure Container Registry, when no secret is referenced")
|
||||
|
||||
clientOptions.BindFlags(flag.CommandLine)
|
||||
logOptions.BindFlags(flag.CommandLine)
|
||||
|
|
@ -148,6 +152,8 @@ func main() {
|
|||
MetricsRecorder: metricsRecorder,
|
||||
Database: db,
|
||||
AwsAutoLogin: awsAutoLogin,
|
||||
GcpAutoLogin: gcpAutoLogin,
|
||||
AzureAutoLogin: azureAutoLogin,
|
||||
}).SetupWithManager(mgr, controllers.ImageRepositoryReconcilerOptions{
|
||||
MaxConcurrentReconciles: concurrent,
|
||||
}); err != nil {
|
||||
|
|
|
|||
Loading…
Reference in New Issue