跳转至

SSH+GithubAction逆天的GitHub网站访问解决方案,还可以解决K8s和云VPC网络访问内部IP服务问题

Github 是全球最大的代码协作平台与社区,绝大数的开源项目都会考虑在 Github 上开源其代码。

但是由于一些原因导致在国内访问Github时网络连接不稳定,需要以企业为单位申请海外出口专线才能获得较为稳定的网络连接。关于原因可以通过这边文章进行了解《Github是否在调试和预演封禁中国IP用户?我们又该怎样应对?》

对于普通开发者或者中小企业,不具备申请海外专线的申请的条件,该如何满足日常的开源项目协作等需求呢?

本文通过对 Github Action 运行机制进行分析,结合 SSH 的高阶使用姿势,以及个人多年的运维经验和工具,设计出一种稳定访问 Github 方案(该方案仅推荐访问Github站点用于开源协作和学习使用)。

其他类似访问一个隔离网络中服务场景也可以解决,比如

  • 在 Kubernetes 集群中使用 Overlay 网络时,直接访问 POD IP 服务场景。
  • 在云平台的 VPC 网络环境中,直接访问私有IP节点上的服务场景。

方案的唯一成本就是一台云国内主机费用,可以在 https://cncfstack.com/hui-cloud/ 获取国内各云厂商的优惠券。如阿里云可以 99元/年获取2核2G-3M固定带宽,40G云盘的ECS虚拟机。

方案原理

分析 Github Action 可以知道,Github Action 在执行流水线线时,可以使用 Ubuntu 系统作为执行环境,规格是 4C8G的虚拟机环境。

既然Github Action是个虚拟机,就可以执行 SSH 远程登录,包括登录国内的具有公网IP地址的公有云的服务器。

而SSH有3个高级功能,可以与Github Action联合实现本文方案,分别是本地端口转发、远程端口转发和动态端口转发功能。

  • 本地端口转发:本地端口转发用于将本地机器的一个端口转发到远程服务器的某个端口,然后远程服务器再将数据转发到目标主机的目标端口。这对于访问位于防火墙后面的服务非常有用。
  • 远程端口转发:远程端口转发则相反,它允许远程服务器上的某个端口被转发到本地机器的某个端口,然后本地机器再将数据转发到目标主机的目标端口。这适用于当你需要让外部访问本地的服务时使用。
  • 动态端口转发:是通过SSH建立Socket端口,所有转发给该端口的请求都会由 sshd 服务代理请求。

方案构建的链路为:

  1. Github Action运行SSH动态端口,用于转发所有请求。
  2. Github Action远程SSH登录到国内的一台虚拟机上,将步骤1的动态端口映射到国内虚拟机上。
  3. (可选)将国内虚拟机的端口映射到本地电脑
  4. 用户浏览器配置 Socket5 代理,地址为本地地址和端口。

如图示意:

这个方案完整的理解起来可能比较复杂,可以直接跳过原理理解,搭建环境即可。

方案步骤

步骤一:Github Action运行项目创建

Github Action运行在某个项目仓库中,对于用户私有的仓库,Github 每月有 2000分钟(约33小时)的 Action 执行配额,可能无法满足一个月使用要求。

推荐创建一个开源的仓库,因为开源的仓库没有 Action 执行配额限制。开源仓库的安全问题在步骤三中解决。

步骤二:添加 Action 文件

Github会自动识别并执行 .github/workflows/ 目录中的 Action 文件,可以通过命令行创建或通过页面创建。

方法1:通过命令行创建

将项目通过ssh协议克隆到本地,进入到项目的根目录创建文件。这里建个空文件即可,内容会在后续的步骤中填充。

git clone git@github.com:example/mynetrepo.git
cd mynetrepo
mkdir  .github/workflows -p
touch .github/workflows/blank.yml

方法2:通过页面创建

通过页面创建 Action文件,选择推荐的简单工作流配置即可,页面路径如图所示:

步骤三:添加国内主机的SSH私钥

由于需要Action执行机器使用 SSH 远程免密登录到国内的云服务器,因此需要创建一个 SSH 秘钥对,也可以使用已经存在的秘钥对。

SSH秘钥对创建命令

ssh-keygen -t rsa -f ./vmsshkey -P ''

将公钥添加到虚拟机认证文件中,使私钥可以远程SSH免密登录

cat vmsshkey.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys

将私钥文件添加到Github的 Secret 中,步骤路径如图:

添加为 Secret 的文件后无法再次查看内容,流水线中日志也不会显示,只可用于 Action 流水线执行,是安全的。

这里添加的 Secret 配置文件名称需要记录一下,下一步的流水线配置也文件中需要基于文件名称进行引用,假设文件名称为:vmsshkey

步骤四:填写 Action

修改 .github/workflows/blank.yml 文件,内容填充如下

name: mynetrepo
on:
  push:
    branches: [ "main" ]
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run a multi-line script
        run: |
          ssh-keygen  -t rsa -f ./newsshkey -P ''
          mkdir -p  ~/.ssh
          cat newsshkey.pub >> ~/.ssh/authorized_keys
          chmod 600 ~/.ssh/authorized_keys
          ssh  -N -f -g -o ServerAliveInterval=60 -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i ./newsshkey -D 9999 localhost 
          echo "${{ secrets.VMSSHKEY }}" > ./vmsshkey
          chmod 400 ./vmsshkey
          ssh -p 22 -i ./vmsshkey  -N -f -g -o ServerAliveInterval=60 -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -R 9999:localhost:9999  root@1.2.3.4
          num=`cat push.event`
          for i in `seq $num`
          do
            echo $i
            sleep 1
          done
          exit 0

该文件中,需要修改虚拟机的 IP地址,将 1.2.3.4 地址替换成实际IP。

为了便于修改时长,可以在项目的根目录中创建一个 push.event 文件填写秒数的文件,如:3600。

[root@iZbp15iku4Z mynetrepo]# ls -lha
total 24K
drwxr-xr-x  4 root root 4.0K May 17 18:33 .
drwxr-xr-x 10 root root 4.0K May  9 14:53 ..
drwxr-xr-x  8 root root 4.0K May 17 15:59 .git
drwxr-xr-x  3 root root 4.0K Mar 30 15:47 .github
-rw-r--r--  1 root root    5 May 17 18:33 push.event
-rw-r--r--  1 root root    9 Mar 30 15:46 README.md
[root@iZbp15iku4Z mynetrepo]# cat push.event 
3600

这个是流水线执行的时间,单位是秒,这段时间内都可以基于这种方式访问 Github ,具体时长可以根据个人需求修改。任意内容修改后执行 push 就会自动执行了。

步骤五(可选):终端本地端口转发

到步骤四其实已经可以配置浏览器通过国内虚拟机进行Github访问了,但是需要虚拟机的安全组或防火墙在公网上开放端口。

由于该端口没有认证,可能会导致端口被互联网上的其他用户访问,有可能用于访问Github之外的网站,存在很大的安全隐患。

因此,建议不在公网上开放端口,可以再添加一层本地端口转发,将虚拟机的端口映射到本地 127.0.0.1 网络上,就只能自己访问使用了。

ssh   -N -f -g -o ServerAliveInterval=60  -L 9999:localhost:9999 root@1.2.3.4

这里也可以优化打通本地到虚拟机的免密登录。

步骤六:浏览器/操作系统配置代理访问

可以给操作系统、浏览器、微信等任何支持Socket5代理的应用使用该网络连接。

Windows 系统代理配置

Windows 系统代理配置

Windows 系统的代理能够给整个系统内所有的应用提供代理功能。

Chrome 代理插件 SwitchyOmega

对于浏览器访问互联网比较常见,如果不想修改系统全局的代理配置,可以通过添加代理插件的访问目标网络。在 Chrome 浏览器中我习惯使用 SwitchyOmega 这个代理插件。也可以直接使用夸克浏览器,其插件市场中直接下载该插件。

官方插件获取地址:

https://getcrx.cn/https://github.com/FelisCatus/SwitchyOmega/releases

Chrome 代理插件 SwitchyOmega

在使用时,推荐使用 “auto switch” 模式,将无法访问的网站URL通过代理访问,其他可访问的网站通过“直接连接”访问。

方案注意事项

注意事项也是一些经验和踩过的坑

  • 第一次 Github https 页面打开可能经常失败,可以多次刷新浏览器、更换网络或使用手机移动热点网络来进行初始配置。Github页面打开后最好快速一次性配置完成,过段时间可能又会断开。
  • 经过尝试经验 Github 项目的 ssh 协议是相对稳定的,在 clone 代码时尽量使用 ssh 协议,而不是默认的 https 协议。
  • 私有的仓库有2000分钟Action配额限制,开源的仓库没有配额限制。
  • 开源的仓库中不能直接暴露SSH私钥,需要通过 Github Action Secret 来保存使用。
  • 考虑到一个流水线一直执行不完成,可能会被Github判断为异常强制终止,因此添加了计时器,建议每次连接不超过12小时。
  • 对于国内的虚拟机如果跳过【步骤五(可选):终端本地端口转发】直接使用公网IP进行代理时,需要将虚拟机的 sshd 配置文件 /etc/ssh/sshd_config 中 GatewayPorts 设置为 yes。默认为no只允许虚拟机本地访问。

Action脚本解析

前面这段是Github Action的配置语法,基于Ubuntu系统环境。

name: mynetrepo
on:
  push:
    branches: [ "main" ]
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Run a multi-line script
        run: |

该流水线配置会监听这个仓库的所有 push 事件,只要有push就会执行这个流水线。所以在后续使用时,可以修改任意内容,然后执行push 即可

git add . && git commit -m "update" && git push

下面这段就是多行的 shell 脚本,具体每行的作用添加了注释

# 新创建一对SSH密钥,用于Action本地动态端口转发的SSH免密本地登录
ssh-keygen  -t rsa -f ./newsshkey -P ''
# 由于Github Action默认没有密钥和目录,需要创建默认的ssh用户目录
mkdir -p  ~/.ssh
# 这一步直接将公钥导入到认证文件,替代交互式的 ssh-copy-id 命令
cat newsshkey.pub >> ~/.ssh/authorized_keys
chmod 600 ~/.ssh/authorized_keys
# 启动动态端口转发命令,在Action机器本地监听9999端口
ssh  -N -f -g -o ServerAliveInterval=60 -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -i ./newsshkey -D 9999 localhost 


# 将虚拟机私钥文件的Secret配置导出为本地的文件,并修改权限为400
echo "${{ secrets.VMSSHKEY }}" > ./vmsshkey
chmod 400 ./vmsshkey
# 启动远程端口转发,将虚拟机上的9999端口请求转发到Action机器的9999端口
ssh -p 22 -i ./vmsshkey  -N -f -g -o ServerAliveInterval=60 -o "StrictHostKeyChecking=no" -o "UserKnownHostsFile=/dev/null" -R 9999:localhost:9999  root@1.2.3.4


# 设计一个计算器,让这个流水线执行多长时间。
# 为了便于修改时长,可以在项目的根目录中创建一个文件 push.event 填写秒数的文件,如:3600,
num=`cat push.event`
for i in `seq $num`
do
  echo $i
  sleep 1
done
# 计时器时间执行完成后,退出整个任务
exit 0

总结思考

该方案除了可以解决Github网站访问问题外,其他网站访问需要自行探索。

另外,其他类似访问一个隔离网络中服务场景也可以解决,比如

  • 在 Kubernetes 集群中使用 Overlay 网络时,直接访问 POD IP 服务场景。
  • 在云平台的 VPC 网络环境中,直接访问私有IP节点上的服务场景。

这两种场景只需要用到 SSH 的动态端口转发+本地/远程端口转发的功能即可解决。