Skip to content

Commit

Permalink
Refactoring of helm:upload and add tests for insecure (#310)
Browse files Browse the repository at this point in the history
  • Loading branch information
sschnabe authored Jul 31, 2023
1 parent 786320d commit 1833b0e
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 71 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ Parameter | Type | User Property | Required | Description
`<skipDependencyBuild>` | boolean | helm.dependency-build.skip | false | skip dependency-build goal
`<skipPackage>` | boolean | helm.package.skip | false | skip package goal
`<skipUpload>` | boolean | helm.upload.skip | false | skip upload goal
`<insecure>` | boolean | helm.upload.insecure | false | Skip tls certificate checks for the chart upload.
`<skipInstall>` | boolean | helm.install.skip | false | skip install goal
`<security>` | string | helm.security | false | path to your [settings-security.xml](https://maven.apache.org/guides/mini/guide-encryption.html) (default: `~/.m2/settings-security.xml`)
`<keyring>` | string | helm.package.keyring | false | path to gpg secret keyring for signing
Expand Down
122 changes: 53 additions & 69 deletions src/main/java/io/kokuwa/maven/helm/UploadMojo.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,8 @@
import java.security.cert.X509Certificate;
import java.util.Base64;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

Expand Down Expand Up @@ -126,61 +124,52 @@ private void uploadSingle(Path chart) throws MojoExecutionException, IOException
HelmRepository uploadRepo = getHelmUploadRepo();

HttpURLConnection connection;

if (uploadRepo.getType() == null) {
throw new IllegalArgumentException("Repository type missing. Check your plugin configuration.");
}

SSLSocketFactory dsf = HttpsURLConnection.getDefaultSSLSocketFactory();
HostnameVerifier dhv = HttpsURLConnection.getDefaultHostnameVerifier();
try {
if (insecure) {
setupInsecureTLS();
}
switch (uploadRepo.getType()) {
case ARTIFACTORY:
connection = getConnectionForUploadToArtifactory(fileToUpload, uploadRepo);
break;
case CHARTMUSEUM:
connection = getConnectionForUploadToChartMuseum();
break;
case NEXUS:
connection = getConnectionForUploadToNexus(fileToUpload);
break;
default:
throw new IllegalArgumentException("Unsupported repository type for upload.");
}

switch (uploadRepo.getType()) {
case ARTIFACTORY:
connection = getConnectionForUploadToArtifactory(fileToUpload, uploadRepo);
break;
case CHARTMUSEUM:
connection = getConnectionForUploadToChartMuseum();
break;
case NEXUS:
connection = getConnectionForUploadToNexus(fileToUpload);
break;
default:
throw new IllegalArgumentException("Unsupported repository type for upload.");
}
if (insecure && connection instanceof HttpsURLConnection) {
setupInsecureTLS((HttpsURLConnection) connection);
}

try (FileInputStream fileInputStream = new FileInputStream(fileToUpload)) {
IOUtils.copy(fileInputStream, connection.getOutputStream());
}
if (connection.getResponseCode() >= 300) {
String response;
if (connection.getErrorStream() != null) {
response = new String(IOUtils.toByteArray(connection.getErrorStream()));
} else if (connection.getInputStream() != null) {
response = new String(IOUtils.toByteArray(connection.getInputStream()));
} else {
response = "No details provided";
}
throw new MojoExecutionException("Failed to upload: " + response);
try (FileInputStream fileInputStream = new FileInputStream(fileToUpload)) {
IOUtils.copy(fileInputStream, connection.getOutputStream());
}
if (connection.getResponseCode() >= 300) {
String response;
if (connection.getErrorStream() != null) {
response = new String(IOUtils.toByteArray(connection.getErrorStream()));
} else if (connection.getInputStream() != null) {
response = new String(IOUtils.toByteArray(connection.getInputStream()));
} else {
String message = Integer.toString(connection.getResponseCode());
if (connection.getInputStream() != null) {
message += " - " + new String(IOUtils.toByteArray(connection.getInputStream()));
}
getLog().info(message);
}
connection.disconnect();
} finally {
if (dsf != null) {
HttpsURLConnection.setDefaultSSLSocketFactory(dsf);
response = "No details provided";
}
if (dhv != null) {
HttpsURLConnection.setDefaultHostnameVerifier(dhv);
throw new MojoExecutionException("Failed to upload: " + response);
} else {
String message = "Returned: " + connection.getResponseCode();
if (connection.getInputStream() != null) {
String details = new String(IOUtils.toByteArray(connection.getInputStream()));
if (!details.isEmpty()) {
message += " - " + details;
}
}
getLog().info(message);
}
connection.disconnect();
}

private HttpURLConnection getConnectionForUploadToChartMuseum() throws IOException, MojoExecutionException {
Expand Down Expand Up @@ -251,41 +240,36 @@ private HttpURLConnection getConnectionForUploadToNexus(File file) throws IOExce
return connection;
}

private void setupInsecureTLS() throws MojoExecutionException {
// Create a trust manager that does not validate certificate chains
TrustManager[] trustAllCerts = new TrustManager[] { new X509TrustManager() {
private void setupInsecureTLS(HttpsURLConnection connection) throws MojoExecutionException {

TrustManager trustManager = new X509TrustManager() {

@Override
public java.security.cert.X509Certificate[] getAcceptedIssuers() {
return null; //NOSONAR
return null;
}

public void checkClientTrusted(X509Certificate[] certs, String authType) { //NOSONAR
// no-op
}
@Override
public void checkClientTrusted(X509Certificate[] certs, String authType) {}

public void checkServerTrusted(X509Certificate[] certs, String authType) { //NOSONAR
// no-op
}
} };
@Override
public void checkServerTrusted(X509Certificate[] certs, String authType) {}
};

// Install the all-trusting trust manager
SSLContext sc;
try {
sc = SSLContext.getInstance("TLS");
} catch (NoSuchAlgorithmException e) {
throw new MojoExecutionException("Cannot create TLS context instance", e);
}
try {
sc.init(null, trustAllCerts, new java.security.SecureRandom());
sc.init(null, new TrustManager[] { trustManager }, new java.security.SecureRandom());
} catch (KeyManagementException e) {
throw new MojoExecutionException("Cannot initialize TLS context instance", e);
} catch (NoSuchAlgorithmException e) {
throw new MojoExecutionException("Cannot create TLS context instance", e);
}
HttpsURLConnection.setDefaultSSLSocketFactory(sc.getSocketFactory());

// Create all-trusting host name verifier
HostnameVerifier allHostsValid = (hostname, session) -> true; //NOSONAR
connection.setSSLSocketFactory(sc.getSocketFactory());
connection.setHostnameVerifier((hostname, session) -> true);

// Install the all-trusting host verifier
HttpsURLConnection.setDefaultHostnameVerifier(allHostsValid);
getLog().info("Use insecure TLS connection for [" + connection.getURL() + "]");
}

/**
Expand Down
35 changes: 33 additions & 2 deletions src/test/java/io/kokuwa/maven/helm/UploadMojoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,13 @@
import java.util.List;

import org.apache.hc.core5.http.HttpHeaders;
import org.apache.maven.plugin.MojoExecutionException;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import com.github.tomakehurst.wiremock.client.WireMock;
import com.github.tomakehurst.wiremock.core.WireMockConfiguration;
import com.github.tomakehurst.wiremock.http.RequestMethod;
import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
import com.github.tomakehurst.wiremock.matching.RequestPatternBuilder;
Expand All @@ -26,7 +28,10 @@
public class UploadMojoTest extends AbstractMojoTest {

@RegisterExtension
static WireMockExtension mock = WireMockExtension.newInstance().failOnUnmatchedRequests(true).build();
static WireMockExtension mock = WireMockExtension.newInstance()
.failOnUnmatchedRequests(true)
.options(WireMockConfiguration.wireMockConfig().dynamicPort().dynamicHttpsPort())
.build();

@DisplayName("no tar gz present")
@Test
Expand Down Expand Up @@ -96,6 +101,30 @@ void urlChartmuseumWithServerIdEncrypted(UploadMojo mojo) {
assertUpload(mojo, RequestMethod.POST, "/chartmuseum", BASIC_FOO_SECRET);
}

@DisplayName("nexus: tls fail because of selfsigned certificate")
@Test
void urlNexusTlsFail(UploadMojo mojo) {
mojo.setInsecure(false);
mojo.setUploadRepoStable(new HelmRepository()
.setType(RepoType.NEXUS)
.setName("my-nexus")
.setUrl("https://127.0.0.1:" + mock.getHttpsPort() + "/nexus"));
copyPackagedHelmChartToOutputdirectory(mojo);
assertThrows(MojoExecutionException.class, mojo::execute, "upload failed");
}

@DisplayName("nexus: tls insecure with selfsigned certificate")
@Test
void urlNexusTlsInsecure(UploadMojo mojo) {
mojo.setInsecure(true);
mojo.setUploadRepoStable(new HelmRepository()
.setType(RepoType.NEXUS)
.setName("my-nexus")
.setUrl("https://127.0.0.1:" + mock.getHttpsPort() + "/nexus"));
Path packaged = copyPackagedHelmChartToOutputdirectory(mojo);
assertUpload(mojo, RequestMethod.PUT, "/nexus/" + packaged.getFileName(), null);
}

@DisplayName("nexus: without authentication")
@Test
void urlNexusWithoutAuthentication(UploadMojo mojo) {
Expand Down Expand Up @@ -280,7 +309,9 @@ private void assertUpload(UploadMojo mojo, RequestMethod method, String path, St
assertEquals(1, requests.size(), "expected only one request");
LoggedRequest request = requests.get(0);
assertEquals(method, request.getMethod(), "method");
assertEquals("http://127.0.0.1:" + mock.getPort() + path, request.getAbsoluteUrl(), "url");
assertEquals((mojo.getUploadRepoStable().getUrl().startsWith("https")
? "https://127.0.0.1:" + mock.getHttpsPort()
: "http://127.0.0.1:" + mock.getPort()) + path, request.getAbsoluteUrl(), "url");
assertEquals("application/gzip", request.getHeader(HttpHeaders.CONTENT_TYPE), "content-type");
assertEquals(authorization, request.getHeader(HttpHeaders.AUTHORIZATION), "authorization");
}
Expand Down

0 comments on commit 1833b0e

Please sign in to comment.