diff options
author | Bjørn Christian Seime <bjorncs@verizonmedia.com> | 2022-07-20 16:01:16 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-20 16:01:16 +0200 |
commit | 37b82350dd673de1d7375c01838123bf0b1e1a91 (patch) | |
tree | d57a651f4c11589a5acefd26f70b612766857f3f /jrt/src | |
parent | 02c4a8fff7668971d0b82581081c1ea9466d5fc8 (diff) | |
parent | 4dcb1c83c96b51ec9a1770c269e75a94debebb9d (diff) |
Merge pull request #23528 from vespa-engine/bjorncs/capabilities
Bjorncs/capabilities [run-systemtest]
Diffstat (limited to 'jrt/src')
-rw-r--r-- | jrt/src/com/yahoo/jrt/Connection.java | 17 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/CryptoSocket.java | 12 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/ErrorCode.java | 3 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/InvocationServer.java | 6 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java | 7 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/Method.java | 6 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/RequestAccessFilter.java | 17 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/RequireCapabilitiesFilter.java | 54 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/Target.java | 12 | ||||
-rw-r--r-- | jrt/src/com/yahoo/jrt/TlsCryptoSocket.java | 12 |
10 files changed, 115 insertions, 31 deletions
diff --git a/jrt/src/com/yahoo/jrt/Connection.java b/jrt/src/com/yahoo/jrt/Connection.java index 644e2ef4ff3..1e4092efb75 100644 --- a/jrt/src/com/yahoo/jrt/Connection.java +++ b/jrt/src/com/yahoo/jrt/Connection.java @@ -1,9 +1,10 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.jrt; -import com.yahoo.security.tls.authz.ConnectionAuthContext; +import com.yahoo.security.tls.ConnectionAuthContext; import java.io.IOException; +import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; @@ -11,7 +12,6 @@ import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.IdentityHashMap; import java.util.Map; -import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; import java.util.logging.Level; import java.util.logging.Logger; @@ -438,9 +438,16 @@ class Connection extends Target { } @Override - public Optional<ConnectionAuthContext> getConnectionAuthContext() { - return Optional.ofNullable(socket) - .flatMap(CryptoSocket::getConnectionAuthContext); + public ConnectionAuthContext connectionAuthContext() { + if (socket == null) throw new IllegalStateException("Not connected"); + return socket.connectionAuthContext(); + } + + @Override + public Spec peerSpec() { + if (socket == null) throw new IllegalStateException("Not connected"); + InetSocketAddress addr = (InetSocketAddress) socket.channel().socket().getRemoteSocketAddress(); + return new Spec(addr.getHostString(), addr.getPort()); } public boolean isClient() { diff --git a/jrt/src/com/yahoo/jrt/CryptoSocket.java b/jrt/src/com/yahoo/jrt/CryptoSocket.java index aac91362405..e30579d2bdc 100644 --- a/jrt/src/com/yahoo/jrt/CryptoSocket.java +++ b/jrt/src/com/yahoo/jrt/CryptoSocket.java @@ -2,12 +2,11 @@ package com.yahoo.jrt; -import com.yahoo.security.tls.authz.ConnectionAuthContext; +import com.yahoo.security.tls.ConnectionAuthContext; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.util.Optional; /** @@ -104,11 +103,6 @@ public interface CryptoSocket { **/ public void dropEmptyBuffers(); - /** - * Returns the auth context for the current connection (given handshake completed), - * or empty if the current connection is not secure. - */ - default public Optional<ConnectionAuthContext> getConnectionAuthContext() { - return Optional.empty(); - } + /** Returns the auth context for the current connection (given handshake completed) */ + default ConnectionAuthContext connectionAuthContext() { return ConnectionAuthContext.defaultAllCapabilities(); } } diff --git a/jrt/src/com/yahoo/jrt/ErrorCode.java b/jrt/src/com/yahoo/jrt/ErrorCode.java index beaabcea316..8e129cfef98 100644 --- a/jrt/src/com/yahoo/jrt/ErrorCode.java +++ b/jrt/src/com/yahoo/jrt/ErrorCode.java @@ -49,4 +49,7 @@ public class ErrorCode /** Method failed (111) **/ public static final int METHOD_FAILED = 111; + + /** Permission denied (112) **/ + public static final int PERMISSION_DENIED = 112; } diff --git a/jrt/src/com/yahoo/jrt/InvocationServer.java b/jrt/src/com/yahoo/jrt/InvocationServer.java index 9df92eb20a6..7704c0019ed 100644 --- a/jrt/src/com/yahoo/jrt/InvocationServer.java +++ b/jrt/src/com/yahoo/jrt/InvocationServer.java @@ -31,7 +31,11 @@ class InvocationServer { public void invoke() { if (method != null) { if (method.checkParameters(request)) { - method.invoke(request); + if (method.requestAccessFilter().allow(request)) { + method.invoke(request); + } else { + request.setError(ErrorCode.PERMISSION_DENIED, "Permission denied"); + } } else { request.setError(ErrorCode.WRONG_PARAMS, "Parameters in " + request + " does not match " + method); } diff --git a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java index 42442289cd1..ab9d78d2676 100644 --- a/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java +++ b/jrt/src/com/yahoo/jrt/MaybeTlsCryptoSocket.java @@ -1,12 +1,11 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.jrt; -import com.yahoo.security.tls.authz.ConnectionAuthContext; +import com.yahoo.security.tls.ConnectionAuthContext; import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; -import java.util.Optional; /** * A crypto socket for the server side of a connection that @@ -132,7 +131,5 @@ public class MaybeTlsCryptoSocket implements CryptoSocket { @Override public int write(ByteBuffer src) throws IOException { return socket.write(src); } @Override public FlushResult flush() throws IOException { return socket.flush(); } @Override public void dropEmptyBuffers() { socket.dropEmptyBuffers(); } - @Override public Optional<ConnectionAuthContext> getConnectionAuthContext() { - return Optional.ofNullable(socket).flatMap(CryptoSocket::getConnectionAuthContext); - } + @Override public ConnectionAuthContext connectionAuthContext() { return socket.connectionAuthContext(); } } diff --git a/jrt/src/com/yahoo/jrt/Method.java b/jrt/src/com/yahoo/jrt/Method.java index 4fc9f0714da..89c66747e0b 100644 --- a/jrt/src/com/yahoo/jrt/Method.java +++ b/jrt/src/com/yahoo/jrt/Method.java @@ -40,6 +40,8 @@ public class Method { private String[] returnName; private String[] returnDesc; + private RequestAccessFilter filter = RequestAccessFilter.ALLOW_ALL; + private static final String undocumented = "???"; @@ -147,6 +149,10 @@ public class Method { return this; } + public Method requestAccessFilter(RequestAccessFilter filter) { this.filter = filter; return this; } + + public RequestAccessFilter requestAccessFilter() { return filter; } + /** * Obtain the name of a parameter * diff --git a/jrt/src/com/yahoo/jrt/RequestAccessFilter.java b/jrt/src/com/yahoo/jrt/RequestAccessFilter.java new file mode 100644 index 00000000000..6701436d6ce --- /dev/null +++ b/jrt/src/com/yahoo/jrt/RequestAccessFilter.java @@ -0,0 +1,17 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jrt; + +/** + * Request access filter is invoked before any call to {@link Method#invoke(Request)}. + * If {@link #allow(Request)} returns false, the method is not invoked, and the request is failed with error + * {@link ErrorCode#PERMISSION_DENIED}. + * + * @author bjorncs + */ +public interface RequestAccessFilter { + + RequestAccessFilter ALLOW_ALL = __ -> true; + + boolean allow(Request r); + +} diff --git a/jrt/src/com/yahoo/jrt/RequireCapabilitiesFilter.java b/jrt/src/com/yahoo/jrt/RequireCapabilitiesFilter.java new file mode 100644 index 00000000000..bb2eafcf711 --- /dev/null +++ b/jrt/src/com/yahoo/jrt/RequireCapabilitiesFilter.java @@ -0,0 +1,54 @@ +// Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +package com.yahoo.jrt; + +import com.yahoo.security.tls.Capability; +import com.yahoo.security.tls.CapabilityMode; +import com.yahoo.security.tls.CapabilitySet; +import com.yahoo.security.tls.ConnectionAuthContext; +import com.yahoo.security.tls.TransportSecurityUtils; + +import java.util.logging.Logger; + +import static com.yahoo.security.tls.CapabilityMode.DISABLE; +import static com.yahoo.security.tls.CapabilityMode.LOG_ONLY; + +/** + * @author bjorncs + */ +public class RequireCapabilitiesFilter implements RequestAccessFilter { + + private static final Logger log = Logger.getLogger(RequireCapabilitiesFilter.class.getName()); + private static final CapabilityMode MODE = TransportSecurityUtils.getCapabilityMode(); + + private final CapabilitySet requiredCapabilities; + + public RequireCapabilitiesFilter(CapabilitySet requiredCapabilities) { + this.requiredCapabilities = requiredCapabilities; + } + + public RequireCapabilitiesFilter(Capability... requiredCapabilities) { + this(CapabilitySet.from(requiredCapabilities)); + } + + @Override + public boolean allow(Request r) { + if (MODE == DISABLE) return true; + ConnectionAuthContext ctx = r.target().connectionAuthContext(); + CapabilitySet peerCapabilities = ctx.capabilities(); + boolean authorized = peerCapabilities.has(requiredCapabilities); + if (!authorized) { + String msg = "%sPermission denied for RPC method '%s'. Peer at %s with %s. Call requires %s, but peer has %s" + .formatted(MODE == LOG_ONLY ? "Dry-run: " : "", r.methodName(), r.target().peerSpec(), ctx.peerCertificateString().orElseThrow(), + requiredCapabilities.toNames(), peerCapabilities.toNames()); + if (MODE == LOG_ONLY) { + log.info(msg); + return true; + } else { + log.warning(msg); + return false; + } + } + return true; + } + +} diff --git a/jrt/src/com/yahoo/jrt/Target.java b/jrt/src/com/yahoo/jrt/Target.java index 239a71f53b3..0e8c27deac5 100644 --- a/jrt/src/com/yahoo/jrt/Target.java +++ b/jrt/src/com/yahoo/jrt/Target.java @@ -1,9 +1,7 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.jrt; -import com.yahoo.security.tls.authz.ConnectionAuthContext; - -import java.util.Optional; +import com.yahoo.security.tls.ConnectionAuthContext; /** * A Target represents a connection endpoint with RPC @@ -71,9 +69,13 @@ public abstract class Target { public Exception getConnectionLostReason() { return null; } /** - * Returns the connection auth context associated with this target, or empty if no connection or is insecure. + * Returns the connection auth context associated with this target. */ - public abstract Optional<ConnectionAuthContext> getConnectionAuthContext(); + public abstract ConnectionAuthContext connectionAuthContext(); + + + /** @return address spec of socket peer */ + public abstract Spec peerSpec(); /** * Check if this target represents the client side of a diff --git a/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java b/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java index ecd76e1eb17..13274dc3ba5 100644 --- a/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java +++ b/jrt/src/com/yahoo/jrt/TlsCryptoSocket.java @@ -1,8 +1,8 @@ // Copyright Yahoo. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. package com.yahoo.jrt; -import com.yahoo.security.tls.authz.ConnectionAuthContext; -import com.yahoo.security.tls.authz.PeerAuthorizerTrustManager; +import com.yahoo.security.tls.ConnectionAuthContext; +import com.yahoo.security.tls.PeerAuthorizerTrustManager; import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngineResult; @@ -14,7 +14,7 @@ import java.io.IOException; import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; import java.nio.channels.SocketChannel; -import java.util.Optional; +import java.util.Objects; import java.util.logging.Logger; import static javax.net.ssl.SSLEngineResult.Status; @@ -219,9 +219,9 @@ public class TlsCryptoSocket implements CryptoSocket { } @Override - public Optional<ConnectionAuthContext> getConnectionAuthContext() { - if (handshakeState != HandshakeState.COMPLETED) return Optional.empty(); - return Optional.ofNullable(authContext); + public ConnectionAuthContext connectionAuthContext() { + if (handshakeState != HandshakeState.COMPLETED) throw new IllegalStateException("Handshake not complete"); + return Objects.requireNonNull(authContext); } private boolean handshakeWrap() throws IOException { |