X-Git-Url: https://git.argeo.org/?a=blobdiff_plain;f=org.argeo.cms.lib.sshd%2Fsrc%2Forg%2Fargeo%2Fcms%2Fssh%2FCmsSshServer.java;fp=org.argeo.cms.lib.sshd%2Fsrc%2Forg%2Fargeo%2Fcms%2Fssh%2FCmsSshServer.java;h=f5609a37d5f70e2786153c567f96e0d0759a5684;hb=76a8481ee26616efa0fa59838a93bcad937b2692;hp=ab62654f23a32f521171904b42a394c278070317;hpb=921b6cf95420aafa6b9cebe107c927e8062ed865;p=lgpl%2Fargeo-commons.git diff --git a/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java b/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java index ab62654f2..f5609a37d 100644 --- a/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java +++ b/org.argeo.cms.lib.sshd/src/org/argeo/cms/ssh/CmsSshServer.java @@ -1,16 +1,36 @@ package org.argeo.cms.ssh; import java.io.IOException; +import java.io.InputStream; +import java.io.Writer; +import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardOpenOption; +import java.nio.file.attribute.PosixFilePermission; +import java.security.KeyPair; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.UnrecoverableKeyException; +import java.security.cert.CertificateException; import java.util.Collections; +import java.util.HashSet; +import java.util.Set; +import org.apache.sshd.common.config.keys.PublicKeyEntry; import org.apache.sshd.common.forward.PortForwardingEventListener; +import org.apache.sshd.common.keyprovider.KeyPairProvider; import org.apache.sshd.common.session.Session; import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.scp.server.ScpCommandFactory; import org.apache.sshd.server.SshServer; import org.apache.sshd.server.auth.gss.GSSAuthenticator; +import org.apache.sshd.server.config.keys.AuthorizedKeysAuthenticator; +import org.apache.sshd.server.config.keys.DefaultAuthorizedKeysAuthenticator; import org.apache.sshd.server.forward.AcceptAllForwardingFilter; import org.apache.sshd.server.jaas.JaasPasswordAuthenticator; import org.apache.sshd.server.keyprovider.SimpleGeneratorHostKeyProvider; @@ -25,7 +45,6 @@ import org.argeo.cms.CmsSshd; public class CmsSshServer implements CmsSshd { private final static CmsLog log = CmsLog.getLog(CmsSshServer.class); - private static final String DEFAULT_SSH_HOST_KEY_PATH = CmsConstants.NODE + '/' + CmsConstants.NODE + ".ser"; private CmsState cmsState; private SshServer sshd = null; @@ -41,30 +60,50 @@ public class CmsSshServer implements CmsSshd { host = cmsState.getDeployProperty(CmsDeployProperty.HOST.getProperty()); - Path hostKeyPath = cmsState.getDataPath(DEFAULT_SSH_HOST_KEY_PATH); + KeyPair nodeKeyPair = loadNodeKeyPair(); try { + // authorized keys + String authorizedKeysStr = cmsState.getDeployProperty(CmsDeployProperty.SSHD_AUTHORIZEDKEYS.getProperty()); + Path authorizedKeysPath = authorizedKeysStr != null ? Paths.get(authorizedKeysStr) + : AuthorizedKeysAuthenticator.getDefaultAuthorizedKeysFile(); + if (authorizedKeysStr != null && !Files.exists(authorizedKeysPath)) { + Files.createFile(authorizedKeysPath); + Set posixPermissions = new HashSet<>(); + posixPermissions.add(PosixFilePermission.OWNER_READ); + posixPermissions.add(PosixFilePermission.OWNER_WRITE); + Files.setPosixFilePermissions(authorizedKeysPath, posixPermissions); + + if (nodeKeyPair != null) + try { + String openSsshPublicKey = PublicKeyEntry.toString(nodeKeyPair.getPublic()); + try (Writer writer = Files.newBufferedWriter(authorizedKeysPath, StandardCharsets.US_ASCII, + StandardOpenOption.APPEND)) { + writer.write(openSsshPublicKey); + } + } catch (IOException e) { + log.error("Cannot add node public key to SSH authorized keys", e); + } + } + + // create server sshd = SshServer.setUpDefaultServer(); sshd.setPort(port); if (host != null) sshd.setHost(host); - if (hostKeyPath == null) - throw new IllegalStateException("An SSH server key must be set"); - sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKeyPath)); - // sshd.setShellFactory(new ProcessShellFactory(new String[] { "/bin/sh", "-i", - // "-l" })); -// String[] shellCommand = OS.LOCAL.getDefaultShellCommand(); - // FIXME transfer args -// sshd.setShellFactory(new ProcessShellFactory(shellCommand)); - sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE); - sshd.setCommandFactory(new ScpCommandFactory()); + + // host key + if (nodeKeyPair != null) { + sshd.setKeyPairProvider(KeyPairProvider.wrap(nodeKeyPair)); + } else { + Path hostKeyPath = cmsState.getDataPath(DEFAULT_SSH_HOST_KEY_PATH); + if (hostKeyPath == null) // TODO deal with no data area? + throw new IllegalStateException("An SSH server key must be set"); + sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider(hostKeyPath)); + } // tunnels sshd.setForwardingFilter(AcceptAllForwardingFilter.INSTANCE); - // sshd.setForwardingFilter(ForwardingFilter.asForwardingFilter(null, null, - // TcpForwardingFilter.DEFAULT)); - // sshd.setForwarderFactory(DefaultForwarderFactory.INSTANCE); -// TcpForwardingFilter tcpForwardingFilter = sshd.getTcpForwardingFilter(); sshd.addPortForwardingEventListener(new PortForwardingEventListener() { @Override @@ -94,22 +133,38 @@ public class CmsSshServer implements CmsSshd { }); // Authentication - // sshd.setPublickeyAuthenticator(new DefaultAuthorizedKeysAuthenticator(true)); - sshd.setPublickeyAuthenticator(null); + // FIXME use strict, set proper permissions, etc. + sshd.setPublickeyAuthenticator( + new DefaultAuthorizedKeysAuthenticator("user.name", authorizedKeysPath, true)); + // sshd.setPublickeyAuthenticator(null); // sshd.setKeyboardInteractiveAuthenticator(null); JaasPasswordAuthenticator jaasPasswordAuthenticator = new JaasPasswordAuthenticator(); jaasPasswordAuthenticator.setDomain(CmsAuth.NODE.getLoginContextName()); sshd.setPasswordAuthenticator(jaasPasswordAuthenticator); - Path krb5keyTab = cmsState.getDataPath("node/krb5.keytab"); - if (Files.exists(krb5keyTab)) { - // FIXME experimental - GSSAuthenticator gssAuthenticator = new GSSAuthenticator(); - gssAuthenticator.setKeytabFile(cmsState.getDataPath("node/krb5.keytab").toString()); - gssAuthenticator.setServicePrincipalName("HTTP@" + host); - sshd.setGSSAuthenticator(gssAuthenticator); + boolean gssApi = false; + if (gssApi) { + Path krb5keyTab = cmsState.getDataPath("private/krb5.keytab"); + if (Files.exists(krb5keyTab)) { + // FIXME experimental + GSSAuthenticator gssAuthenticator = new GSSAuthenticator(); + gssAuthenticator.setKeytabFile(krb5keyTab.toString()); + gssAuthenticator.setServicePrincipalName("HTTP@" + host); + sshd.setGSSAuthenticator(gssAuthenticator); + } } + // shell + // TODO make it configurable + sshd.setShellFactory(InteractiveProcessShellFactory.INSTANCE); +// String[] shellCommand = OS.LOCAL.getDefaultShellCommand(); +// StringJoiner command = new StringJoiner(" "); +// for (String str : shellCommand) { +// command.add(str); +// } +// sshd.setShellFactory(new ProcessShellFactory(command.toString(), shellCommand)); + sshd.setCommandFactory(new ScpCommandFactory()); + // SFTP sshd.setSubsystemFactories(Collections.singletonList(new SftpSubsystemFactory())); @@ -135,6 +190,27 @@ public class CmsSshServer implements CmsSshd { } + protected KeyPair loadNodeKeyPair() { + try { + char[] keyStorePassword = cmsState.getDeployProperty(CmsDeployProperty.SSL_PASSWORD.getProperty()) + .toCharArray(); + Path keyStorePath = Paths.get(cmsState.getDeployProperty(CmsDeployProperty.SSL_KEYSTORE.getProperty())); + String keyStoreType = cmsState.getDeployProperty(CmsDeployProperty.SSL_KEYSTORETYPE.getProperty()); + + KeyStore store = KeyStore.getInstance(keyStoreType, "SunJSSE"); + try (InputStream fis = Files.newInputStream(keyStorePath)) { + store.load(fis, keyStorePassword); + } + return new KeyPair(store.getCertificate(CmsConstants.NODE).getPublicKey(), + (PrivateKey) store.getKey(CmsConstants.NODE, keyStorePassword)); + } catch (IOException | KeyStoreException | NoSuchProviderException | NoSuchAlgorithmException + | CertificateException | IllegalArgumentException | UnrecoverableKeyException e) { + log.error("Cannot add node public key to SSH authorized keys", e); + return null; + } + + } + public void setCmsState(CmsState cmsState) { this.cmsState = cmsState; }