Skip to content

Commit bcfaf65

Browse files
committed
Support for registry (insecure / blocked) from registries.conf
Signed-off-by: Valentin Delaye <jonesbusy@users.noreply.github.com>
1 parent 5e0eb8a commit bcfaf65

13 files changed

Lines changed: 566 additions & 39 deletions

src/main/java/land/oras/ContainerRef.java

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,62 @@ public ContainerRef forRegistry(String registry) {
411411
return new ContainerRef(registry, false, namespace, repository, tag, digest);
412412
}
413413

414+
/**
415+
* Check if access to this container reference is insecure by the registry configuration
416+
* @param registry The registry
417+
* @return True if access to this container reference is insecure, false otherwise
418+
*/
419+
public boolean isInsecure(Registry registry) {
420+
String effectiveRegistry = getEffectiveRegistry(registry);
421+
if (registry.getRegistriesConf().isInsecure(effectiveRegistry)) {
422+
LOG.info(
423+
"Access to container reference {} is insecure by location configuration for registry {}",
424+
this,
425+
effectiveRegistry);
426+
return true;
427+
}
428+
return registry.isInsecure();
429+
}
430+
431+
/**
432+
* Check if access to this container reference is blocked by the registry configuration
433+
* @param registry The registry
434+
* @return True if access to this container reference is blocked, false otherwise
435+
*/
436+
public boolean isBlocked(Registry registry) {
437+
boolean blocked = false;
438+
String effectiveRegistry = getEffectiveRegistry(registry);
439+
if (registry.getRegistriesConf().isBlocked(effectiveRegistry)) {
440+
LOG.info(
441+
"Access to container reference {} is blocked by location configuration for registry {}",
442+
this,
443+
effectiveRegistry);
444+
blocked = true;
445+
}
446+
String location = "%s/%s".formatted(effectiveRegistry, getFullRepository(registry));
447+
if (registry.getRegistriesConf().isBlocked(location)) {
448+
LOG.info(
449+
"Access to container reference {} is blocked by location configuration for registry/repository {}",
450+
this,
451+
location);
452+
blocked = true;
453+
}
454+
return blocked;
455+
}
456+
457+
/**
458+
* Check if access to this container reference is blocked by the registry configuration and throw exception if it is
459+
* @param registry The registry
460+
* @throws OrasException if access to this container reference is blocked by the registry configuration
461+
*/
462+
ContainerRef checkBlocked(Registry registry) throws OrasException {
463+
if (isBlocked(registry)) {
464+
throw new OrasException(
465+
"Access to container reference %s is blocked by registry configuration".formatted(this));
466+
}
467+
return this;
468+
}
469+
414470
/**
415471
* Return a copy of reference for a registry other registry
416472
* @param registry The registry
@@ -459,7 +515,7 @@ private String determineFirstUnqualifiedSearchRegistry(Registry registry) {
459515
registry.getRegistriesConf().getUnqualifiedRegistries());
460516
List<String> unqualifiedRegistries = registry.getRegistriesConf().getUnqualifiedRegistries();
461517
for (String searchRegistry : unqualifiedRegistries) {
462-
Registry targetRegistry = registry.copy(registry, searchRegistry);
518+
Registry targetRegistry = registry.copy(searchRegistry);
463519
LOG.debug("Checking if container {} exists in unqualified search registry {}", this, searchRegistry);
464520
if (targetRegistry.exists(this)) {
465521
LOG.debug("Found container {} in unqualified search registry {}", this, searchRegistry);

src/main/java/land/oras/Registry.java

Lines changed: 95 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,14 @@ private void setInsecure(boolean insecure) {
115115
this.insecure = insecure;
116116
}
117117

118+
/**
119+
* Return if this registry is insecure
120+
* @return True if insecure
121+
*/
122+
public boolean isInsecure() {
123+
return insecure;
124+
}
125+
118126
/**
119127
* Return this registry with the auth provider
120128
* @param authProvider The auth provider
@@ -158,12 +166,19 @@ public String getScheme() {
158166

159167
/**
160168
* Return a new registry with the given registry URL but with same settings
161-
* @param existing The registry
162169
* @param newRegistry The new target registry URL to use in the new registry
163170
* @return The new registry
164171
*/
165-
public Registry copy(Registry existing, String newRegistry) {
166-
return new Builder().from(existing).withRegistry(newRegistry).build();
172+
public Registry copy(String newRegistry) {
173+
return new Builder().from(this).withRegistry(newRegistry).build();
174+
}
175+
176+
/**
177+
* Return a new registry as insecure but with same settings
178+
* @return The new registry
179+
*/
180+
public Registry asInsecure() {
181+
return new Builder().from(this).withInsecure(true).build();
167182
}
168183

169184
/**
@@ -176,7 +191,10 @@ public Registry copy(Registry existing, String newRegistry) {
176191

177192
@Override
178193
public Tags getTags(ContainerRef containerRef) {
179-
ContainerRef ref = containerRef.forRegistry(this);
194+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
195+
if (ref.isInsecure(this) && !this.isInsecure()) {
196+
return asInsecure().getTags(containerRef);
197+
}
180198
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getTagsPath(this)));
181199
HttpClient.ResponseWrapper<String> response = client.get(
182200
uri, Map.of(Const.ACCEPT_HEADER, Const.DEFAULT_JSON_MEDIA_TYPE), Scopes.of(this, ref), authProvider);
@@ -186,13 +204,13 @@ public Tags getTags(ContainerRef containerRef) {
186204

187205
@Override
188206
public Repositories getRepositories() {
189-
ContainerRef containerRef = ContainerRef.parse("default").forRegistry(this);
190-
URI uri = URI.create("%s://%s".formatted(getScheme(), containerRef.getRepositoriesPath(this)));
207+
if (registry != null && getRegistriesConf().isInsecure(registry) && !this.isInsecure()) {
208+
return asInsecure().getRepositories();
209+
}
210+
ContainerRef ref = ContainerRef.parse("default").forRegistry(this);
211+
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getRepositoriesPath(this)));
191212
HttpClient.ResponseWrapper<String> response = client.get(
192-
uri,
193-
Map.of(Const.ACCEPT_HEADER, Const.DEFAULT_JSON_MEDIA_TYPE),
194-
Scopes.of(this, containerRef),
195-
authProvider);
213+
uri, Map.of(Const.ACCEPT_HEADER, Const.DEFAULT_JSON_MEDIA_TYPE), Scopes.of(this, ref), authProvider);
196214
handleError(response);
197215
return JsonUtils.fromJson(response.response(), Repositories.class);
198216
}
@@ -202,7 +220,10 @@ public Referrers getReferrers(ContainerRef containerRef, @Nullable ArtifactType
202220
if (containerRef.getDigest() == null) {
203221
throw new OrasException("Digest is required to get referrers");
204222
}
205-
ContainerRef ref = containerRef.forRegistry(this);
223+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
224+
if (ref.isInsecure(this) && !this.isInsecure()) {
225+
return asInsecure().getReferrers(containerRef, artifactType);
226+
}
206227
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getReferrersPath(this, artifactType)));
207228
HttpClient.ResponseWrapper<String> response = client.get(
208229
uri, Map.of(Const.ACCEPT_HEADER, Const.DEFAULT_INDEX_MEDIA_TYPE), Scopes.of(this, ref), authProvider);
@@ -215,7 +236,11 @@ public Referrers getReferrers(ContainerRef containerRef, @Nullable ArtifactType
215236
* @param containerRef The artifact
216237
*/
217238
public void deleteManifest(ContainerRef containerRef) {
218-
ContainerRef ref = containerRef.forRegistry(this);
239+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
240+
if (ref.isInsecure(this) && !this.isInsecure()) {
241+
asInsecure().deleteManifest(containerRef);
242+
return;
243+
}
219244
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
220245
HttpClient.ResponseWrapper<String> response = client.delete(uri, Map.of(), Scopes.of(this, ref), authProvider);
221246
logResponse(response);
@@ -231,7 +256,10 @@ public Manifest pushManifest(ContainerRef containerRef, Manifest manifest) {
231256
manifestAnnotations.put(Const.ANNOTATION_CREATED, Const.currentTimestamp());
232257
manifest = manifest.withAnnotations(manifestAnnotations);
233258
}
234-
ContainerRef ref = containerRef.forRegistry(this);
259+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
260+
if (ref.isInsecure(this) && !this.isInsecure()) {
261+
return asInsecure().pushManifest(containerRef, manifest);
262+
}
235263
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
236264
byte[] manifestData = manifest.getJson() != null
237265
? manifest.getJson().getBytes()
@@ -257,7 +285,10 @@ public Manifest pushManifest(ContainerRef containerRef, Manifest manifest) {
257285

258286
@Override
259287
public Index pushIndex(ContainerRef containerRef, Index index) {
260-
ContainerRef ref = containerRef.forRegistry(this);
288+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
289+
if (ref.isInsecure(this) && !this.isInsecure()) {
290+
return asInsecure().pushIndex(containerRef, index);
291+
}
261292
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
262293
byte[] indexData = JsonUtils.toJson(index).getBytes();
263294
LOG.debug("Index data to push: {}", new String(indexData, StandardCharsets.UTF_8));
@@ -277,7 +308,11 @@ public Index pushIndex(ContainerRef containerRef, Index index) {
277308
* @param containerRef The container
278309
*/
279310
public void deleteBlob(ContainerRef containerRef) {
280-
ContainerRef ref = containerRef.forRegistry(this);
311+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
312+
if (ref.isInsecure(this) && !this.isInsecure()) {
313+
asInsecure().deleteBlob(containerRef);
314+
return;
315+
}
281316
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getBlobsPath(this)));
282317
HttpClient.ResponseWrapper<String> response = client.delete(uri, Map.of(), Scopes.of(this, ref), authProvider);
283318
logResponse(response);
@@ -286,7 +321,11 @@ public void deleteBlob(ContainerRef containerRef) {
286321

287322
@Override
288323
public void pullArtifact(ContainerRef containerRef, Path path, boolean overwrite) {
289-
ContainerRef ref = containerRef.forRegistry(this);
324+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
325+
if (ref.isInsecure(this) && !this.isInsecure()) {
326+
asInsecure().pullArtifact(containerRef, path, overwrite);
327+
return;
328+
}
290329
// Only collect layer that are files
291330
String contentType = getContentType(ref);
292331
List<Layer> layers = collectLayers(ref, contentType, false);
@@ -371,7 +410,10 @@ public Manifest pushArtifact(
371410
public Layer pushBlob(ContainerRef containerRef, Path blob, Map<String, String> annotations) {
372411
String digest = containerRef.getAlgorithm().digest(blob);
373412
LOG.debug("Digest: {}", digest);
374-
ContainerRef ref = containerRef.forRegistry(this);
413+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
414+
if (ref.isInsecure(this) && !this.isInsecure()) {
415+
return asInsecure().pushBlob(containerRef, blob, annotations);
416+
}
375417
// This might not works with registries performing HEAD request
376418
if (hasBlob(ref.withDigest(digest))) {
377419
LOG.info("Blob already exists: {}", digest);
@@ -426,7 +468,10 @@ public Layer pushBlob(ContainerRef containerRef, Path blob, Map<String, String>
426468
@Override
427469
public Layer pushBlob(ContainerRef containerRef, byte[] data) {
428470
String digest = containerRef.getAlgorithm().digest(data);
429-
ContainerRef ref = containerRef.forRegistry(this);
471+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
472+
if (ref.isInsecure(this) && !this.isInsecure()) {
473+
return asInsecure().pushBlob(containerRef, data);
474+
}
430475
if (ref.getDigest() != null) {
431476
ensureDigest(ref, data);
432477
}
@@ -489,7 +534,10 @@ private boolean hasBlob(ContainerRef containerRef) {
489534
}
490535

491536
private HttpClient.ResponseWrapper<String> headBlob(ContainerRef containerRef) {
492-
ContainerRef ref = containerRef.forRegistry(this);
537+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
538+
if (ref.isInsecure(this) && !this.isInsecure()) {
539+
return asInsecure().headBlob(containerRef);
540+
}
493541
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getBlobsPath(this)));
494542
HttpClient.ResponseWrapper<String> response = client.head(
495543
uri,
@@ -507,7 +555,10 @@ private HttpClient.ResponseWrapper<String> headBlob(ContainerRef containerRef) {
507555
*/
508556
@Override
509557
public byte[] getBlob(ContainerRef containerRef) {
510-
ContainerRef ref = containerRef.forRegistry(this);
558+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
559+
if (ref.isInsecure(this) && !this.isInsecure()) {
560+
return asInsecure().getBlob(containerRef);
561+
}
511562
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getBlobsPath(this)));
512563
HttpClient.ResponseWrapper<String> response = client.get(
513564
uri,
@@ -523,7 +574,11 @@ public byte[] getBlob(ContainerRef containerRef) {
523574

524575
@Override
525576
public void fetchBlob(ContainerRef containerRef, Path path) {
526-
ContainerRef ref = containerRef.forRegistry(this);
577+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
578+
if (ref.isInsecure(this) && !this.isInsecure()) {
579+
asInsecure().fetchBlob(containerRef, path);
580+
return;
581+
}
527582
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getBlobsPath(this)));
528583
HttpClient.ResponseWrapper<Path> response = client.download(
529584
uri,
@@ -538,7 +593,10 @@ public void fetchBlob(ContainerRef containerRef, Path path) {
538593

539594
@Override
540595
public InputStream fetchBlob(ContainerRef containerRef) {
541-
ContainerRef ref = containerRef.forRegistry(this);
596+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
597+
if (ref.isInsecure(this) && !this.isInsecure()) {
598+
return asInsecure().fetchBlob(containerRef);
599+
}
542600
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getBlobsPath(this)));
543601
HttpClient.ResponseWrapper<InputStream> response = client.download(
544602
uri,
@@ -639,7 +697,10 @@ public Descriptor probeDescriptor(ContainerRef ref) {
639697
* @return True if exists
640698
*/
641699
boolean exists(ContainerRef containerRef) {
642-
ContainerRef ref = containerRef.forRegistry(this);
700+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
701+
if (ref.isInsecure(this) && !this.isInsecure()) {
702+
return asInsecure().exists(containerRef);
703+
}
643704
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
644705
HttpClient.ResponseWrapper<String> response = client.head(
645706
uri, Map.of(Const.ACCEPT_HEADER, Const.MANIFEST_ACCEPT_TYPE), Scopes.of(this, ref), authProvider);
@@ -653,7 +714,10 @@ boolean exists(ContainerRef containerRef) {
653714
* @return The response
654715
*/
655716
private HttpClient.ResponseWrapper<String> getManifestResponse(ContainerRef containerRef) {
656-
ContainerRef ref = containerRef.forRegistry(this);
717+
ContainerRef ref = containerRef.forRegistry(this).checkBlocked(this);
718+
if (ref.isInsecure(this) && !this.isInsecure()) {
719+
return asInsecure().getManifestResponse(containerRef);
720+
}
657721
URI uri = URI.create("%s://%s".formatted(getScheme(), ref.getManifestsPath(this)));
658722
HttpClient.ResponseWrapper<String> response = client.head(
659723
uri, Map.of(Const.ACCEPT_HEADER, Const.MANIFEST_ACCEPT_TYPE), Scopes.of(this, ref), authProvider);
@@ -799,17 +863,17 @@ URI createLocationWithDigest(String location, String digest) {
799863

800864
/**
801865
* Execute a head request on the manifest URL and return the headers
802-
* @param containerRef The container
866+
* @param ref The container
803867
* @return The headers
804868
*/
805-
Map<String, String> getHeaders(ContainerRef containerRef) {
869+
Map<String, String> getHeaders(ContainerRef ref) {
870+
if (ref.isInsecure(this) && !this.isInsecure()) {
871+
return asInsecure().getHeaders(ref);
872+
}
806873
URI uri = URI.create(
807-
"%s://%s".formatted(getScheme(), containerRef.forRegistry(this).getManifestsPath(this)));
874+
"%s://%s".formatted(getScheme(), ref.forRegistry(this).getManifestsPath(this)));
808875
HttpClient.ResponseWrapper<String> response = client.head(
809-
uri,
810-
Map.of(Const.ACCEPT_HEADER, Const.MANIFEST_ACCEPT_TYPE),
811-
Scopes.of(this, containerRef),
812-
authProvider);
876+
uri, Map.of(Const.ACCEPT_HEADER, Const.MANIFEST_ACCEPT_TYPE), Scopes.of(this, ref), authProvider);
813877
logResponse(response);
814878
handleError(response);
815879
return response.headers();

0 commit comments

Comments
 (0)