SSH

Cloudflare Tunnel 使用笔记

Cloudflare 自不用多说,Tunnel 是 Cloudflare 提供的一项功能,可以将本地的服务通过 Cloudflare 的网络暴露到公网,这样就可以实现内网穿透,同时还可以通过 Cloudflare 的网络加速服务,提高访问速度。

初识 Cloudflare Tunnel #

最开始接触 Cloudflare Tunnel 是在 Twitter 上看到一个项目cloudflare-tunnel-ingress-controller ,这个项目是一个 Kubernetes 的 Ingress Controller,可以将 Kubernetes 中的服务通过 Cloudflare Tunnel 暴露到公网,这样就可以实现内网穿透,也就是说局域网搭建的服务可以通过 Cloudflare 的网络暴露到公网。

熟悉内网穿透的小伙伴,应该对这中东西很熟悉,也没什么好说的。

前提 #

  • 一个 Cloudflare 账号
  • 一个域名

cloudfalred #

Mac 上可以通过 brew install cloudflared 安装,安装完成后,可以通过 cloudflared -v 查看版本。

$ cloudflared -v
cloudflared version 2023.7.3 (built 2023-07-25T20:51:49Z)

参考 cloudflared 官方文档 安装。

$ cloudflared login

使用 #

我的使用场景除了最开始提到的 Kubernetes Ingress Controller 之外,还有一个就是将局域网内的开发机通过 Cloudflare Tunnel 暴露到公网,方便远程开发。

...

SSH Tunnel小工具

背景 #

生产环境数据库不允许直接访问,但是又经常有需要直接操作数据库的需求😂。先不说合不合理,背景就是这个背景,因此只能通过跳板机来连接数据库,一(就)般(我)来(而)说(言)会使用ssh隧道,就轻松能解决这个问题,然鹅,事情并不简单。这里陈述一下:

  1. 生产环境数据库不让直接访问;
  2. 跳板机上没有公钥,没有权限;
  3. 我一次可能需要开3+个隧道才能启动服务【敲重点】

解决 #

本着“我不造轮子,谁来造轮子”的想法,这里就造一个小轮子:用Go来实现SSH隧道多开,并支持配置。成果预览:

原理简要分析 #

如果代理原理有点了解,这里的原理差不多是一样的:Local <-> SSH tunnel <-> Remote Server,对于隧道来说把Local的请求传给Remote, 把Remote的响应告诉Local。直接上代码:

// Start .
// TODO: support random port by using localhost:0
func (tunnel *SSHTunnel) Start() error {
    listener, err := net.Listen("tcp", tunnel.LocalAddr)
    if err != nil {
        return err
    }
    defer listener.Close()
    // tunnel.Local.Port = listener.Addr().(*net.TCPAddr).Port
    for {
        conn, err := listener.Accept()
        if err != nil {
            return err
        }
        logger.Infof(tunnel.name() + " accepted connection")
        go tunnel.forward(conn)
    }
}

// 创建隧道并传递消息,分别有两个端点,一个是本地隧道口,另一个是远程服务器上的隧道口
func (tunnel *SSHTunnel) forward(localConn net.Conn) {
    // 创建本地到跳板机的SSH连接
    serverSSHClient, err := ssh.Dial("tcp", tunnel.ServerAddr, tunnel.SSHConfig)
    if err != nil {
        logger.Infof(tunnel.name()+" server dial error: %s", err)
        return
    }
    logger.Infof(tunnel.name()+" connected to server=%s (1 of 2)", tunnel.ServerAddr)
    // 创建跳板机到远程服务器的连接
    remoteConn, err := serverSSHClient.Dial("tcp", tunnel.RemoteAddr)
    if err != nil {
        logger.Infof(tunnel.name()+" remote dial error: %s", err)
        return
    }
    logger.Infof(tunnel.name()+" connected to remote=%s (2 of 2)", tunnel.RemoteAddr)

    copyConn := func(writer, reader net.Conn) {
        _, err := io.Copy(writer, reader)
        if err != nil {
            logger.Infof(tunnel.name()+" io.Copy error: %s", err)
        }
    }

    // local(w) => 远程(r)
    go copyConn(localConn, remoteConn)
    // 远程(w) => 本地(r)
    go copyConn(remoteConn, localConn)
}

代码中需要注意的是:

...

访问量 访客数