Skip to content

Commit

Permalink
Merge pull request #631 from abhilashjoseph/master
Browse files Browse the repository at this point in the history
Use registry authentication for azure sp credentials, when authType is azureCloudConfig
  • Loading branch information
181192 authored Nov 12, 2023
2 parents ffbc2f5 + 5a546e0 commit baaa950
Show file tree
Hide file tree
Showing 7 changed files with 145 additions and 22 deletions.
5 changes: 4 additions & 1 deletion cmd/azure-keyvault-controller/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,10 @@ func main() {

if logFormat == "json" {
loggerFactory := jsonlogs.Factory{}
logger, _ := loggerFactory.Create(logConfig.LoggingConfiguration{}, logConfig.LoggingOptions{})
logger, _ := loggerFactory.Create(*logConfig.NewLoggingConfiguration(), logConfig.LoggingOptions{
ErrorStream: os.Stderr,
InfoStream: os.Stdout,
})
klog.SetLogger(logger)
}
klog.InfoS("log settings", "format", logFormat, "level", flag.Lookup("v").Value)
Expand Down
2 changes: 1 addition & 1 deletion cmd/azure-keyvault-env/environment.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

const (
envLookupRegex = `^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)@azurekeyvault(\?([a-zA-Z_][a-zA-Z0-9_\.]*)?)?$`
envLookupRegex = `^([a-zA-Z0-9]([a-zA-Z0-9-]{0,253}[a-zA-Z0-9])?)@azurekeyvault(\?([a-zA-Z_][a-zA-Z0-9_\.]*)?)?$`
)

type EnvSecret struct {
Expand Down
5 changes: 4 additions & 1 deletion cmd/azure-keyvault-env/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,10 @@ func main() {

if logFormat == "json" {
loggerFactory := jsonlogs.Factory{}
logger, _ := loggerFactory.Create(logConfig.LoggingConfiguration{}, logConfig.LoggingOptions{})
logger, _ := loggerFactory.Create(*logConfig.NewLoggingConfiguration(), logConfig.LoggingOptions{
ErrorStream: os.Stderr,
InfoStream: os.Stdout,
})
klog.SetLogger(logger)
}

Expand Down
8 changes: 6 additions & 2 deletions cmd/azure-keyvault-secrets-webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,11 @@ func main() {

if params.logFormat == "json" {
loggerFactory := jsonlogs.Factory{}
logger, _ := loggerFactory.Create(logConfig.LoggingConfiguration{}, logConfig.LoggingOptions{})
logger, _ := loggerFactory.Create(*logConfig.NewLoggingConfiguration(), logConfig.LoggingOptions{
ErrorStream: os.Stderr,
InfoStream: os.Stdout,
})

klog.SetLogger(logger)
}

Expand Down Expand Up @@ -319,7 +323,7 @@ func main() {
wg := new(sync.WaitGroup)
wg.Add(2)

config.registry = registry.NewRegistry(config.cloudConfig)
config.registry = registry.NewRegistry(config.authType, config.credentialProvider)

createHTTPEndpoint(wg, config.httpPort, config.useAuthService, config.authService)
createMTLSEndpoint(wg, config.mtlsPort, config.useAuthService, config.authService)
Expand Down
5 changes: 5 additions & 0 deletions pkg/azure/credentialprovider/acr.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ func (c CloudConfigCredentialProvider) GetAcrCredentials(image string) (k8sCrede
Password: "",
}

if !c.IsAcrRegistry(image) {
klog.V(4).Info("image not from acr, returning empty credentials")
return cred, nil
}

if c.config.UseManagedIdentityExtension {
klog.V(4).Info("using managed identity for acr credentials")
loginServer := parseACRLoginServerFromImage(image, c.environment)
Expand Down
96 changes: 79 additions & 17 deletions pkg/docker/registry/registry.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import (
"fmt"
"net/http"

"github.com/SparebankenVest/azure-key-vault-to-kubernetes/pkg/azure/credentialprovider"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/authn/k8schain"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand Down Expand Up @@ -50,13 +52,17 @@ type ImageRegistryOptions struct {

// Registry impl
type Registry struct {
imageCache *cache.Cache
authType string
imageCache *cache.Cache
credentialProvider credentialprovider.CredentialProvider
}

// NewRegistry creates and initializes registry
func NewRegistry(cloudConfigPath string) ImageRegistry {
func NewRegistry(authType string, credentialProvider credentialprovider.CredentialProvider) ImageRegistry {
return &Registry{
imageCache: cache.New(cache.NoExpiration, cache.NoExpiration),
authType: authType,
imageCache: cache.New(cache.NoExpiration, cache.NoExpiration),
credentialProvider: credentialProvider,
}
}

Expand Down Expand Up @@ -100,31 +106,83 @@ func (r *Registry) GetImageConfig(
containerInfo.ImagePullSecrets = append(containerInfo.ImagePullSecrets, imagePullSecret.Name)
}

imageConfig, err := getImageConfig(ctx, client, containerInfo, opt)
remoteOptions, err := getContainerRegistryRemoteOptions(ctx, client, containerInfo, r.authType, opt, r.credentialProvider)
if err != nil {
return nil, fmt.Errorf("failed to get remote options: %w", err)
}

imageConfig, err := getImageConfig(containerInfo, remoteOptions)
if imageConfig != nil && allowToCache {
r.imageCache.Set(container.Image, imageConfig, cache.DefaultExpiration)
}

return imageConfig, err
}

// getImageConfig download image blob from registry
func getImageConfig(ctx context.Context, client kubernetes.Interface, container containerInfo, opt ImageRegistryOptions) (*v1.Config, error) {
authChain, err := k8schain.New(
ctx,
client,
k8schain.Options{
Namespace: container.Namespace,
ServiceAccountName: container.ServiceAccountName,
ImagePullSecrets: container.ImagePullSecrets,
},
)
// getContainerRegistryRemoteOptions get container registry remote option
func getContainerRegistryRemoteOptions(ctx context.Context, client kubernetes.Interface, container containerInfo, authType string, opt ImageRegistryOptions, r credentialprovider.CredentialProvider) ([]remote.Option, error) {
ref, err := name.ParseReference(container.Image)
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse image reference: %w", err)
}
registry := ref.Context().Registry.Name()

klog.InfoS("using registry", "imageRegistry", registry)

authChain := new(authn.Keychain)
switch authType {
case "azureCloudConfig":
klog.InfoS("using cloudConfig for registry authentication", "config.authType", authType)
dockerConfigEntry, err := r.GetAcrCredentials(container.Image)
if err != nil {
return nil, fmt.Errorf("cannot fetch acr credentials: %w", err)
}

if dockerConfigEntry.Username != "" {

sec := []corev1.Secret{ //{
*dockerCfgSecretType.Create(container.Namespace, "secret", registry, authn.AuthConfig{
Username: dockerConfigEntry.Username, Password: dockerConfigEntry.Password,
}),
}
*authChain, err = k8schain.NewFromPullSecrets(
ctx,
sec,
)
if err != nil {
return nil, err
}
} else {
*authChain, err = k8schain.New(
ctx,
client,
k8schain.Options{
Namespace: container.Namespace,
ServiceAccountName: container.ServiceAccountName},
)
if err != nil {
return nil, err
}
}

default:
klog.InfoS("using imagePullSecrets for registry authentication", "config.authType", authType)
*authChain, err = k8schain.New(
ctx,
client,
k8schain.Options{
Namespace: container.Namespace,
ServiceAccountName: container.ServiceAccountName,
ImagePullSecrets: container.ImagePullSecrets,
},
)
if err != nil {
return nil, err
}
}

options := []remote.Option{
remote.WithAuthFromKeychain(authChain),
remote.WithAuthFromKeychain(*authChain),
}

if opt.SkipVerify {
Expand All @@ -133,7 +191,11 @@ func getImageConfig(ctx context.Context, client kubernetes.Interface, container
}
options = append(options, remote.WithTransport(tr))
}
return options, err
}

// getImageConfig download image blob from registry
func getImageConfig(container containerInfo, options []remote.Option) (*v1.Config, error) {
ref, err := name.ParseReference(container.Image)
if err != nil {
return nil, fmt.Errorf("failed to parse image reference: %w", err)
Expand Down
46 changes: 46 additions & 0 deletions pkg/docker/registry/registry_secret.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package registry

import (
"encoding/json"
"fmt"

"github.com/google/go-containerregistry/pkg/authn"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

type secretType struct {
name corev1.SecretType
key string
marshal func(registry string, auth authn.AuthConfig) []byte
}

func (s *secretType) Create(namespace, name string, registry string, auth authn.AuthConfig) *corev1.Secret {
return &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
Type: s.name,
Data: map[string][]byte{
s.key: s.marshal(registry, auth),
},
}
}

var dockerCfgSecretType = secretType{
name: corev1.SecretTypeDockercfg,
key: corev1.DockerConfigKey,
marshal: func(target string, auth authn.AuthConfig) []byte {
return toJSON(map[string]authn.AuthConfig{target: auth})
},
}

func toJSON(obj any) []byte {
bites, err := json.Marshal(obj)

if err != nil {
fmt.Errorf("unable to json marshal: %w", err)
}
return bites
}

0 comments on commit baaa950

Please sign in to comment.