Skip to content

Commit

Permalink
Allows only HTTPS unless 'allowHttp' is true. (#437)
Browse files Browse the repository at this point in the history
* Allows only HTTPS unless 'allowHttp' is true.

* Adds RegistryClient.Factory.
  • Loading branch information
coollog authored Jun 26, 2018
1 parent ec98737 commit 3a8ee13
Show file tree
Hide file tree
Showing 23 changed files with 338 additions and 80 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,18 @@
package com.google.cloud.tools.jib.builder;

import com.google.cloud.tools.jib.Command;
import com.google.cloud.tools.jib.cache.CacheDirectoryCreationException;
import com.google.cloud.tools.jib.cache.CacheDirectoryNotOwnedException;
import com.google.cloud.tools.jib.cache.CacheMetadataCorruptedException;
import com.google.cloud.tools.jib.cache.Caches;
import com.google.cloud.tools.jib.image.ImageReference;
import com.google.cloud.tools.jib.registry.LocalRegistry;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.concurrent.ExecutionException;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.ClassRule;
Expand All @@ -40,7 +46,9 @@ public class BuildStepsIntegrationTest {
@Rule public TemporaryFolder temporaryCacheDirectory = new TemporaryFolder();

@Test
public void testSteps_forBuildToDockerRegistry() throws Exception {
public void testSteps_forBuildToDockerRegistry()
throws IOException, URISyntaxException, InterruptedException, CacheMetadataCorruptedException,
ExecutionException, CacheDirectoryNotOwnedException, CacheDirectoryCreationException {
SourceFilesConfiguration sourceFilesConfiguration = new TestSourceFilesConfiguration();
BuildConfiguration buildConfiguration =
BuildConfiguration.builder(logger)
Expand All @@ -49,6 +57,7 @@ public void testSteps_forBuildToDockerRegistry() throws Exception {
.setMainClass("HelloWorld")
.setJavaArguments(Collections.singletonList("An argument."))
.setExposedPorts(Arrays.asList("1000", "2000-2002/tcp", "3000/udp"))
.setAllowHttp(true)
.build();

Path cacheDirectory = temporaryCacheDirectory.newFolder().toPath();
Expand Down Expand Up @@ -81,7 +90,9 @@ public void testSteps_forBuildToDockerRegistry() throws Exception {
}

@Test
public void testSteps_forBuildToDockerDaemon() throws Exception {
public void testSteps_forBuildToDockerDaemon()
throws IOException, URISyntaxException, InterruptedException, CacheMetadataCorruptedException,
ExecutionException, CacheDirectoryNotOwnedException, CacheDirectoryCreationException {
SourceFilesConfiguration sourceFilesConfiguration = new TestSourceFilesConfiguration();
BuildConfiguration buildConfiguration =
BuildConfiguration.builder(logger)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@ public class AuthenticationMethodRetrieverIntegrationTest {
public void testGetRegistryAuthenticator()
throws RegistryAuthenticationFailedException, IOException, RegistryException {
RegistryClient registryClient =
new RegistryClient(null, "registry.hub.docker.com", "library/busybox");
RegistryClient.factory("registry.hub.docker.com", "library/busybox")
.newWithAuthorization(null);
RegistryAuthenticator registryAuthenticator = registryClient.getRegistryAuthenticator();
Authorization authorization = registryAuthenticator.authenticatePull();

RegistryClient authorizedRegistryClient =
new RegistryClient(authorization, "registry.hub.docker.com", "library/busybox");
RegistryClient.factory("registry.hub.docker.com", "library/busybox")
.newWithAuthorization(authorization);
authorizedRegistryClient.pullManifest("latest");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ public class BlobCheckerIntegrationTest {

@Test
public void testCheck_exists() throws IOException, RegistryException {
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "busybox");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
V22ManifestTemplate manifestTemplate =
registryClient.pullManifest("latest", V22ManifestTemplate.class);
DescriptorDigest blobDigest = manifestTemplate.getLayers().get(0).getDigest();
Expand All @@ -41,7 +42,8 @@ public void testCheck_exists() throws IOException, RegistryException {

@Test
public void testCheck_doesNotExist() throws IOException, RegistryException, DigestException {
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "busybox");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
DescriptorDigest fakeBlobDigest =
DescriptorDigest.fromHash(
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public class BlobPullerIntegrationTest {
@Test
public void testPull() throws IOException, RegistryException {
// Pulls the busybox image.
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "busybox");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
V21ManifestTemplate manifestTemplate =
registryClient.pullManifest("latest", V21ManifestTemplate.class);

Expand All @@ -62,7 +63,8 @@ public void testPull_unknownBlob() throws RegistryException, IOException, Digest
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa");

try {
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "busybox");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
registryClient.pullBlob(nonexistentDigest, Mockito.mock(OutputStream.class));
Assert.fail("Trying to pull nonexistent blob should have errored");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public void testPush() throws DigestException, IOException, RegistryException {
DescriptorDigest.fromHash(
"52a9e4d4ba4333ce593707f98564fee1e6d898db0d3602408c0b2a6a424d357c");

RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "testimage");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "testimage").newAllowHttp();
Assert.assertFalse(registryClient.pushBlob(testBlobDigest, testBlob));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ public class ManifestPullerIntegrationTest {

@Test
public void testPull_v21() throws IOException, RegistryException {
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "busybox");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
V21ManifestTemplate manifestTemplate =
registryClient.pullManifest("latest", V21ManifestTemplate.class);

Expand All @@ -42,7 +43,8 @@ public void testPull_v21() throws IOException, RegistryException {

@Test
public void testPull_v22() throws IOException, RegistryException {
RegistryClient registryClient = new RegistryClient(null, "gcr.io", "distroless/java");
RegistryClient registryClient =
RegistryClient.factory("gcr.io", "distroless/java").newWithAuthorization(null);
ManifestTemplate manifestTemplate = registryClient.pullManifest("latest");

Assert.assertEquals(2, manifestTemplate.getSchemaVersion());
Expand All @@ -53,7 +55,8 @@ public void testPull_v22() throws IOException, RegistryException {
@Test
public void testPull_unknownManifest() throws RegistryException, IOException {
try {
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "busybox");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
registryClient.pullManifest("nonexistent-tag");
Assert.fail("Trying to pull nonexistent image should have errored");

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,11 @@ public class ManifestPusherIntegrationTest {

@Test
public void testPush_missingBlobs() throws IOException, RegistryException {
RegistryClient registryClient = new RegistryClient(null, "gcr.io", "distroless/java");
RegistryClient registryClient =
RegistryClient.factory("gcr.io", "distroless/java").newWithAuthorization(null);
ManifestTemplate manifestTemplate = registryClient.pullManifest("latest");

registryClient = new RegistryClient(null, "localhost:5000", "busybox");
registryClient = RegistryClient.factory("localhost:5000", "busybox").newAllowHttp();
try {
registryClient.pushManifest((V22ManifestTemplate) manifestTemplate, "latest");
Assert.fail("Pushing manifest without its BLOBs should fail");
Expand Down Expand Up @@ -70,7 +71,8 @@ public void testPush() throws DigestException, IOException, RegistryException {
expectedManifestTemplate.setContainerConfiguration(5, testContainerConfigurationBlobDigest);

// Pushes the BLOBs.
RegistryClient registryClient = new RegistryClient(null, "localhost:5000", "testimage");
RegistryClient registryClient =
RegistryClient.factory("localhost:5000", "testimage").newAllowHttp();
Assert.assertFalse(registryClient.pushBlob(testLayerBlobDigest, testLayerBlob));
Assert.assertFalse(
registryClient.pushBlob(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ public static class Builder {
private Class<? extends BuildableManifestTemplate> targetFormat = V22ManifestTemplate.class;
@Nullable private CacheConfiguration applicationLayersCacheConfiguration;
@Nullable private CacheConfiguration baseImageLayersCacheConfiguration;
private boolean allowHttp = false;

private BuildLogger buildLogger;

Expand Down Expand Up @@ -163,6 +164,17 @@ public Builder setBaseImageLayersCacheConfiguration(
return this;
}

/**
* Sets whether or not to allow communication over HTTP (as opposed to HTTPS).
*
* @param allowHttp if {@code true}, insecure connections will be allowed
* @return this
*/
public Builder setAllowHttp(boolean allowHttp) {
this.allowHttp = allowHttp;
return this;
}

/** @return the corresponding build configuration */
public BuildConfiguration build() {
// Validates the parameters.
Expand Down Expand Up @@ -203,7 +215,8 @@ public BuildConfiguration build() {
expandPortRanges(exposedPorts),
targetFormat,
applicationLayersCacheConfiguration,
baseImageLayersCacheConfiguration);
baseImageLayersCacheConfiguration,
allowHttp);

case 1:
throw new IllegalStateException(errorMessages.get(0));
Expand Down Expand Up @@ -318,6 +331,7 @@ public static Builder builder(BuildLogger buildLogger) {
private final Class<? extends BuildableManifestTemplate> targetFormat;
@Nullable private final CacheConfiguration applicationLayersCacheConfiguration;
@Nullable private final CacheConfiguration baseImageLayersCacheConfiguration;
private final boolean allowHttp;

/** Instantiate with {@link Builder#build}. */
private BuildConfiguration(
Expand All @@ -335,7 +349,8 @@ private BuildConfiguration(
ImmutableList<String> exposedPorts,
Class<? extends BuildableManifestTemplate> targetFormat,
@Nullable CacheConfiguration applicationLayersCacheConfiguration,
@Nullable CacheConfiguration baseImageLayersCacheConfiguration) {
@Nullable CacheConfiguration baseImageLayersCacheConfiguration,
boolean allowHttp) {
this.buildLogger = buildLogger;
this.baseImageReference = baseImageReference;
this.baseImageCredentialHelperName = baseImageCredentialHelperName;
Expand All @@ -351,6 +366,7 @@ private BuildConfiguration(
this.targetFormat = targetFormat;
this.applicationLayersCacheConfiguration = applicationLayersCacheConfiguration;
this.baseImageLayersCacheConfiguration = baseImageLayersCacheConfiguration;
this.allowHttp = allowHttp;
}

public BuildLogger getBuildLogger() {
Expand Down Expand Up @@ -444,12 +460,21 @@ public CacheConfiguration getApplicationLayersCacheConfiguration() {
}

/**
* Sets the location of the cache for storing base image layers.
* Gets the location of the cache for storing base image layers.
*
* @return the base image layers {@link CacheConfiguration}, or {@code null} if not set
*/
@Nullable
public CacheConfiguration getBaseImageLayersCacheConfiguration() {
return baseImageLayersCacheConfiguration;
}

/**
* Gets whether or not to allow communication over HTTP (as opposed to HTTPS).
*
* @return {@code true} if insecure connections will be allowed; {@code false} otherwise
*/
public boolean getAllowHttp() {
return allowHttp;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ public Authorization call()
String.format(DESCRIPTION, buildConfiguration.getTargetImageRegistry()))) {
Authorization registryCredentials =
NonBlockingSteps.get(retrieveTargetRegistryCredentialsStep);

RegistryAuthenticator registryAuthenticator =
RegistryAuthenticators.forOther(
buildConfiguration.getTargetImageRegistry(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,11 +69,14 @@ public ListenableFuture<CachedLayer> getFuture() {
public CachedLayer call() throws IOException, RegistryException {
try (Timer ignored =
new Timer(buildConfiguration.getBuildLogger(), String.format(DESCRIPTION, layerDigest))) {
RegistryClient registryClient =
new RegistryClient(
pullAuthorization,
RegistryClient.Factory registryClientFactory =
RegistryClient.factory(
buildConfiguration.getBaseImageRegistry(),
buildConfiguration.getBaseImageRepository());
RegistryClient registryClient =
buildConfiguration.getAllowHttp()
? registryClientFactory.newAllowHttp()
: registryClientFactory.newWithAuthorization(pullAuthorization);

// Checks if the layer already exists in the cache.
CachedLayer cachedLayer = new CacheReader(cache).getLayer(layerDigest);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,11 +134,13 @@ public BaseImageWithAuthorization call()
private Image<Layer> pullBaseImage(@Nullable Authorization registryCredentials)
throws IOException, RegistryException, LayerPropertyNotFoundException,
LayerCountMismatchException {
RegistryClient.Factory registryClientFactory =
RegistryClient.factory(
buildConfiguration.getBaseImageRegistry(), buildConfiguration.getBaseImageRepository());
RegistryClient registryClient =
new RegistryClient(
registryCredentials,
buildConfiguration.getBaseImageRegistry(),
buildConfiguration.getBaseImageRepository());
buildConfiguration.getAllowHttp()
? registryClientFactory.newAllowHttp()
: registryClientFactory.newWithAuthorization(registryCredentials);

ManifestTemplate manifestTemplate =
registryClient.pullManifest(buildConfiguration.getBaseImageTag());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,16 @@ public ListenableFuture<BlobDescriptor> getFuture() {
public BlobDescriptor call() throws IOException, RegistryException, ExecutionException {
try (Timer timer =
new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION + blobDescriptor)) {
RegistryClient.Factory registryClientFactory =
RegistryClient.factory(
buildConfiguration.getTargetImageRegistry(),
buildConfiguration.getTargetImageRepository());
RegistryClient registryClient =
new RegistryClient(
NonBlockingSteps.get(authenticatePushStep),
buildConfiguration.getTargetImageRegistry(),
buildConfiguration.getTargetImageRepository())
.setTimer(timer);
buildConfiguration.getAllowHttp()
? registryClientFactory.newAllowHttp()
: registryClientFactory.newWithAuthorization(
NonBlockingSteps.get(authenticatePushStep));
registryClient.setTimer(timer);

if (registryClient.checkBlob(blobDescriptor.getDigest()) != null) {
buildConfiguration
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,11 +116,15 @@ private ListenableFuture<Void> afterPushSteps() throws ExecutionException {

private Void afterAllPushed() throws IOException, RegistryException, ExecutionException {
try (Timer ignored = new Timer(buildConfiguration.getBuildLogger(), DESCRIPTION)) {
RegistryClient registryClient =
new RegistryClient(
NonBlockingSteps.get(authenticatePushStep),
RegistryClient.Factory registryClientFactory =
RegistryClient.factory(
buildConfiguration.getTargetImageRegistry(),
buildConfiguration.getTargetImageRepository());
RegistryClient registryClient =
buildConfiguration.getAllowHttp()
? registryClientFactory.newAllowHttp()
: registryClientFactory.newWithAuthorization(
NonBlockingSteps.get(authenticatePushStep));

// Constructs the image.
ImageToJsonTranslator imageToJsonTranslator =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import com.google.cloud.tools.jib.cache.Caches;
import com.google.cloud.tools.jib.cache.Caches.Initializer;
import com.google.cloud.tools.jib.configuration.CacheConfiguration;
import com.google.cloud.tools.jib.registry.InsecureRegistryException;
import com.google.cloud.tools.jib.registry.RegistryAuthenticationFailedException;
import com.google.cloud.tools.jib.registry.RegistryUnauthorizedException;
import com.google.common.annotations.VisibleForTesting;
Expand Down Expand Up @@ -196,30 +197,36 @@ public void build(HelpfulSuggestions helpfulSuggestions) throws BuildStepsExecut
} catch (ExecutionException executionException) {
BuildConfiguration buildConfiguration = buildSteps.getBuildConfiguration();

if (executionException.getCause() instanceof HttpHostConnectException) {
Throwable exceptionDuringBuildSteps = executionException.getCause();

if (exceptionDuringBuildSteps instanceof HttpHostConnectException) {
// Failed to connect to registry.
throw new BuildStepsExecutionException(
helpfulSuggestions.forHttpHostConnect(), executionException.getCause());
helpfulSuggestions.forHttpHostConnect(), exceptionDuringBuildSteps);

} else if (executionException.getCause() instanceof RegistryUnauthorizedException) {
} else if (exceptionDuringBuildSteps instanceof RegistryUnauthorizedException) {
handleRegistryUnauthorizedException(
(RegistryUnauthorizedException) executionException.getCause(),
(RegistryUnauthorizedException) exceptionDuringBuildSteps,
buildConfiguration,
helpfulSuggestions);

} else if (executionException.getCause() instanceof RegistryAuthenticationFailedException
&& executionException.getCause().getCause() instanceof HttpResponseException) {
} else if (exceptionDuringBuildSteps instanceof RegistryAuthenticationFailedException
&& exceptionDuringBuildSteps.getCause() instanceof HttpResponseException) {
handleRegistryUnauthorizedException(
new RegistryUnauthorizedException(
buildConfiguration.getTargetImageRegistry(),
buildConfiguration.getTargetImageRepository(),
(HttpResponseException) executionException.getCause().getCause()),
(HttpResponseException) exceptionDuringBuildSteps.getCause()),
buildConfiguration,
helpfulSuggestions);

} else if (executionException.getCause() instanceof UnknownHostException) {
} else if (exceptionDuringBuildSteps instanceof UnknownHostException) {
throw new BuildStepsExecutionException(
helpfulSuggestions.forUnknownHost(), exceptionDuringBuildSteps);

} else if (exceptionDuringBuildSteps instanceof InsecureRegistryException) {
throw new BuildStepsExecutionException(
helpfulSuggestions.forUnknownHost(), executionException.getCause());
helpfulSuggestions.forInsecureRegistry(), exceptionDuringBuildSteps);

} else {
throw new BuildStepsExecutionException(
Expand Down
Loading

0 comments on commit 3a8ee13

Please sign in to comment.