-
Notifications
You must be signed in to change notification settings - Fork 9
/
crane_registry_client.go
168 lines (144 loc) · 7.34 KB
/
crane_registry_client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
package crane
import (
"fmt"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/instrumentation"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/instrumentation/metric"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/instrumentation/metric/util"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/instrumentation/trace"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/registry"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/registry/wrappers"
"github.com/Azure/AzureDefender-K8S-InClusterDefense/pkg/infra/utils"
"github.com/google/go-containerregistry/pkg/authn"
"github.com/google/go-containerregistry/pkg/crane"
"github.com/pkg/errors"
)
const (
_userAgent = "azdproxy"
)
// CraneRegistryClient implements registry.IRegistryClient interface
var _ registry.IRegistryClient = (*CraneRegistryClient)(nil)
// CraneRegistryClient crane based implementation of the registry client interface registry.IRegistryClient
type CraneRegistryClient struct {
// tracerProvider is the tracer provider for the registry client
tracerProvider trace.ITracerProvider
// metricSubmitter is the metric submitter for the registry client.
metricSubmitter metric.IMetricSubmitter
// craneWrapper is the wrappers.ICraneWrapper that wraps crane mod
craneWrapper wrappers.ICraneWrapper
acrKeychainFactory IACRKeychainFactory
k8sKeychainFactory IK8SKeychainFactory
}
// NewCraneRegistryClient Constructor for the registry client
func NewCraneRegistryClient(instrumentationProvider instrumentation.IInstrumentationProvider, craneWrapper wrappers.ICraneWrapper, acrKeychainFactory IACRKeychainFactory, k8sKeychainFactory IK8SKeychainFactory) *CraneRegistryClient {
return &CraneRegistryClient{
tracerProvider: instrumentationProvider.GetTracerProvider("CraneRegistryClient"),
metricSubmitter: instrumentationProvider.GetMetricSubmitter(),
craneWrapper: craneWrapper,
acrKeychainFactory: acrKeychainFactory,
k8sKeychainFactory: k8sKeychainFactory,
}
}
// GetDigestUsingACRAttachAuth receives image reference and get it's digest using ACR attach authntication
// ACR attach auth is based MSI token used to access the registry
// Authenticate with multikeychain with acrkeychain and default keychain
func (client *CraneRegistryClient) GetDigestUsingACRAttachAuth(imageReference registry.IImageReference) (string, error) {
tracer := client.tracerProvider.GetTracer("GetDigestUsingACRAttachAuth")
tracer.Info("Received image:", "imageReference", imageReference)
// Argument validation
if imageReference == nil {
err := errors.Wrap(utils.NilArgumentError, "CraneRegistryClient.GetDigestUsingACRAttachAuth")
tracer.Error(err, "")
client.metricSubmitter.SendMetric(1, util.NewErrorEncounteredMetric(err, "CraneRegistryClient.GetDigestUsingK8SAuth"))
return "", err
}
// Create ACR auth keychain to registry - keychain with refresh token
acrKeyChain, err := client.acrKeychainFactory.Create(imageReference.Registry())
if err != nil {
err = errors.Wrap(err, "CraneRegistryClient.GetDigestUsingACRAttachAuth: could not create acrKeychain")
tracer.Error(err, "")
return "", err
}
// Get digest and passing the keychain
digest, err := client.getDigest(imageReference, acrKeyChain)
if err != nil {
// Report error
err = errors.Wrap(err, "Failed with client. getDigest:")
tracer.Error(err, "")
return "", err
}
tracer.Info("Image resolved successfully", "imageRef", imageReference, "digest", digest)
return digest, nil
}
// GetDigestUsingK8SAuth receives image reference and get it's digest using K8S secerts and auth
// K8S auth is based image pull secrets used in deployment or attached to service account to pull the image
// Authenticate with multikeychain with k8skeychain and default keychain
func (client *CraneRegistryClient) GetDigestUsingK8SAuth(imageReference registry.IImageReference, namespace string, imagePullSecrets []string, serviceAccountName string) (string, error) {
tracer := client.tracerProvider.GetTracer("GetDigestUsingK8SAuth")
tracer.Info("Received image:", "imageReference", imageReference, "namespace", namespace, "imagePullSecrets", imagePullSecrets, "serviceAccountName", serviceAccountName)
// Argument validation
if imageReference == nil {
err := errors.Wrap(utils.NilArgumentError, "CraneRegistryClient.GetDigestUsingK8SAuth")
tracer.Error(err, "")
client.metricSubmitter.SendMetric(1, util.NewErrorEncounteredMetric(err, "CraneRegistryClient.GetDigestUsingK8SAuth"))
return "", err
}
// Create K8S keychain
k8sKeychain, err := client.k8sKeychainFactory.Create(namespace, imagePullSecrets, serviceAccountName)
if err != nil {
err = errors.Wrap(err, "CraneRegistryClient.GetDigestUsingK8SAuth: could not create k8sKeychain")
tracer.Error(err, "")
return "", err
}
// Get digest and passing keychain
digest, err := client.getDigest(imageReference, k8sKeychain)
if err != nil {
// Report error
err = errors.Wrap(err, "Failed with client.getDigest:")
tracer.Error(err, "")
return "", err
}
tracer.Info("Image resolved successfully", "imageRef", imageReference, "digest", digest)
return digest, nil
}
// GetDigestUsingDefaultAuth receives image reference and get it's digest using the default docker config auth
func (client *CraneRegistryClient) GetDigestUsingDefaultAuth(imageReference registry.IImageReference) (string, error) {
tracer := client.tracerProvider.GetTracer("GetDigestUsingDefaultAuth")
tracer.Info("Received image:", "imageReference", imageReference)
// Argument validation
if imageReference == nil {
err := errors.Wrap(utils.NilArgumentError, "CraneRegistryClient.GetDigestUsingDefaultAuth")
tracer.Error(err, "")
client.metricSubmitter.SendMetric(1, util.NewErrorEncounteredMetric(err, "CraneRegistryClient.GetDigestUsingDefaultAuth"))
return "", err
}
// Get digest
digest, err := client.getDigest(imageReference, authn.DefaultKeychain)
if err != nil {
// Report error
err = errors.Wrap(err, "Failed with client.getDigest")
tracer.Error(err, "")
return "", err
}
tracer.Info("Image resolved successfully", "imageRef", imageReference, "digest", digest)
return digest, nil
}
// getDigest private function that receives imageReference and a keychain, it wraps keychain received with multikeychain and appends the defaultkeychain as well
// Then calls crane Digest function using the multikeychian created and the client _userAgent
func (client *CraneRegistryClient) getDigest(imageReference registry.IImageReference, keychain authn.Keychain) (string, error) {
tracer := client.tracerProvider.GetTracer("getDigest")
receivedKeyChainType := fmt.Sprintf("%T", keychain)
tracer.Info("Received image:", "imageReference", imageReference.Original(), "receivedKeyChainType", receivedKeyChainType)
// Resolve digest using Options:
// - multikeychain of received keychain and the default keychain,
// - _userAgent of the client
digest, err := client.craneWrapper.Digest(imageReference.Original(), crane.WithAuthFromKeychain(authn.NewMultiKeychain(keychain, authn.DefaultKeychain)), crane.WithUserAgent(_userAgent))
if err != nil {
// Report error
err = errors.Wrapf(err, "CraneRegistryClient.getDigest with receivedKeyChainType %v", receivedKeyChainType)
tracer.Error(err, "")
return "", err
}
// Log digest and return it
tracer.Info("Image resolved successfully", "imageRef", imageReference.Original(), "digest", digest)
return digest, nil
}