External service integration support for TLS transport
[netconf.git] / transport / transport-tls / src / main / java / org / opendaylight / netconf / transport / tls / TLSServer.java
1 /*
2  * Copyright (c) 2022 PANTHEON.tech, s.r.o. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.netconf.transport.tls;
9
10 import com.google.common.util.concurrent.ListenableFuture;
11 import io.netty.bootstrap.Bootstrap;
12 import io.netty.bootstrap.ServerBootstrap;
13 import io.netty.handler.ssl.ClientAuth;
14 import io.netty.handler.ssl.SslContext;
15 import io.netty.handler.ssl.SslContextBuilder;
16 import org.eclipse.jdt.annotation.NonNull;
17 import org.opendaylight.netconf.transport.api.TransportChannelListener;
18 import org.opendaylight.netconf.transport.api.TransportStack;
19 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
20 import org.opendaylight.netconf.transport.tcp.TCPClient;
21 import org.opendaylight.netconf.transport.tcp.TCPServer;
22 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.client.rev230417.TcpClientGrouping;
23 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tcp.server.rev230417.TcpServerGrouping;
24 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417.TlsServerGrouping;
25 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417.tls.server.grouping.server.identity.auth.type.Certificate;
26 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.tls.server.rev230417.tls.server.grouping.server.identity.auth.type.RawPrivateKey;
27
28 /**
29  * A {@link TransportStack} acting as a TLS server.
30  */
31 public final class TLSServer extends TLSTransportStack {
32     private TLSServer(final TransportChannelListener listener, final SslContext sslContext) {
33         super(listener, sslContext);
34     }
35
36     private TLSServer(final TransportChannelListener listener, final SslHandlerFactory factory) {
37         super(listener, factory);
38     }
39
40     public static @NonNull ListenableFuture<TLSServer> connect(final TransportChannelListener listener,
41             final Bootstrap bootstrap, final TcpClientGrouping connectParams, final TlsServerGrouping serverParams)
42                 throws UnsupportedConfigurationException {
43         final var server = newServer(listener, serverParams);
44         return transformUnderlay(server, TCPClient.connect(server.asListener(), bootstrap, connectParams));
45     }
46
47     public static @NonNull ListenableFuture<TLSServer> listen(final TransportChannelListener listener,
48             final ServerBootstrap bootstrap, final TcpServerGrouping listenParams, final TlsServerGrouping serverParams)
49                 throws UnsupportedConfigurationException {
50         final var server = newServer(listener, serverParams);
51         return transformUnderlay(server, TCPServer.listen(server.asListener(), bootstrap, listenParams));
52     }
53
54     public static @NonNull ListenableFuture<TLSServer> listen(final TransportChannelListener listener,
55             final ServerBootstrap bootstrap, final TcpServerGrouping listenParams, final SslHandlerFactory factory)
56                 throws UnsupportedConfigurationException {
57         final var server = new TLSServer(listener, factory);
58         return transformUnderlay(server, TCPServer.listen(server.asListener(), bootstrap, listenParams));
59     }
60
61     private static TLSServer newServer(final TransportChannelListener listener, final TlsServerGrouping serverParams)
62             throws UnsupportedConfigurationException {
63         final var serverIdentity = serverParams.getServerIdentity();
64         if (serverIdentity == null) {
65             throw new UnsupportedConfigurationException("Missing server identity");
66         }
67         final SslContextBuilder builder;
68         final var authType = serverIdentity.getAuthType();
69         if (authType instanceof Certificate cert) {
70             // if-feature "server-ident-x509-cert"
71             final var certificate = cert.getCertificate();
72             if (certificate == null) {
73                 throw new UnsupportedConfigurationException("Missing certificate in " + cert);
74             }
75             builder = SslContextBuilder.forServer(newKeyManager(certificate));
76         } else if (authType instanceof RawPrivateKey rawKey) {
77             // if-feature "server-ident-raw-public-key"
78             final var rawPrivateKey = rawKey.getRawPrivateKey();
79             if (rawPrivateKey == null) {
80                 throw new UnsupportedConfigurationException("Missing key in " + rawKey);
81             }
82             builder = SslContextBuilder.forServer(newKeyManager(rawPrivateKey));
83         } else if (authType != null) {
84             throw new UnsupportedConfigurationException("Unsupported server authentication type " + authType);
85         } else {
86             throw new UnsupportedConfigurationException("Missing server authentication type");
87         }
88
89         final var clientAuth = serverParams.getClientAuthentication();
90         if (clientAuth != null) {
91             // CA && EE Certs : if-feature "client-ident-x509-cert"
92             // Raw public keys : if-feature "client-ident-raw-public-key"
93             final var trustManager = newTrustManager(clientAuth.getCaCerts(), clientAuth.getEeCerts(),
94                     clientAuth.getRawPublicKeys());
95             if (trustManager == null) {
96                 throw new UnsupportedOperationException("No client authentication methods in " + clientAuth);
97             }
98             builder.clientAuth(ClientAuth.REQUIRE).trustManager(trustManager);
99         } else {
100             builder.clientAuth(ClientAuth.NONE);
101         }
102         return new TLSServer(listener, buildSslContext(builder, serverParams.getHelloParams()));
103     }
104 }