NPS 内网穿透(一)TCP 隧道基本使用两三例


前段时间在 K8S 中部署服务,在不通过 Ingress、ALB 暴露服务的前提下,想在本机临时访问到服务的网站还挺不容易的。无意间了解到 nps 这个内网穿透工具,正是我需要的,测试使用示例记录如下

如果你之前使用过 frp 类的工具,那么 nps 也不容错过,值得体验。

部署 NPS 服务端

服务端部署在内部、外部都能访问到的中间节点

部署 nps 服务端,这里选择的是 yisier/nps,它是基于 ehang-io/nps 的 Fork 版本,后者已经三年没有更新。

下载二进制服务

$ wget https://github.com/yisier/nps/releases/download/v0.26.17/linux_amd64_server.tar.gz
$ tar xvf linux_amd64_server.tar.gz

修改配置:conf/nps.conf

我的服务器上的 80、443 已被占用,于是修改为 8000 和 8443 端口,另外 allow_ports 是映射时自动使用的端口范围,这个范围对应的是我在防火墙放行的端口区间,auth_key 需要设定为一个随机字符串。

http_proxy_port=8000
https_proxy_port=8443

auth_key=r4g8zyFVsHxTuwXM
#auth_crypt_key =1234567812345678

allow_ports=8100-8300

tls_enable=false

部署服务

# 安装
$ ./nps install

# 启动,停止和重启可用 stop 和 restart
$ nps start  

打开自带的 Web Conosle 页面,地址为 http://[服务器IP]:8081,默认用户名密码为 admin / 123

部署 nps 客户端

客户端部署在目标服务或能访问目标服务的节点上

客户端可以直接使用密钥连接到 NPS 服务端(无配置文件模式),也可以配置客户端 conf(取决于服务器是否支持),本例认证模式使用前者

首先在 Web 页面新增一个客户端,可以看到「唯一验证密钥」,客户端连接服务器的时候使用(通过 -vkey 参数指定)

https://github.com/yisier/nps/releases 下载二进制客户端

$ wget https://github.com/yisier/nps/releases/download/v0.26.17/linux_amd64_client.tar.gz
$ tar xvf linux_amd64_client.tar.gz

因为作者没有构建 macOS arm64 架构,所以我自己编译下电脑上使用的客户端,命令如下:

# 指定跟 server 相同的版本避免兼容性问题
$ git clone https://github.com/yisier/nps.git
$ cd nps
$ git checkout v0.26.17
$ go mod tidy
$ go build cmd/npc/npc.go

运行客户端

$ ./npc install -server=11.22.33.44:8024 -vkey=<唯一验证密钥>
$ ./npc start

输出

2024/02/06 14:49:57.341 [I] [npc.go:234]  the version of client is 0.26.17, the core version of client is 0.26.0,tls enable is false
2024/02/06 14:49:58.370 [I] [client.go:73]  Successful connection with server 11.22.33.44:8024

成功连接到服务器,部署完成,接下来通过 Web Console 配置规则,进行转发测试。

场景一:访问内网 Redis 服务

在上一小节,我的电脑已启动 NPS Client,来到 Web Console 控制台,左侧选择「TCP 隧道」,点击「新增」

重要的参数是「客户端ID」及「目标IP」,对于 nps 客户端来讲,我的电脑上的服务都是 127.0.0.1,所以目标 IP 填写 127.0.0.1:6379

创建后,展示信息如上,接下来通过外部服务器连接到 server 的端口,此处是随机到的 8229 进行测试(此处的端口随机范围即配置中的 allow_ports=8100-8300)

在能够访问 nps 服务端的机器上使用 Redis 客户端连接内网的 Redis

$ redis-cli -h 11.22.33.44 -p 8229
11.22.33.44:8229> set message "hello world!"
OK
(1.51s)

而后我们来到内网的电脑,可以看到 key 已被设置

这个就是最基本的内网穿透使用示例,通过服务器建立隧道,转发流量

同理,家庭内部的 NAS 等服务,也可以通过内网穿透暴露出来,从互联网上访问。

场景二:通过跳板机将 K8S 服务映射出来

环境描述,K8S 集群需要通过特定的跳板机访问,跳板机器上有 kubectl 命令

首先,创建 Memos 示例服务

apiVersion: v1
kind: Pod
metadata:
  name: memos-pod
  labels:
    app: memos
spec:
  containers:
  - name: memos-container
    image: neosmemo/memos:stable
    ports:
    - containerPort: 5230
---
apiVersion: v1
kind: Service
metadata:
  name: memos-service
spec:
  selector:
    app: memos
  ports:
  - protocol: TCP
    port: 5230
    targetPort: 5230

创建后,服务只能在集群内部访问,执行 kubectl 命令将端口映射到跳板机器,即在跳板机器上访问 127.0.0.1:5230 可以请求到 memos 服务。

$ kubectl port-forward --namespace default svc/memos-service 5230:5230
Forwarding from 127.0.0.1:5230 -> 5230
Forwarding from [::1]:5230 -> 5230

但是跳板机是 Linux 服务器,没有界面,我们期望通过本地电脑的浏览器访问网页时,就可以借助 nps 工具。

此处省略了跳板机部署 NPS Client 的步骤,注册后客户端ID 为 4,接下来在 Web Console 新增配置一条规则

这样,通过 http://11.22.33.44:8221 即可访问到 Kubernetes 集群中的 memos-service 服务

场景三:在 K8S 集群内部署 nps-clinet 作为代理

场景二通过跳板机 + port-forward 的方式,将 K8S 内的服务临时暴露,步骤较多,稍作优化,将 NPS Client 部署到 K8S 集群中,使用上就能更加便捷灵活

在集群内创建一个测试 Pod,用于安装 NPS Client

apiVersion: v1
kind: Pod
metadata:
  name: exec-pod
spec:
  containers:
    - args:
        - '-c'
        - while true; do sleep 100000; done
      command:
        - /bin/sh
      image: alpine:3.8
      imagePullPolicy: IfNotPresent
      name: alpine-container

安装 NPS Client 步骤略,注册后客户端ID 为 5

创建一个 TCP 隧道,在集群内服务间访问使用的是 Service,所以目标地址应填写 memos-service.default.svc.cluster.local:5230(这是集群内部 Memos 服务的地址,Memos 服务在场景二中创建)

这样,无需借助 port-forward,配置规则就能直接访问到集群内部服务,场景三中 NPS Client Pod 相当于一个 K8S 内服服务的跳板机。

对场景三进一步封装优化

场景三使用临时的 Pod,手动部署 NPS Client,当 Pod 重启则客户端就会丢失,不是一个稳定的部署方案,接下来构建 NPS Client 镜像,以云原生的方式在集群中部署 NPS Client。

FROM alpine:3.19

WORKDIR /app

# 设置npc客户端的环境变量
ENV NPC_VERSION=v0.26.17
ENV NPS_SERVER=
ENV NPS_VKEY=

# 安装必要的依赖
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
    apk update && apk add --no-cache ca-certificates

# https://github.com/yisier/nps/releases/download/v0.26.17/linux_amd64_client.tar.gz

RUN if [ "$TARGETPLATFORM" = "linux/arm64" ]; then \
        wget -O npc.tar.gz https://mirror.ghproxy.com/https://github.com/yisier/nps/releases/download/${NPC_VERSION}/linux_arm64_client.tar.gz; \
    else \
        wget -O npc.tar.gz https://mirror.ghproxy.com/https://github.com/yisier/nps/releases/download/${NPC_VERSION}/linux_amd64_client.tar.gz; \
fi && tar -xzf npc.tar.gz && rm -rf npc.tar.gz conf;

# 运行npc客户端
CMD ["sh", "-c", "./npc -server=$NPS_SERVER -vkey=$NPS_VKEY && tail -f /dev/null"]

多架构构建

docker buildx build -t kissbug8720/nps-client:0.0.1 --platform=linux/arm64,linux/amd64 --push .

使用 Docker 启动

$ docker run -d --rm --name nps-client -e NPS_SERVER=11.22.33.44:8024 -e NPS_VKEY=<your-client-vkey> kissbug8720/npc-client:0.0.1

使用 Kubernetes 启动

deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: npc
spec:
  selector:
    matchLabels:
      app: npc
  template:
    metadata:
      labels:
        app: npc
    spec:
      containers:
        - name: npc
          image: kissbug8720/nps-client:0.0.1
          env:
            - name: NPS_SERVER
              value: "11.22.33.44:8024"
            - name: NPS_VKEY
              value: "<your-client-vkey>"

最后放两张 NPS Console 的统计图表

前些天第一次听说 NPS 这个服务,测试后感觉功能强大、体验良好,值得后续进一步学习使用。