From a83c09a7871ee10cd2aebaf014ec38af483ad0d4 Mon Sep 17 00:00:00 2001 From: LamGC Date: Sun, 15 Aug 2021 18:45:26 +0800 Subject: [PATCH] =?UTF-8?q?feat(ssh):=20=E6=94=AF=E6=8C=81=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E9=A6=96=E6=AC=A1=E8=BF=9E=E6=8E=A5=E8=AE=A4=E8=AF=81?= =?UTF-8?q?=E7=AD=96=E7=95=A5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 支持更改首次连接认证策略, 以减少确认服务器密钥的工作量(尽管这可能导致后续连接不再安全). --- .../ssh/OracleInstanceServerKeyVerifier.java | 78 ++++++++++++++----- src/main/resources/application.yml | 16 +++- 2 files changed, 72 insertions(+), 22 deletions(-) diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/OracleInstanceServerKeyVerifier.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/OracleInstanceServerKeyVerifier.java index eb481ec..99e7bf9 100644 --- a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/OracleInstanceServerKeyVerifier.java +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/OracleInstanceServerKeyVerifier.java @@ -7,6 +7,8 @@ import org.apache.sshd.client.session.ClientSession; import org.apache.sshd.common.config.keys.KeyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.lang.NonNull; import java.net.SocketAddress; import java.security.PublicKey; @@ -19,6 +21,10 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier { private final static Logger log = LoggerFactory.getLogger(OracleInstanceServerKeyVerifier.class); + @Value("${oracle.ssh.firstConnection.authenticationPolicy}") + @NonNull + private static String firstConnectionPolicy; + private final ComputeInstance instance; private final SshAuthInfo info; @@ -34,7 +40,7 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier { .verifyServerKey(clientSession, remoteAddress, serverKey); } else { log.warn("首次连接实例 SSH, 需要用户确认服务器公钥是否可信..."); - boolean result = confirm(remoteAddress, serverKey); + boolean result = usePolicyConfirm(remoteAddress, serverKey); if (result) { log.info("用户已确认服务器密钥可信, 将该密钥列入该实例下的信任密钥."); info.setServerKey(serverKey); @@ -46,12 +52,26 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier { } } - public boolean confirm(SocketAddress address, PublicKey key) { - String fingerPrint = KeyUtils.getFingerPrint(key); - log.warn("开始密钥认证流程... (InstanceId: {}, ServerAddress: {}, KeyFingerPrint: {})", - instance.getInstanceId(), address, fingerPrint); - Scanner scanner = new Scanner(System.in); - log.info(""" + private boolean usePolicyConfirm(SocketAddress address, PublicKey serverKey) { + FirstConnectionPolicy policy; + try { + policy = FirstConnectionPolicy.valueOf(firstConnectionPolicy.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("Unsupported policy: " + firstConnectionPolicy); + } + return policy.confirmFunction.confirm(this.instance, address, serverKey); + } + + @SuppressWarnings("unused") + private enum FirstConnectionPolicy { + ACCEPT((instance, address, key) -> true), + REJECT((instance, address, key) -> false), + CONFIRM((instance, address, key) -> { + String fingerPrint = KeyUtils.getFingerPrint(key); + log.warn("开始密钥认证流程... (InstanceId: {}, ServerAddress: {}, KeyFingerPrint: {})", + instance.getInstanceId(), address, fingerPrint); + Scanner scanner = new Scanner(System.in); + log.info(""" 本次连接 SSH 为首次连接, 为确保 SSH 安全性,请通过可信渠道获取服务器密钥指纹, 并与下列指纹比对: 实例 ID:{} 实例名称:{} @@ -62,18 +82,40 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier { 以上密钥指纹是否与服务器密钥指纹相同?如果指纹相同,对该密钥可信,请输入“Yes”,否则输入任意内容拒绝连接。 该密钥是否可信?(Yes/No):""", - instance.getInstanceId(), - instance.getInstanceName(), - address, - fingerPrint - ); + instance.getInstanceId(), + instance.getInstanceName(), + address, + fingerPrint + ); + + do { + if (scanner.hasNextLine()) { + String input = scanner.nextLine(); + return "yes".trim().equalsIgnoreCase(input); + } + } while (true); + }); + + private final PublicKeyConfirm confirmFunction; + + + FirstConnectionPolicy(PublicKeyConfirm confirmFunction) { + this.confirmFunction = confirmFunction; + } + } + + @FunctionalInterface + private interface PublicKeyConfirm { + + /** + * 确认密钥. + * @param instance 待认证的服务器所属计算实例. + * @param address 远程地址. + * @param serverKey 服务器密钥. + * @return 如果通过, 返回 {@code true}. + */ + boolean confirm(ComputeInstance instance, SocketAddress address, PublicKey serverKey); - do { - if (scanner.hasNextLine()) { - String input = scanner.nextLine(); - return "yes".trim().equalsIgnoreCase(input); - } - } while (true); } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 17c7f41..307bb79 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,8 +1,16 @@ oracle: identity: + # 身份配置文件的文件名匹配规则. pattern: '.+\.oracle\.ini$' - location: "./identity/" + # 身份配置文件夹路径. + location: './identity/' script: - location: "./scripts/" - ssh: - identityPath: "./config/ssh.config.json" + # 脚本文件夹路径. + location: './scripts/' + ssh: + # SSH 认证文件存储路径. + identityPath: './config/ssh-auth.json' + firstConnection: + # 首次连接认证策略 + # 支持 inquiry(询问) accept(接受) reject(拒绝) + authenticationPolicy: 'inquiry'