From 17579280e419347d76ff950ebac88844c27a4d8e Mon Sep 17 00:00:00 2001 From: Jon Marius Venstad Date: Thu, 13 Jun 2019 14:24:49 +0200 Subject: Separate API and deployment authenticators --- .../ai/vespa/hosted/auth/ApiAuthenticator.java | 16 +++++ .../java/ai/vespa/hosted/auth/Authenticator.java | 78 ---------------------- .../vespa/hosted/auth/EndpointAuthenticator.java | 68 +++++++++++++++++++ 3 files changed, 84 insertions(+), 78 deletions(-) create mode 100644 tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java delete mode 100644 tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java create mode 100644 tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java (limited to 'tenant-auth') diff --git a/tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java b/tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java new file mode 100644 index 00000000000..2b5bbb188dc --- /dev/null +++ b/tenant-auth/src/main/java/ai/vespa/hosted/auth/ApiAuthenticator.java @@ -0,0 +1,16 @@ +package ai.vespa.hosted.auth; + +import ai.vespa.hosted.api.ControllerHttpClient; +import ai.vespa.hosted.api.Properties; + +public class ApiAuthenticator implements ai.vespa.hosted.api.ApiAuthenticator { + + /** Returns an authenticating controller client, using private key signatures for authentication. */ + @Override + public ControllerHttpClient controller() { + return ControllerHttpClient.withSignatureKey(Properties.endpoint(), + Properties.privateKeyFile(), + Properties.application()); + } + +} diff --git a/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java b/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java deleted file mode 100644 index f2de1f1e210..00000000000 --- a/tenant-auth/src/main/java/ai/vespa/hosted/auth/Authenticator.java +++ /dev/null @@ -1,78 +0,0 @@ -package ai.vespa.hosted.auth; - -import ai.vespa.hosted.api.ControllerHttpClient; -import ai.vespa.hosted.api.Properties; -import com.yahoo.config.provision.ApplicationId; -import com.yahoo.security.KeyUtils; -import com.yahoo.security.SslContextBuilder; -import com.yahoo.security.X509CertificateUtils; - -import javax.net.ssl.SSLContext; -import java.io.IOException; -import java.io.UncheckedIOException; -import java.net.URI; -import java.net.http.HttpRequest; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.security.NoSuchAlgorithmException; -import java.security.PrivateKey; -import java.security.cert.X509Certificate; -import java.time.Instant; -import java.util.Optional; - -import static ai.vespa.hosted.api.Properties.getNonBlankProperty; -import static ai.vespa.hosted.api.Properties.requireNonBlankProperty; - -/** - * Authenticates against the hosted Vespa API using private key signatures, and against Vespa applications using mutual TLS. - * - * @author jonmv - */ -public class Authenticator implements ai.vespa.hosted.api.Authenticator { - - /** - * If {@code System.getProperty("vespa.test.credentials.root")} is set, key and certificate files - * "key" and "cert" in that directory are used; otherwise, the system default SSLContext is returned. - */ - @Override - public SSLContext sslContext() { - try { - Optional credentialsRootProperty = getNonBlankProperty("vespa.test.credentials.root"); - if (credentialsRootProperty.isEmpty()) - return SSLContext.getDefault(); - - Path credentialsRoot = Path.of(credentialsRootProperty.get()); - Path certificateFile = credentialsRoot.resolve("cert"); - Path privateKeyFile = credentialsRoot.resolve("key"); - - X509Certificate certificate = X509CertificateUtils.fromPem(new String(Files.readAllBytes(certificateFile))); - if ( Instant.now().isBefore(certificate.getNotBefore().toInstant()) - || Instant.now().isAfter(certificate.getNotAfter().toInstant())) - throw new IllegalStateException("Certificate at '" + certificateFile + "' is valid between " + - certificate.getNotBefore() + " and " + certificate.getNotAfter() + " — not now."); - - PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(new String(Files.readAllBytes(privateKeyFile))); - return new SslContextBuilder().withKeyStore(privateKey, certificate).build(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(e); - } - } - - @Override - public HttpRequest.Builder authenticated(HttpRequest.Builder request) { - return request; - } - - /** Returns an authenticating controller client, using the (overridable) project properties of this Vespa application. */ - @Override - public ControllerHttpClient controller() { - return ControllerHttpClient.withSignatureKey(Properties.endpoint(), - Properties.privateKeyFile(), - Properties.application()); - } - -} diff --git a/tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java b/tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java new file mode 100644 index 00000000000..abb4197bda1 --- /dev/null +++ b/tenant-auth/src/main/java/ai/vespa/hosted/auth/EndpointAuthenticator.java @@ -0,0 +1,68 @@ +package ai.vespa.hosted.auth; + +import com.yahoo.config.provision.SystemName; +import com.yahoo.security.KeyUtils; +import com.yahoo.security.SslContextBuilder; +import com.yahoo.security.X509CertificateUtils; + +import javax.net.ssl.SSLContext; +import java.io.IOException; +import java.io.UncheckedIOException; +import java.net.http.HttpRequest; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.cert.X509Certificate; +import java.time.Instant; +import java.util.Optional; + +import static ai.vespa.hosted.api.Properties.getNonBlankProperty; + +/** + * Authenticates against the hosted Vespa API using private key signatures, and against Vespa applications using mutual TLS. + * + * @author jonmv + */ +public class EndpointAuthenticator implements ai.vespa.hosted.api.EndpointAuthenticator { + + /** Don't touch. */ + public EndpointAuthenticator(@SuppressWarnings("unused") SystemName __) { } + + /** + * If {@code System.getProperty("vespa.test.credentials.root")} is set, key and certificate files + * "key" and "cert" in that directory are used; otherwise, the system default SSLContext is returned. + */ + @Override + public SSLContext sslContext() { + try { + Optional credentialsRootProperty = getNonBlankProperty("vespa.test.credentials.root"); + if (credentialsRootProperty.isEmpty()) + return SSLContext.getDefault(); + + Path credentialsRoot = Path.of(credentialsRootProperty.get()); + Path certificateFile = credentialsRoot.resolve("cert"); + Path privateKeyFile = credentialsRoot.resolve("key"); + + X509Certificate certificate = X509CertificateUtils.fromPem(new String(Files.readAllBytes(certificateFile))); + if ( Instant.now().isBefore(certificate.getNotBefore().toInstant()) + || Instant.now().isAfter(certificate.getNotAfter().toInstant())) + throw new IllegalStateException("Certificate at '" + certificateFile + "' is valid between " + + certificate.getNotBefore() + " and " + certificate.getNotAfter() + " — not now."); + + PrivateKey privateKey = KeyUtils.fromPemEncodedPrivateKey(new String(Files.readAllBytes(privateKeyFile))); + return new SslContextBuilder().withKeyStore(privateKey, certificate).build(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + } + + @Override + public HttpRequest.Builder authenticated(HttpRequest.Builder request) { + return request; + } + +} -- cgit v1.2.3