diff --git a/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java b/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java index 7b769f7..6029d47 100644 --- a/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java +++ b/src/main/java/net/lamgc/oracle/sentry/ApplicationInitiation.java @@ -107,6 +107,7 @@ class ApplicationInitiation { @PostConstruct @Order(1) + @SuppressWarnings({"MismatchedReadAndWriteOfArray", "RedundantOperationOnEmptyContainer"}) private void initialEnvironment() throws IOException { String[] directors = new String[] { "./config", @@ -115,7 +116,7 @@ class ApplicationInitiation { }; String[] files = new String[] { - sshIdentityPath + }; for (String directory : directors) { diff --git a/src/main/java/net/lamgc/oracle/sentry/Constants.java b/src/main/java/net/lamgc/oracle/sentry/Constants.java index 6053124..9bdc112 100644 --- a/src/main/java/net/lamgc/oracle/sentry/Constants.java +++ b/src/main/java/net/lamgc/oracle/sentry/Constants.java @@ -1,5 +1,7 @@ package net.lamgc.oracle.sentry; +import net.lamgc.oracle.sentry.oci.compute.ssh.ConfiguredForwardingFilter; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.lang.NonNull; import org.springframework.stereotype.Component; @@ -23,6 +25,8 @@ public final class Constants { @NonNull private String firstConnectionPolicy; + @Autowired + private ConfiguredForwardingFilter forwardingFilter; /** * 获取 SSH 首次连接策略. @@ -32,4 +36,12 @@ public final class Constants { public String getFirstConnectionPolicy() { return firstConnectionPolicy; } + + /** + * 获取已配置的转发过滤器. + * @return 返回转发过滤器. + */ + public ConfiguredForwardingFilter getForwardingFilter() { + return forwardingFilter; + } } diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/ConfiguredForwardingFilter.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/ConfiguredForwardingFilter.java new file mode 100644 index 0000000..1de5274 --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/ConfiguredForwardingFilter.java @@ -0,0 +1,45 @@ +package net.lamgc.oracle.sentry.oci.compute.ssh; + +import org.apache.sshd.common.session.Session; +import org.apache.sshd.common.util.net.SshdSocketAddress; +import org.apache.sshd.server.forward.ForwardingFilter; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +/** + * 已配置转发过滤器. + *

根据应用配置, 选择是否允许转发指定类型的流量. + * @author LamGC + */ +@Component("sentry.script.ssh.forwarding.filter") +public class ConfiguredForwardingFilter implements ForwardingFilter { + + @Value("oracle.ssh.forwarding.X11.enable") + private String x11Enabled; + + @Value("oracle.ssh.forwarding.tcp.enable") + private String tcpEnabled; + + @Value("oracle.ssh.forwarding.agent.enable") + private String agentEnabled; + + @Override + public boolean canForwardAgent(Session session, String requestType) { + return Boolean.parseBoolean(agentEnabled); + } + + @Override + public boolean canListen(SshdSocketAddress address, Session session) { + return Boolean.parseBoolean(tcpEnabled); + } + + @Override + public boolean canConnect(Type type, SshdSocketAddress address, Session session) { + return Boolean.parseBoolean(tcpEnabled); + } + + @Override + public boolean canForwardX11(Session session, String requestType) { + return Boolean.parseBoolean(x11Enabled); + } +} diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/InstanceSsh.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/InstanceSsh.java index 1a9c71c..9cfd37a 100644 --- a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/InstanceSsh.java +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/InstanceSsh.java @@ -1,6 +1,7 @@ package net.lamgc.oracle.sentry.oci.compute.ssh; import com.google.common.base.Strings; +import net.lamgc.oracle.sentry.Constants; import net.lamgc.oracle.sentry.oci.compute.ComputeInstance; import org.apache.sshd.client.SshClient; import org.apache.sshd.client.future.AuthFuture; @@ -40,6 +41,7 @@ public class InstanceSsh implements AutoCloseable { this.authInfo = Objects.requireNonNull(authInfo); sshClient = SshClient.setUpDefaultClient(); + sshClient.setForwardingFilter(Constants.instance.getForwardingFilter()); sshClient.setServerKeyVerifier(new OracleInstanceServerKeyVerifier(instance, authInfo)); if (authInfo instanceof PublicKeyAuthInfo info) { sshClient.setKeyIdentityProvider(new FileKeyPairProvider(info.getPrivateKeyPath().toPath())); diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/SshSession.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/SshSession.java index 0adf13c..5b81c95 100644 --- a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/SshSession.java +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/SshSession.java @@ -1,6 +1,8 @@ package net.lamgc.oracle.sentry.oci.compute.ssh; import org.apache.sshd.client.session.ClientSession; +import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker; +import org.apache.sshd.common.util.net.SshdSocketAddress; import org.apache.sshd.sftp.client.SftpClientFactory; import java.io.Closeable; @@ -45,6 +47,31 @@ public class SshSession implements Closeable { return new SftpSession(factory.createSftpClient(clientSession)); } + /** + * 创建本地 TCP 转发隧道. + *

该隧道为方向为 "本地->远端" (本地发起连接转发至远端端口). + * @return 返回 TCP 转发通道对象, 可获取通道信息和关闭通道. + */ + public TcpForwardingChannel createLocalTcpForwarding(int localPort, int remotePort) throws IOException { + ExplicitPortForwardingTracker tracker = clientSession + .createLocalPortForwardingTracker(localPort, new SshdSocketAddress(remotePort)); + return new TcpForwardingChannel(tracker); + } + + /** + * 创建远端 TCP 转发隧道. + *

该隧道为方向为 "本地<-远端" (远端服务器发起连接转发至本地端口). + * @param remotePort 远端监听端口号, 该端口为远端服务连接转发的端口号. + * @param localPort 本地连接端口号, 该端口为本地服务端的端口号. + * @return 返回 Tcp 转发通道对象, 用于管理转发通道. + */ + public TcpForwardingChannel createRemoteTcpForwarding(int remotePort, int localPort) throws IOException { + ExplicitPortForwardingTracker tracker = + clientSession.createRemotePortForwardingTracker( + new SshdSocketAddress(remotePort), new SshdSocketAddress(localPort)); + return new TcpForwardingChannel(tracker); + } + /** * 关闭 SSH 连接会话, 该连接会话所属的其他会话将会一同被关闭. * @throws IOException 关闭失败时抛出异常, diff --git a/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/TcpForwardingChannel.java b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/TcpForwardingChannel.java new file mode 100644 index 0000000..f9ff9ef --- /dev/null +++ b/src/main/java/net/lamgc/oracle/sentry/oci/compute/ssh/TcpForwardingChannel.java @@ -0,0 +1,63 @@ +package net.lamgc.oracle.sentry.oci.compute.ssh; + +import org.apache.sshd.client.session.forward.ExplicitPortForwardingTracker; +import org.apache.sshd.common.util.net.SshdSocketAddress; + +/** + * TCP 隧道. + *

可通过该对象管理隧道. + * @author LamGC + */ +public class TcpForwardingChannel implements AutoCloseable { + + private final ExplicitPortForwardingTracker tracker; + + TcpForwardingChannel(ExplicitPortForwardingTracker tracker) { + this.tracker = tracker; + } + + /** + * 是否为本地转发. + * @return 如果是本地转发, 则为 {@code true}, 否则返回 {@code false}, 代表远端转发. + */ + public boolean isLocalForwarding() { + return tracker.isLocalForwarding(); + } + + /** + * 隧道是否已打开. + * @return 如果已经打开, 返回 {@code true}, 否则返回 {@code false} 表示已关闭. + */ + public boolean isOpen() { + return tracker.isOpen(); + } + + /** + * 获取本地地址. + * @return 获取本地连接地址. + */ + public SshdSocketAddress getLocalAddress() { + return tracker.getLocalAddress(); + } + + /** + * 获取远端地址. + * @return 获取远端地址. + */ + public SshdSocketAddress getRemoteAddress() { + return tracker.getRemoteAddress(); + } + + /** + * 获取监听绑定地址. + * @return 返回监听绑定地址. + */ + public SshdSocketAddress getBoundAddress() { + return tracker.getBoundAddress(); + } + + @Override + public void close() throws Exception { + tracker.close(); + } +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 0d857b5..ba5cbff 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -14,3 +14,14 @@ oracle: # 首次连接认证策略 # 支持 confirm(询问) accept(接受) reject(拒绝) authenticationPolicy: 'confirm' + # SSH 转发设定 + forwarding: + # X11 转发 + X11: + enable: false + # SSH-Agent 转发 + agent: + enable: false + # TCP 转发 + tcp: + enable: true