2 * Copyright (c) 2023 PANTHEON.tech, s.r.o. and others. All rights reserved.
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
8 package org.opendaylight.netconf.transport.ssh;
10 import static java.util.Objects.requireNonNull;
12 import com.google.common.collect.ImmutableList;
13 import com.google.common.collect.ImmutableMap;
14 import com.google.errorprone.annotations.DoNotCall;
15 import io.netty.channel.EventLoopGroup;
16 import java.security.PublicKey;
17 import java.util.List;
18 import org.opendaylight.netconf.shaded.sshd.common.keyprovider.KeyPairProvider;
19 import org.opendaylight.netconf.shaded.sshd.netty.NettyIoServiceFactoryFactory;
20 import org.opendaylight.netconf.shaded.sshd.server.ServerBuilder;
21 import org.opendaylight.netconf.shaded.sshd.server.SshServer;
22 import org.opendaylight.netconf.shaded.sshd.server.auth.UserAuthFactory;
23 import org.opendaylight.netconf.shaded.sshd.server.auth.hostbased.UserAuthHostBasedFactory;
24 import org.opendaylight.netconf.shaded.sshd.server.auth.password.UserAuthPasswordFactory;
25 import org.opendaylight.netconf.shaded.sshd.server.auth.pubkey.UserAuthPublicKeyFactory;
26 import org.opendaylight.netconf.shaded.sshd.server.subsystem.SubsystemFactory;
27 import org.opendaylight.netconf.transport.api.UnsupportedConfigurationException;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.server.rev230417.SshServerGrouping;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.server.rev230417.ssh.server.grouping.ClientAuthentication;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.server.rev230417.ssh.server.grouping.Keepalives;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.ssh.server.rev230417.ssh.server.grouping.ServerIdentity;
34 * Our internal-use {@link SshServer}. We reuse all the properties and logic of an {@link SshServer}, but we never allow
37 final class TransportSshServer extends SshServer {
38 private TransportSshServer() {
43 * Guaranteed to throw an exception.
45 * @throws UnsupportedOperationException always
48 @Deprecated(forRemoval = true)
49 @DoNotCall("Always throws UnsupportedOperationException")
51 throw new UnsupportedOperationException();
55 * Guaranteed to throw an exception.
57 * @throws UnsupportedOperationException always
60 @Deprecated(forRemoval = true)
61 @DoNotCall("Always throws UnsupportedOperationException")
63 throw new UnsupportedOperationException();
67 * A {@link ServerBuilder} producing {@link TransportSshServer}s. Also hosts adaptation from
68 * {@code ietf-netconf-server.yang} configuration.
70 static final class Builder extends ServerBuilder {
71 private final NettyIoServiceFactoryFactory ioServiceFactory;
72 private final EventLoopGroup group;
73 private final SubsystemFactory subsystemFactory;
75 private ServerFactoryManagerConfigurator configurator;
76 private ClientAuthentication clientAuthentication;
77 private ServerIdentity serverIdentity;
78 private Keepalives keepAlives;
80 Builder(final NettyIoServiceFactoryFactory ioServiceFactory, final EventLoopGroup group,
81 final SubsystemFactory subsystemFactory) {
82 this.ioServiceFactory = requireNonNull(ioServiceFactory);
83 this.group = requireNonNull(group);
84 this.subsystemFactory = requireNonNull(subsystemFactory);
87 Builder serverParams(final SshServerGrouping serverParams) throws UnsupportedConfigurationException {
88 if (serverParams != null) {
89 ConfigUtils.setTransportParams(this, serverParams.getTransportParams(),
90 TransportUtils::getServerKexFactories);
91 keepAlives = serverParams.getKeepalives();
92 serverIdentity = serverParams.getServerIdentity();
93 if (serverIdentity == null) {
94 throw new UnsupportedConfigurationException("Server identity configuration is required");
96 clientAuthentication = serverParams.getClientAuthentication();
101 Builder configurator(final ServerFactoryManagerConfigurator newConfigurator) {
102 configurator = newConfigurator;
107 * Guaranteed to throw an exception.
109 * @throws UnsupportedOperationException always
112 @Deprecated(forRemoval = true)
113 @DoNotCall("Always throws UnsupportedOperationException")
114 public TransportSshServer build() {
115 throw new UnsupportedOperationException();
119 * Guaranteed to throw an exception.
121 * @throws UnsupportedOperationException always
124 @Deprecated(forRemoval = true)
125 @DoNotCall("Always throws UnsupportedOperationException")
126 public TransportSshServer build(final boolean isFillWithDefaultValues) {
127 throw new UnsupportedOperationException();
130 TransportSshServer buildChecked() throws UnsupportedConfigurationException {
131 final var ret = (TransportSshServer) super.build(true);
132 if (keepAlives != null) {
133 ConfigUtils.setKeepAlives(ret, keepAlives.getMaxWait(), keepAlives.getMaxAttempts());
135 ConfigUtils.setKeepAlives(ret, null, null);
137 if (serverIdentity != null) {
138 setServerIdentity(ret, serverIdentity);
140 if (clientAuthentication != null) {
141 setClientAuthentication(ret, clientAuthentication);
143 if (configurator != null) {
144 configurator.configureServerFactoryManager(ret);
147 ret.setSubsystemFactories(List.of(subsystemFactory));
148 ret.setIoServiceFactoryFactory(ioServiceFactory);
149 ret.setScheduledExecutorService(group);
153 } catch (IllegalArgumentException e) {
154 throw new UnsupportedConfigurationException("Inconsistent client configuration", e);
160 protected ServerBuilder fillWithDefaultValues() {
161 if (factory == null) {
162 factory = TransportSshServer::new;
164 return super.fillWithDefaultValues();
167 private static void setServerIdentity(final TransportSshServer server, final ServerIdentity serverIdentity)
168 throws UnsupportedConfigurationException {
169 final var hostKey = serverIdentity.getHostKey();
170 if (hostKey == null || hostKey.isEmpty()) {
171 throw new UnsupportedConfigurationException("Host keys is missing in server identity configuration");
173 final var serverHostKeyPairs = ConfigUtils.extractServerHostKeys(hostKey);
174 if (!serverHostKeyPairs.isEmpty()) {
175 server.setKeyPairProvider(KeyPairProvider.wrap(serverHostKeyPairs));
179 private static void setClientAuthentication(final TransportSshServer server,
180 final ClientAuthentication clientAuthentication) throws UnsupportedConfigurationException {
181 final var users = clientAuthentication.getUsers();
185 final var userMap = users.getUser();
186 if (userMap != null) {
187 final var passwordMapBuilder = ImmutableMap.<String, String>builder();
188 final var hostBasedMapBuilder = ImmutableMap.<String, List<PublicKey>>builder();
189 final var publicKeyMapBuilder = ImmutableMap.<String, List<PublicKey>>builder();
190 for (var entry : userMap.entrySet()) {
191 final var username = entry.getKey().getName();
192 final var value = entry.getValue();
193 final var password = value.getPassword();
194 if (password != null) {
195 passwordMapBuilder.put(username, password.getValue());
197 final var hostBased = value.getHostbased();
198 if (hostBased != null) {
199 hostBasedMapBuilder.put(username,
200 ConfigUtils.extractPublicKeys(hostBased.getInlineOrTruststore()));
202 final var publicKey = value.getPublicKeys();
203 if (publicKey != null) {
204 publicKeyMapBuilder.put(username,
205 ConfigUtils.extractPublicKeys(publicKey.getInlineOrTruststore()));
208 final var authFactoriesBuilder = ImmutableList.<UserAuthFactory>builder();
209 final var passwordMap = passwordMapBuilder.build();
210 if (!passwordMap.isEmpty()) {
211 authFactoriesBuilder.add(new UserAuthPasswordFactory());
212 server.setPasswordAuthenticator(new CryptHashPasswordAuthenticator(passwordMap));
214 final var hostBasedMap = hostBasedMapBuilder.build();
215 if (!hostBasedMap.isEmpty()) {
216 final var factory = new UserAuthHostBasedFactory();
217 factory.setSignatureFactories(server.getSignatureFactories());
218 authFactoriesBuilder.add(factory);
219 server.setHostBasedAuthenticator(new UserPublicKeyAuthenticator(hostBasedMap));
221 final var publicKeyMap = publicKeyMapBuilder.build();
222 if (!publicKeyMap.isEmpty()) {
223 final var factory = new UserAuthPublicKeyFactory();
224 factory.setSignatureFactories(server.getSignatureFactories());
225 authFactoriesBuilder.add(factory);
226 server.setPublickeyAuthenticator(new UserPublicKeyAuthenticator(publicKeyMap));
228 server.setUserAuthFactories(authFactoriesBuilder.build());