Skip to content

Commit a1b1224

Browse files
committed
fix: correctly handle multiple registries with the same Host
Write out a single CA file for multiple registries with the same url.Host. Return an error if the CA content for those registries do not match.
1 parent dbaf14b commit a1b1224

File tree

2 files changed

+119
-12
lines changed

2 files changed

+119
-12
lines changed

pkg/handlers/generic/mutation/mirrors/containerd_files.go

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"net/url"
1111
"path"
12+
"slices"
1213
"strings"
1314
"text/template"
1415

@@ -139,21 +140,17 @@ func generateRegistryCACertFiles(
139140

140141
var files []cabpkv1.File //nolint:prealloc // We don't know the size of the slice yet.
141142

142-
for _, config := range configs {
143-
if config.CASecretName == "" {
144-
continue
145-
}
146-
147-
registryCACertPathOnRemote, err := config.filePathFromURL()
148-
if err != nil {
149-
return nil, fmt.Errorf("failed generating CA certificate file path from URL: %w", err)
150-
}
143+
filesToGenerate, err := registryCACertFiles(configs)
144+
if err != nil {
145+
return nil, err
146+
}
147+
for _, file := range filesToGenerate {
151148
files = append(files, cabpkv1.File{
152-
Path: registryCACertPathOnRemote,
149+
Path: file.path,
153150
Permissions: "0600",
154151
ContentFrom: &cabpkv1.FileSource{
155152
Secret: cabpkv1.SecretFileSource{
156-
Name: config.CASecretName,
153+
Name: file.caSecretName,
157154
Key: secretKeyForCACert,
158155
},
159156
},
@@ -189,3 +186,52 @@ func formatURLForContainerd(uri string) (string, error) {
189186
// using path.Join on all elements incorrectly drops a "/" from "https://"
190187
return fmt.Sprintf("%s/%s", mirror, mirrorPath), nil
191188
}
189+
190+
type containerdConfigFile struct {
191+
path string
192+
url string
193+
caSecretName string
194+
caCert string
195+
}
196+
197+
// registryCACertFiles returns a list of CA certificate files
198+
// that should be generated for the given containerd configurations.
199+
// If any of the provided configurations share the same url.Host only a single file will be generated.
200+
// An error will be returned, if the CA certificate content for the same URL.Host do not match.
201+
func registryCACertFiles(configs []containerdConfig) ([]containerdConfigFile, error) {
202+
filesToGenerate := make([]containerdConfigFile, 0)
203+
for _, config := range configs {
204+
// Skip if CA certificate is not provided.
205+
if config.CASecretName == "" {
206+
continue
207+
}
208+
registryCACertPathOnRemote, err := config.filePathFromURL()
209+
if err != nil {
210+
return nil, fmt.Errorf("failed generating CA certificate file path from URL: %w", err)
211+
}
212+
213+
foundIndex := slices.IndexFunc(filesToGenerate, func(f containerdConfigFile) bool {
214+
return registryCACertPathOnRemote == f.path
215+
})
216+
// File not already found and needs to be generated.
217+
if foundIndex == -1 {
218+
filesToGenerate = append(filesToGenerate, containerdConfigFile{
219+
path: registryCACertPathOnRemote,
220+
url: config.URL,
221+
caSecretName: config.CASecretName,
222+
caCert: config.CACert,
223+
})
224+
continue
225+
}
226+
// File is already in the list, check if the CA certificate content matches.
227+
if config.CACert != filesToGenerate[foundIndex].caCert {
228+
return nil, fmt.Errorf(
229+
"CA certificate content for %q does not match one for %q",
230+
config.URL,
231+
filesToGenerate[foundIndex].url,
232+
)
233+
}
234+
}
235+
236+
return filesToGenerate, nil
237+
}

pkg/handlers/generic/mutation/mirrors/containerd_files_test.go

Lines changed: 62 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
package mirrors
55

66
import (
7+
"fmt"
78
"testing"
89

910
"github.com/stretchr/testify/assert"
@@ -156,6 +157,7 @@ func Test_generateRegistryCACertFiles(t *testing.T) {
156157
name string
157158
configs []containerdConfig
158159
want []cabpkv1.File
160+
wantErr error
159161
}{
160162
{
161163
name: "ECR mirror image registry with no CA certificate",
@@ -173,6 +175,39 @@ func Test_generateRegistryCACertFiles(t *testing.T) {
173175
{
174176
URL: "https://registry.example.com",
175177
CASecretName: "my-registry-credentials-secret",
178+
CACert: "-----BEGIN CERTIFICATE-----",
179+
Mirror: true,
180+
},
181+
},
182+
want: []cabpkv1.File{
183+
{
184+
Path: "/etc/containerd/certs.d/registry.example.com/ca.crt",
185+
Owner: "",
186+
Permissions: "0600",
187+
Encoding: "",
188+
Append: false,
189+
ContentFrom: &cabpkv1.FileSource{
190+
Secret: cabpkv1.SecretFileSource{
191+
Name: "my-registry-credentials-secret",
192+
Key: "ca.crt",
193+
},
194+
},
195+
},
196+
},
197+
},
198+
{
199+
name: "Mirror image registry and registry with the same CA certificate",
200+
configs: []containerdConfig{
201+
{
202+
URL: "https://registry.example.com/mirror",
203+
CASecretName: "my-registry-credentials-secret",
204+
CACert: "-----BEGIN CERTIFICATE-----",
205+
Mirror: true,
206+
},
207+
{
208+
URL: "https://registry.example.com/library",
209+
CASecretName: "my-registry-credentials-secret",
210+
CACert: "-----BEGIN CERTIFICATE-----",
176211
Mirror: true,
177212
},
178213
},
@@ -192,13 +227,39 @@ func Test_generateRegistryCACertFiles(t *testing.T) {
192227
},
193228
},
194229
},
230+
{
231+
name: "Mirror image registry and registry with different CA certificates",
232+
configs: []containerdConfig{
233+
{
234+
URL: "https://registry.example.com/mirror",
235+
CASecretName: "my-registry-credentials-secret",
236+
CACert: "-----BEGIN CERTIFICATE-----",
237+
Mirror: true,
238+
},
239+
{
240+
URL: "https://registry.example.com/library",
241+
CASecretName: "my-registry-credentials-secret",
242+
CACert: "-----BEGIN CERTIFICATE----------END CERTIFICATE-----",
243+
Mirror: true,
244+
},
245+
},
246+
wantErr: fmt.Errorf(
247+
"CA certificate content for \"https://registry.example.com/library\" " +
248+
"does not match one for \"https://registry.example.com/mirror\"",
249+
),
250+
},
195251
}
196252
for idx := range tests {
197253
tt := tests[idx]
198254
t.Run(tt.name, func(t *testing.T) {
199255
t.Parallel()
200256
file, err := generateRegistryCACertFiles(tt.configs)
201-
require.NoError(t, err)
257+
if tt.wantErr != nil {
258+
assert.Equal(t, tt.wantErr.Error(), err.Error())
259+
return
260+
} else {
261+
require.NoError(t, err)
262+
}
202263
assert.Equal(t, tt.want, file)
203264
})
204265
}

0 commit comments

Comments
 (0)