Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use registry authentication for azure sp credentials, when authType i… #631

Merged
merged 6 commits into from
Nov 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
}