Repository: karaf Updated Branches: refs/heads/master 7f9ef85bd -> 44323c275
[KARAF-3622]Enhance SSH configuration mechanism Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/44323c27 Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/44323c27 Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/44323c27 Branch: refs/heads/master Commit: 44323c27529c8668db6cc836f00d0bb75e2cb4d5 Parents: 7f9ef85 Author: Freeman Fang <[email protected]> Authored: Wed Nov 2 15:57:41 2016 +0800 Committer: Freeman Fang <[email protected]> Committed: Wed Nov 2 15:57:41 2016 +0800 ---------------------------------------------------------------------- .../resources/etc/org.apache.karaf.shell.cfg | 20 ++++ shell/ssh/pom.xml | 6 + .../org/apache/karaf/shell/ssh/Activator.java | 6 +- .../org/apache/karaf/shell/ssh/SshUtils.java | 118 +++++++++++++------ .../apache/karaf/shell/ssh/SshUtilsTest.java | 103 ++++++++++++++++ 5 files changed, 213 insertions(+), 40 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/44323c27/assemblies/features/base/src/main/resources/resources/etc/org.apache.karaf.shell.cfg ---------------------------------------------------------------------- diff --git a/assemblies/features/base/src/main/resources/resources/etc/org.apache.karaf.shell.cfg b/assemblies/features/base/src/main/resources/resources/etc/org.apache.karaf.shell.cfg index de4b163..e996b1b 100644 --- a/assemblies/features/base/src/main/resources/resources/etc/org.apache.karaf.shell.cfg +++ b/assemblies/features/base/src/main/resources/resources/etc/org.apache.karaf.shell.cfg @@ -93,4 +93,24 @@ hostKeyFormat = simple # This property define the default value when you use the Karaf shell console. # You can change the completion mode directly in the shell console, using shell:completion command. # + +# +# Override allowed SSH cipher algorithms. +# Default: aes128-ctr,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc +# +# ciphers = aes128-ctr,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc + +# +# Override allowed SSH HMAC algorithms. +# Default: hmac-sha2-512,hmac-sha2-256,hmac-sha1 +# +# macs = hmac-sha2-512,hmac-sha2-256,hmac-sha1 + +# +# Override allowed SSH key exchange algorithms. +# Default: diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1 +# +# kexAlgorithms = diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1 + + completionMode = GLOBAL http://git-wip-us.apache.org/repos/asf/karaf/blob/44323c27/shell/ssh/pom.xml ---------------------------------------------------------------------- diff --git a/shell/ssh/pom.xml b/shell/ssh/pom.xml index c0e2664..97f2783 100644 --- a/shell/ssh/pom.xml +++ b/shell/ssh/pom.xml @@ -103,6 +103,12 @@ <version>${commons-io.version}</version> <scope>test</scope> </dependency> + + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + <scope>test</scope> + </dependency> </dependencies> <build> http://git-wip-us.apache.org/repos/asf/karaf/blob/44323c27/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java ---------------------------------------------------------------------- diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java index 2714398..9198cfc 100644 --- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java +++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/Activator.java @@ -149,8 +149,9 @@ public class Activator extends BaseActivator implements ManagedService { String authMethods = getString("authMethods", "keyboard-interactive,password,publickey"); int keySize = getInt("keySize", 4096); String algorithm = getString("algorithm", "RSA"); - String macs = getString("macs", "hmac-sha1"); - String ciphers = getString("ciphers", "aes256-ctr,aes192-ctr,aes128-ctr,arcfour256"); + String macs = getString("macs", "hmac-sha2-512,hmac-sha2-256,hmac-sha1"); + String ciphers = getString("ciphers", "aes128-ctr,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc"); + String kexAlgorithms = getString("kexAlgorithms", "diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1"); String welcomeBanner = getString("welcomeBanner", null); AbstractGeneratorHostKeyProvider keyPairProvider; @@ -182,6 +183,7 @@ public class Activator extends BaseActivator implements ManagedService { server.setHost(sshHost); server.setMacFactories(SshUtils.buildMacs(macs)); server.setCipherFactories(SshUtils.buildCiphers(ciphers)); + server.setKeyExchangeFactories(SshUtils.buildKexAlgorithms(kexAlgorithms)); server.setShellFactory(new ShellFactoryImpl(sessionFactory)); server.setCommandFactory(new ScpCommandFactory.Builder().withDelegate(new ShellCommandFactory(sessionFactory)).build()); server.setSubsystemFactories(Arrays.<NamedFactory<org.apache.sshd.server.Command>>asList(new SftpSubsystemFactory())); http://git-wip-us.apache.org/repos/asf/karaf/blob/44323c27/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshUtils.java ---------------------------------------------------------------------- diff --git a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshUtils.java b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshUtils.java index 83f3112..5b5330c 100644 --- a/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshUtils.java +++ b/shell/ssh/src/main/java/org/apache/karaf/shell/ssh/SshUtils.java @@ -18,71 +18,113 @@ */ package org.apache.karaf.shell.ssh; -import java.security.InvalidKeyException; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; -import java.util.Iterator; -import java.util.LinkedList; import java.util.List; +import org.apache.sshd.server.ServerBuilder; +import org.apache.sshd.server.SshServer; import org.apache.sshd.common.NamedFactory; import org.apache.sshd.common.cipher.BuiltinCiphers; import org.apache.sshd.common.cipher.Cipher; -import org.apache.sshd.common.mac.BuiltinMacs; +import org.apache.sshd.common.compression.Compression; +import org.apache.sshd.common.kex.KeyExchange; import org.apache.sshd.common.mac.Mac; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + public class SshUtils { - public static <S> List<NamedFactory<S>> filter(Collection<NamedFactory<S>> factories, String names) { + private static final Logger LOGGER = LoggerFactory.getLogger(SshUtils.class); + + public static <S> List<NamedFactory<S>> filter(Class<S> type, + Collection<NamedFactory<S>> factories, String names) { List<NamedFactory<S>> list = new ArrayList<NamedFactory<S>>(); for (String name : names.split(",")) { + name = name.trim(); + boolean found = false; for (NamedFactory<S> factory : factories) { if (factory.getName().equals(name)) { list.add(factory); + found = true; + break; } } + if (!found) { + LOGGER.warn("Configured " + type.getSimpleName().toLowerCase() + + " '" + name + "' not available"); + } } return list; } public static List<NamedFactory<Mac>> buildMacs(String names) { - return filter(Arrays.<NamedFactory<Mac>>asList( - BuiltinMacs.hmacmd5, - BuiltinMacs.hmacsha1, - BuiltinMacs.hmacmd596, - BuiltinMacs.hmacsha196), - names); + return filter(Mac.class, new ServerConfig().getMacFactories(), names); } public static List<NamedFactory<Cipher>> buildCiphers(String names) { - List<NamedFactory<Cipher>> avail = new LinkedList<NamedFactory<Cipher>>(); - avail.add(BuiltinCiphers.aes128ctr); - avail.add(BuiltinCiphers.aes256ctr); - avail.add(BuiltinCiphers.arcfour128); - avail.add(BuiltinCiphers.arcfour256); - avail.add(BuiltinCiphers.aes128cbc); - avail.add(BuiltinCiphers.tripledescbc); - avail.add(BuiltinCiphers.blowfishcbc); - avail.add(BuiltinCiphers.aes192cbc); - avail.add(BuiltinCiphers.aes256cbc); - - avail = filter(avail, names); - - for (Iterator<NamedFactory<Cipher>> i = avail.iterator(); i.hasNext();) { - final NamedFactory<Cipher> f = i.next(); - try { - final Cipher c = f.create(); - final byte[] key = new byte[c.getBlockSize()]; - final byte[] iv = new byte[c.getIVSize()]; - c.init(Cipher.Mode.Encrypt, key, iv); - } catch (InvalidKeyException e) { - i.remove(); - } catch (Exception e) { - i.remove(); - } + ServerConfig defaults = new ServerConfig(); + List<NamedFactory<Cipher>> avail = defaults.getCipherFactories(); + return filter(Cipher.class, avail, names); + } + + public static List<NamedFactory<KeyExchange>> buildKexAlgorithms(String names) { + ServerConfig defaults = new ServerConfig(); + List<NamedFactory<KeyExchange>> avail = defaults.getKeyExchangeFactories(); + + return filter(KeyExchange.class, avail, names); + } + + /** + * Simple helper class to avoid duplicating available configuration entries. + */ + private static final class ServerConfig extends ServerBuilder { + + public ServerConfig() { + this.build(); + } + + /** + * Just initializes the default configuration - does not create a + * server instance. + * + * @return always <code>null</code> + */ + @Override + public SshServer build() { + return this.build(true); + } + + /** + * Just initializes the default configuration - does not create a + * server instance. + * + * @return always <code>null</code> + */ + @Override + public SshServer build(boolean isFillWithDefaultValues) { + if (isFillWithDefaultValues) { + this.fillWithDefaultValues(); + } + return null; + } + + public List<NamedFactory<KeyExchange>> getKeyExchangeFactories() { + return keyExchangeFactories; + } + + public List<NamedFactory<Cipher>> getCipherFactories() { + return cipherFactories; + } + + public List<NamedFactory<Compression>> getCompressionFactories() { + return compressionFactories; + } + + public List<NamedFactory<Mac>> getMacFactories() { + return macFactories; } - return avail; } } http://git-wip-us.apache.org/repos/asf/karaf/blob/44323c27/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/SshUtilsTest.java ---------------------------------------------------------------------- diff --git a/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/SshUtilsTest.java b/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/SshUtilsTest.java new file mode 100644 index 0000000..5ddf77c --- /dev/null +++ b/shell/ssh/src/test/java/org/apache/karaf/shell/ssh/SshUtilsTest.java @@ -0,0 +1,103 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 + * + * http://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 org.apache.karaf.shell.ssh; + +import java.io.IOException; +import java.util.List; +import org.apache.sshd.common.cipher.Cipher; +import org.apache.sshd.common.kex.KeyExchange; +import org.apache.sshd.common.mac.Mac; +import org.apache.sshd.common.NamedFactory; + +import org.junit.Assert; +import org.junit.Test; + +public class SshUtilsTest { + + + @Test + public void testCiphersDefault() throws IOException { + // verify our default configuration... + String ciphers = "aes128-ctr,arcfour128,aes128-cbc,3des-cbc,blowfish-cbc"; + + List<NamedFactory<Cipher>> list = SshUtils.buildCiphers(ciphers); + + // verify that all configured ciphers are actually resolved... + for (String cipher : ciphers.split(",")) { + boolean found = false; + for (NamedFactory<Cipher> factory : list) { + if (factory.getName().equalsIgnoreCase(cipher)) { + found = true; + break; + } + } + + if (!found) { + Assert.fail("Configured default cipher '" + cipher + "' cannot be resolved"); + } + } + } + + @Test + public void testMacsDefault() throws IOException { + // verify our default configuration... + String macs = "hmac-sha2-512,hmac-sha2-256,hmac-sha1"; + + List<NamedFactory<Mac>> list = SshUtils.buildMacs(macs); + + // verify that all configured HMACs are actually resolved... + for (String mac : macs.split(",")) { + boolean found = false; + for (NamedFactory<Mac> factory : list) { + if (factory.getName().equalsIgnoreCase(mac)) { + found = true; + break; + } + } + + if (!found) { + Assert.fail("Configured default HMAC '" + mac + "' cannot be resolved"); + } + } + } + + @Test + public void testKexAlgorithmsDefault() throws IOException { + // verify our default configuration... + String kexAlgorithms = "diffie-hellman-group-exchange-sha256,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha1,diffie-hellman-group1-sha1"; + + List<NamedFactory<KeyExchange>> list = SshUtils.buildKexAlgorithms(kexAlgorithms); + + // verify that all configured key exchange algorithms are actually resolved... + for (String kex : kexAlgorithms.split(",")) { + boolean found = false; + for (NamedFactory<KeyExchange> factory : list) { + if (factory.getName().equalsIgnoreCase(kex)) { + found = true; + break; + } + } + + if (!found) { + Assert.fail("Configured default key exchange algorithm '" + kex + "' cannot be resolved"); + } + } + } +} +
