Skip to content

Commit

Permalink
Add AssumeRoleWithCertificate credential provider.
Browse files Browse the repository at this point in the history
Signed-off-by: Bala.FA <bala@minio.io>
  • Loading branch information
balamurugana committed Oct 8, 2021
1 parent ca1e8b0 commit 1f5e15f
Show file tree
Hide file tree
Showing 3 changed files with 234 additions and 0 deletions.
32 changes: 32 additions & 0 deletions api/src/main/java/io/minio/credentials/AssumeRoleBaseProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,12 @@
import io.minio.Xml;
import io.minio.errors.XmlParserException;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.ProviderException;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
Expand All @@ -43,6 +46,35 @@ public AssumeRoleBaseProvider(OkHttpClient customHttpClient) {
: new OkHttpClient().newBuilder().protocols(Arrays.asList(Protocol.HTTP_1_1)).build();
}

public AssumeRoleBaseProvider(
OkHttpClient customHttpClient,
SSLSocketFactory sslSocketFactory,
X509TrustManager trustManager)
throws GeneralSecurityException, IOException {
if (customHttpClient != null) {
if (sslSocketFactory != null || trustManager != null) {
throw new IllegalArgumentException(
"Either sslSocketFactory/trustManager or custom HTTP client must be provided");
}

this.httpClient = customHttpClient;
} else {
if (sslSocketFactory == null || trustManager == null) {
throw new IllegalArgumentException(
"Both sslSocketFactory and trustManager must be provided");
}

// HTTP/1.1 is only supported in default client because of HTTP/2 in OkHttpClient cause 5
// minutes timeout on program exit.
this.httpClient =
new OkHttpClient()
.newBuilder()
.protocols(Arrays.asList(Protocol.HTTP_1_1))
.sslSocketFactory(sslSocketFactory, trustManager)
.build();
}
}

@Override
public synchronized Credentials fetch() {
if (credentials != null && !credentials.isExpired()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/*
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.minio.credentials;

import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import org.simpleframework.xml.Element;
import org.simpleframework.xml.Namespace;
import org.simpleframework.xml.Path;
import org.simpleframework.xml.Root;

/**
* Credential provider using <a
* href="https://github.com/minio/minio/blob/master/docs/sts/tls.md">AssumeRoleWithCertificate
* API</a>.
*/
public class CertificateIdentityProvider extends AssumeRoleBaseProvider {
private static final RequestBody EMPTY_BODY =
RequestBody.create(new byte[] {}, MediaType.parse("application/octet-stream"));
private final Request request;

public CertificateIdentityProvider(
@Nonnull String stsEndpoint,
@Nullable SSLSocketFactory sslSocketFactory,
@Nullable X509TrustManager trustManager,
@Nullable Integer durationSeconds,
@Nullable OkHttpClient customHttpClient)
throws GeneralSecurityException, IOException {
super(customHttpClient, sslSocketFactory, trustManager);
stsEndpoint = Objects.requireNonNull(stsEndpoint, "STS endpoint cannot be empty");
HttpUrl url = Objects.requireNonNull(HttpUrl.parse(stsEndpoint), "Invalid STS endpoint");
if (!url.isHttps()) {
throw new IllegalArgumentException("STS endpoint scheme must be HTTPS");
}

HttpUrl.Builder urlBuilder =
newUrlBuilder(
url,
"AssumeRoleWithCertificate",
getValidDurationSeconds(durationSeconds),
null,
null,
null);
url = urlBuilder.build();
this.request = new Request.Builder().url(url).method("POST", EMPTY_BODY).build();
}

@Override
protected Request getRequest() {
return this.request;
}

@Override
protected Class<? extends AssumeRoleBaseProvider.Response> getResponseClass() {
return CertificateIdentityResponse.class;
}

/** Object representation of response XML of AssumeRoleWithCertificate API. */
@Root(name = "AssumeRoleWithCertificateResponse", strict = false)
@Namespace(reference = "https://sts.amazonaws.com/doc/2011-06-15/")
public static class CertificateIdentityResponse implements AssumeRoleBaseProvider.Response {
@Path(value = "AssumeRoleWithCertificateResult")
@Element(name = "Credentials")
private Credentials credentials;

public Credentials getCredentials() {
return credentials;
}
}
}
108 changes: 108 additions & 0 deletions examples/MinioClientWithCertificateIdentityProvider.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
/*
* MinIO Java SDK for Amazon S3 Compatible Cloud Storage, (C) 2021 MinIO, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import io.minio.MinioClient;
import io.minio.StatObjectArgs;
import io.minio.StatObjectResponse;
import io.minio.credentials.CertificateIdentityProvider;
import io.minio.credentials.Provider;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.X509TrustManager;

public class MinioClientWithCertificateIdentityProvider {
public static void main(String[] args) throws Exception {
// STS endpoint usually point to MinIO server.
String stsEndpoint = "https://STS-HOST:STS-PORT/";

// SSL socket factory.
SSLSocketFactory sslSocketFactory = null;

// Trust manager.
X509TrustManager trustManager = null;

// Below is a sample code to construct sslSocketFactory and trustManager for self-signed
// certificates (server and client) used in a MinIO server setup.
//
// CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
//
// Certificate serverCertificate = null;
// try (FileInputStream fis = new FileInputStream("/home/bala/.minio/certs/public.crt")) {
// serverCertificate = certificateFactory.generateCertificate(fis);
// }
//
// KeyStore trustStore = KeyStore.getInstance(KeyStore.getDefaultType());
// trustStore.load(null, "secret".toCharArray());
//
// trustStore.setCertificateEntry("server-certificate", serverCertificate);
//
// String privateKeyString =
// new String(
// Files.readAllBytes(Paths.get("/home/bala/.minio/certs/CAs/client1.key")),
// Charset.defaultCharset())
// .replace("-----BEGIN PRIVATE KEY-----", "")
// .replaceAll(System.lineSeparator(), "")
// .replace("-----END PRIVATE KEY-----", "");
//
// byte[] privateKey = Base64.getDecoder().decode(privateKeyString);
// KeyFactory keyFactory = KeyFactory.getInstance("RSA");
// PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(privateKey);
//
// Certificate certificateChain = null;
// try (FileInputStream fis = new FileInputStream("/home/bala/.minio/certs/CAs/client1.crt")) {
// certificateChain = certificateFactory.generateCertificate(fis);
// }
//
// KeyStore identityStore = KeyStore.getInstance(KeyStore.getDefaultType());
// identityStore.load(null, "secret".toCharArray());
// identityStore.setKeyEntry(
// "client",
// keyFactory.generatePrivate(keySpec),
// "secret".toCharArray(),
// new Certificate[] {certificateChain});
//
// TrustManagerFactory trustManagerFactory =
// TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
// trustManagerFactory.init(trustStore);
// TrustManager[] trustManagers = trustManagerFactory.getTrustManagers();
//
// KeyManagerFactory keyManagerFactory =
// KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
// keyManagerFactory.init(identityStore, "secret".toCharArray());
// KeyManager[] keyManagers = keyManagerFactory.getKeyManagers();
//
// SSLContext sslContext = SSLContext.getInstance("TLS");
// sslContext.init(keyManagers, trustManagers, null);
//
// SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
// X509TrustManager trustManager = (X509TrustManager) trustManagers[0];
//

Provider provider =
new CertificateIdentityProvider(stsEndpoint, sslSocketFactory, trustManager, null, null);

MinioClient minioClient =
MinioClient.builder()
.endpoint("https://MINIO-HOST:MINIO-PORT")
.credentialsProvider(provider)
.build();

// Get information of an object.
StatObjectResponse stat =
minioClient.statObject(
StatObjectArgs.builder().bucket("my-bucketname").object("my-objectname").build());
System.out.println(stat);
}
}

0 comments on commit 1f5e15f

Please sign in to comment.