mirror of
				https://github.com/LamGC/Oracle-Sentry.git
				synced 2025-11-04 02:26:57 +00:00 
			
		
		
		
	feat(ssh): 支持设置首次连接认证策略.
支持更改首次连接认证策略, 以减少确认服务器密钥的工作量(尽管这可能导致后续连接不再安全).
This commit is contained in:
		@ -7,6 +7,8 @@ import org.apache.sshd.client.session.ClientSession;
 | 
				
			|||||||
import org.apache.sshd.common.config.keys.KeyUtils;
 | 
					import org.apache.sshd.common.config.keys.KeyUtils;
 | 
				
			||||||
import org.slf4j.Logger;
 | 
					import org.slf4j.Logger;
 | 
				
			||||||
import org.slf4j.LoggerFactory;
 | 
					import org.slf4j.LoggerFactory;
 | 
				
			||||||
 | 
					import org.springframework.beans.factory.annotation.Value;
 | 
				
			||||||
 | 
					import org.springframework.lang.NonNull;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import java.net.SocketAddress;
 | 
					import java.net.SocketAddress;
 | 
				
			||||||
import java.security.PublicKey;
 | 
					import java.security.PublicKey;
 | 
				
			||||||
@ -19,6 +21,10 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    private final static Logger log = LoggerFactory.getLogger(OracleInstanceServerKeyVerifier.class);
 | 
					    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 ComputeInstance instance;
 | 
				
			||||||
    private final SshAuthInfo info;
 | 
					    private final SshAuthInfo info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -34,7 +40,7 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier {
 | 
				
			|||||||
                    .verifyServerKey(clientSession, remoteAddress, serverKey);
 | 
					                    .verifyServerKey(clientSession, remoteAddress, serverKey);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            log.warn("首次连接实例 SSH, 需要用户确认服务器公钥是否可信...");
 | 
					            log.warn("首次连接实例 SSH, 需要用户确认服务器公钥是否可信...");
 | 
				
			||||||
            boolean result = confirm(remoteAddress, serverKey);
 | 
					            boolean result = usePolicyConfirm(remoteAddress, serverKey);
 | 
				
			||||||
            if (result) {
 | 
					            if (result) {
 | 
				
			||||||
                log.info("用户已确认服务器密钥可信, 将该密钥列入该实例下的信任密钥.");
 | 
					                log.info("用户已确认服务器密钥可信, 将该密钥列入该实例下的信任密钥.");
 | 
				
			||||||
                info.setServerKey(serverKey);
 | 
					                info.setServerKey(serverKey);
 | 
				
			||||||
@ -46,12 +52,26 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    public boolean confirm(SocketAddress address, PublicKey key) {
 | 
					    private boolean usePolicyConfirm(SocketAddress address, PublicKey serverKey) {
 | 
				
			||||||
        String fingerPrint = KeyUtils.getFingerPrint(key);
 | 
					        FirstConnectionPolicy policy;
 | 
				
			||||||
        log.warn("开始密钥认证流程... (InstanceId: {}, ServerAddress: {}, KeyFingerPrint: {})",
 | 
					        try {
 | 
				
			||||||
                instance.getInstanceId(), address, fingerPrint);
 | 
					            policy = FirstConnectionPolicy.valueOf(firstConnectionPolicy.toUpperCase());
 | 
				
			||||||
        Scanner scanner = new Scanner(System.in);
 | 
					        } catch (IllegalArgumentException e) {
 | 
				
			||||||
        log.info("""
 | 
					            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 安全性,请通过可信渠道获取服务器密钥指纹, 并与下列指纹比对:
 | 
					                    本次连接 SSH 为首次连接, 为确保 SSH 安全性,请通过可信渠道获取服务器密钥指纹, 并与下列指纹比对:
 | 
				
			||||||
                    实例 ID:{}
 | 
					                    实例 ID:{}
 | 
				
			||||||
                    实例名称:{}
 | 
					                    实例名称:{}
 | 
				
			||||||
@ -62,18 +82,40 @@ public class OracleInstanceServerKeyVerifier implements ServerKeyVerifier {
 | 
				
			|||||||
                                
 | 
					                                
 | 
				
			||||||
                    以上密钥指纹是否与服务器密钥指纹相同?如果指纹相同,对该密钥可信,请输入“Yes”,否则输入任意内容拒绝连接。
 | 
					                    以上密钥指纹是否与服务器密钥指纹相同?如果指纹相同,对该密钥可信,请输入“Yes”,否则输入任意内容拒绝连接。
 | 
				
			||||||
                    该密钥是否可信?(Yes/No):""",
 | 
					                    该密钥是否可信?(Yes/No):""",
 | 
				
			||||||
                instance.getInstanceId(),
 | 
					                    instance.getInstanceId(),
 | 
				
			||||||
                instance.getInstanceName(),
 | 
					                    instance.getInstanceName(),
 | 
				
			||||||
                address,
 | 
					                    address,
 | 
				
			||||||
                fingerPrint
 | 
					                    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);
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,8 +1,16 @@
 | 
				
			|||||||
oracle:
 | 
					oracle:
 | 
				
			||||||
  identity:
 | 
					  identity:
 | 
				
			||||||
 | 
					    # 身份配置文件的文件名匹配规则.
 | 
				
			||||||
    pattern: '.+\.oracle\.ini$'
 | 
					    pattern: '.+\.oracle\.ini$'
 | 
				
			||||||
    location: "./identity/"
 | 
					    # 身份配置文件夹路径.
 | 
				
			||||||
 | 
					    location: './identity/'
 | 
				
			||||||
  script:
 | 
					  script:
 | 
				
			||||||
    location: "./scripts/"
 | 
					    # 脚本文件夹路径.
 | 
				
			||||||
    ssh:
 | 
					    location: './scripts/'
 | 
				
			||||||
      identityPath: "./config/ssh.config.json"
 | 
					  ssh:
 | 
				
			||||||
 | 
					    # SSH 认证文件存储路径.
 | 
				
			||||||
 | 
					    identityPath: './config/ssh-auth.json'
 | 
				
			||||||
 | 
					    firstConnection:
 | 
				
			||||||
 | 
					      # 首次连接认证策略
 | 
				
			||||||
 | 
					      # 支持 inquiry(询问) accept(接受) reject(拒绝)
 | 
				
			||||||
 | 
					      authenticationPolicy: 'inquiry'
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user