Featured image of post 手把手初始化GCP云主机并快速完成MCP Server的配置

手把手初始化GCP云主机并快速完成MCP Server的配置

前情提要

之前发布的文章:“手把手带你薅 Gemini 付费 API 与 Google 免费服务器” 被人投诉下架了。

这里把文章链接放出来,感兴趣的同学可以去薅一下——

前提:需要先开通自己帐号的 google pro服务

详细步骤:https://blog.cba.nxlan.cn/p/setup_gemini_api

具体怎么开通、初始化机器、登录的过程,上面文章中已经说得很清楚了。

只是还剩下一个任务,也就是本文的主题:

使用1Panel 申请https 免费证书,搭建MCP server 供Agent使用。

肝了两天,文章有点长,觉得好用点个赞呗。

步骤概要

  1. 强化防火墙策略
  2. ddns-go 组件安装
  3. 1panel 安全配置优化 (启用ACME证书注册 )
  4. 启用 MCP server
  5. 反向代理服务配置
  6. 可以一键安装“龙虾” ^.^ (也是我推荐的方式)

Step0 强化防火墙策略

系统初始化后。VPC上 默认放行了 ssh http https 这些服务端口。

具体到OS层面,熟悉Linux 的同学知道,默认INPUT 方向的策略是全部放行的——

1
2
3
> iptables -nvL INPUT
Chain INPUT (policy ACCEPT 85401 packets, 12M bytes)
 pkts bytes target     prot opt in     out     source               destination  

这就导致一个什么问题呢?

ssh 服务经常被暴力攻击——

所以,系统初始化后,第一步是更新系统组件: apt update & apt upgrade

第二步就应该是 加固ssh 服务——避免被外部暴力破解。

这里,使用了iptables 的 “recent"模块,去动态创建&更新 一个叫做"openssh"的白名单库。

实现只有白名单库里的公网IP 可以访问这台 Ubuntu的ssh服务,进一步说:只有这些白名单地址所在的地址

1. 创建iptabls 策略脚本

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
> cat /usr/local/sbin/Protect_SSH_port.sh 
#!/bin/bash
#
# set iptables input rules for protecting ssh port service
# This script is intended to be run by root via a systemd service.

# Flush ONLY the INPUT chain rules to ensure a clean state before applying new ones.
/sbin/iptables -F INPUT

# Set rules
/sbin/iptables -I INPUT 1 -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT
# option: 放行Google IDC的跳板登录
/sbin/iptables -I INPUT 2 -s 34.81.160.0/24 -p tcp --dport 22 -j ACCEPT
/sbin/iptables -I INPUT 3 -p icmp --icmp-type 8 -m length --length 879 -j LOG --log-prefix 'SSH_OPEN_KEY'
/sbin/iptables -I INPUT 4 -p icmp --icmp-type 8 -m length --length 879 -m recent --name openssh --set --rsource -j ACCEPT
/sbin/iptables -I INPUT 5 -p tcp --dport 22 --syn -m recent --name openssh --rcheck --seconds 60 --rsource -j ACCEPT
/sbin/iptables -I INPUT 6 -p tcp --dport 22 -j DROP
/sbin/iptables -A INPUT -p icmp -j DROP

2. 创建 protect-ssh 服务,设置开机运行

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
> cat /etc/systemd/system/protect-ssh.service 
[Unit]
Description=Apply custom iptables rules to protect SSH port
After=network.target

[Service]
Type=oneshot
RemainAfterExit=yes

ExecStart=/usr/local/sbin/Protect_SSH_port.sh

# 当服务停止时,只清空 INPUT 链的规则
ExecStop=/sbin/iptables -F INPUT

[Install]
WantedBy=multi-user.target

启动并查看服务状态——

1
2
3
4
5
6
7
8
9
> systemctl enable protect-ssh
> systemctl restart protect-ssh
> systemctl status protect-ssh
 protect-ssh.service - Apply custom iptables rules to protect SSH port
     Loaded: loaded (/etc/systemd/system/protect-ssh.service; enabled; preset: enabled)
     Active: active (exited) since Thu 2026-03-09 16:43:15 CST; 7s ago
    Process: 141811 ExecStart=/usr/local/sbin/Protect_SSH_port.sh (code=exited, status=0/SUCCESS)
   Main PID: 141811 (code=exited, status=0/SUCCESS)
        CPU: 42ms

3. 效果展示

陌生用户,默认没有登录令牌。

连ssh 会话都建立不起来——

知道登录令牌,先“敲门”登记一下——

这里有个计算公式:

因为: ICMP令牌长度 = Packetsize + ICMP 头部 [ 8 字节 ] + IP 头部 [ 20 字节 ]

所以:packetsize [ 敲门令牌 ] = ICMP令牌长度 [ 这里是879 ] - 8 - 20 = 851 字节

ping通主机后,才可以正常登录——

这样就可以避免:主机被陌生人/主机 探测、破解的可能。

部署后,再也没有陌生人的登录记录。

Step1 ddns-go 组件安装

因为云主机的固定IP是收费的,为了减少支出,在云主机创建时外部IP是临时的。

Create_VM7

外部IP地址不固定就带来一个问题——某天google 给主机更换外部IP 后,域名访问就失效了。

除非人工重新进入 GCP 控制页面,查看新的IP,再手动重新映射域名。

有没有简单的办法解决这个问题呢?有的,兄弟,有的!

解决办法就是常听到的ddns 服务——通过一个探测脚本,定时去更新域名和IP映射关系。

所以,下面以ddns-go 组件为例,在这台ubuntu 上完成域名自动映射服务。

1. 下载ddns-go 源码,创建应用目录

先下载源码——

1
2
3
4
5
6
> cd /tmp/
wget https://github.com/jeessy2/ddns-go/releases/download/v6.16.5/ddns-go_6.16.5_linux_x86_64.tar.gz
tar -zxvf ddns-go_6.16.5_linux_x86_64.tar.gz 

> /tmp# ./ddns-go -v
v6.16.5

创建应用目录和空的配置文件——

1
2
3
mkdir -p /usr/local/bin/ddns-go
touch /usr/local/bin/ddns-go/ddns_go_config.yaml
cp /tmp/ddns-go /usr/local/bin/ddns-go/

2. 导入配置文件

ddns-go配置文件模板如下。

注意更新 ddns账户的域名和 TOKEN(这里以CloudFlare 上的DNS 服务为例)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# cat /usr/local/bin/ddns-go/ddns_go_config.yaml 
dnsconf:
    - name: cloudflare
      ipv4:
        enable: true
        gettype: url
        url: https://checkip.synology.com, https://ddns.oray.com/checkip, https://ip.3322.net,
        netinterface: ens4
        cmd: ""
        domains:
            - YOUR-DOMAIN-NAME
      ipv6:
        enable: false
        gettype: url
        url: https://speed.neu6.edu.cn/getIP.php, https://v6.ident.me, https://6.ipw.cn
        netinterface: ""
        cmd: ""
        ipv6reg: ""
        domains:
            - ""
      dns:
        name: cloudflare
        id: ""
        secret: YOUR-DDNS-TOKEN
        extparam: ""
      ttl: ""
user:
    username: YOURNAME
    password: YOURPASS
webhook:
    webhookurl: YOUR-FEISHU-WEBHOOK
    webhookrequestbody: |-
        {
            "msg_type": "post",
            "content": {
                "post": {
                    "zh_cn": {
                        "title": "您的谷歌云主机公网IP变了",
                        "content": [
                            [
                                {
                                    "tag": "text",
                                    "text": "新的IPv4地址:#{ipv4Addr}"
                                }
                            ],
                            [
                                {
                                    "tag": "text",
                                    "text": "域名更新结果:#{ipv4Result}"
                                }
                            ]
                        ]
                    }
                }
            }
        }
    webhookheaders: 'Content-Type: application/json'
notallowwanaccess: false
lang: zh

3. 创建ddns-go 服务,设置开机运行

和之前一样,为ddns-go 应用创建一个系统服务。

路径和上面一致的话,复制粘贴就好。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# cat /etc/systemd/system/ddns-go.service 
[Unit]
Description=Simple and easy to use DDNS. Automatically update domain name resolution to public IP (Support Aliyun, Tencent Cloud, Dnspod, Cloudflare, Callback, Huawei Cloud, Baidu Cloud, Porkbun, GoDaddy...)
ConditionFileIsExecutable=/usr/local/bin/ddns-go/ddns-go
Requires=network.target  
After=network-online.target 

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/local/bin/ddns-go/ddns-go "-l" "127.0.0.1:9876" "-f" "300" "-cacheTimes" "5" "-c" "/usr/local/bin/ddns-go/ddns_go_config.yaml"
Restart=always
RestartSec=120

[Install]
WantedBy=multi-user.target

然后,启动服务——

1
2
3
> systemctl enable ddns-go 
> systemctl start ddns-go 
> systemctl status ddns-go 

可以看到类似日志: Apr 1 11:47:22 systemd[1]: Started ddns-go.service - Simple and easy to use DDNS. Automatically update domain name resol> Apr 1 11:47:22 ddns-go[242234]: 2026/04/15 11:47:22 监听 127.0.0.1:9876 Apr 1 11:47:22 ddns-go[242234]: 2026/04/15 11:47:22 你的IP 35.212.158.199 没有变化, 域名 cloud.nxlan.cn

4. 效果展示

当云主机 IP 地址变化,会通知到feishu 中。

因为,目前ddns-go的web 管理页面只开放在本地的 127.0.0.1:9876上,目前还不能通过web 方式去配置、修改它。

等到 Step4 中的https 反向代理服务配置好后,就可以通过web 界面去管理了。(服务支持的 DDNS 厂商还是很多的,根据自己情况选择吧)

Step2 1panel 安全配置优化

1. 加固ssh 登录

1Panel的一个好处是:通过web 界面图形化地查看系统日志、设置系统基础配置。

例如,这里的ssh 服务。

进入**“系统” -> “SSH 管理”**页面。

关闭密码认证和反向解析。并且设置root用户“ 仅允许密钥登录 ”。

如果对Linux系统比较熟悉,建议将root 用户的登录也关闭掉。

登录时使用 google 创建云主机时自动创建的普通用户帐号和密钥就行。

此时只需将密钥公钥信息添加到家目录的authorized_keys文件中就行。

~# cat /home/YOURUSER/.ssh/authorized_keys ecdsa-sha2-nistp256 AAAA…..

然后,点击页面中的“密钥信息”,1Panel会自动创建一组登录 密钥用于root 用户登录。

进一步,点击“详情”。把公钥和私钥信息全部“下载”下来。用于本地 ssh 客户端登录这台Google Cloud主机。

点击“授权密钥”,可以看到密钥信息已经加载到 root 用户的ssh 白名单(就是 /root/.ssh/authorized_keys)中了。

配合上一步的ddns 动态域名,使用ssh 客户端就可以顺利以root 用户身份登录 这台云主机了。

2. 申请 ACME 证书

其实也就是申请 “Let’s Encrypt” 颁发的免费证书,并自动续签。

想必你也不希望 自己的数据“裸奔”在不可信的互联网上。

首先来到**“网站” -> “证书”** 页面。

点击“DNS 账户”,关联自己的 DNS 域名和TOKEN。

“类型” 这里与之前ddns-go 域名设置中的域名服务商保持一致——CloudFlare。

点击 “ACME 账户”,创建一个Let’s Encrypt账户,邮箱输入自己常用的就行。

最后,点击"申请证书”,完成自动化证书申请和更新的配置。

注意,“验证方式"为刚才创建的DNS 帐号,并勾选"自动续签”。这样免费证书到期后,也不用人工干预,就可以自动续续订证书 。

申请过程需要等1分钟左右,日志可以查看进度和完成情况——

拿到证书,后续就可以用在很多地方,例如下面的 1Panel 管理页面。

3. 1panel 管理页面加固

来到 “面板设置” -> “安全” 页面。

绑定自己域名,并开启 “面板 SSL”。开启后,1Panel 管理页面会关联之前申请到的 ACME 证书。(如下图中的效果)

同时最好开启两步验证,进一步加强登录安全。

开启两步验证后的效果——

Step3 启用 MCP server

1. MCP server 初始配置

首先来到 “AI” -> “MCP” -> “Servers”

我们知道,MCP 下层用的是HTTP 协议,为了不直接将MCP server 暴露在外网,需要先安装 HTTP 反向代理服务

进入"应用商店" -> 筛选"web 服务器"标签,安装 OpenResty

该应用和后面提到的每一个独立的 MCP server,本质上都是以docker 方式运行的运行时。

关于OpenResty 的简单介绍——

2. 绑定网站

选中 “MCP"页面中的"绑定网站”——

“域名”还是输入 ddns-go 那用到的自己域名。

再次关联之前通过 ACME 拿到的证书。

相同的证书,用在不同的服务上:

之前是用于1Panel管理页面的登录(非443端口),这次是用于标准443 端口上全部https服务的反向代理。

反代类似于在客户端和真实服务间加了一层垫片 。它的好处是:

  1. 缩小被攻击的范围;2. 隔离不同服务;3. 内部服务不用重复申请证书;4. 统一访问日志,可追加安全控制策略。

3. 创建第一个MCP 服务

我们先创建一个常用的MCP 服务——sequential-thinking。

它可以为 大模型对话,提供多轮思考的上下文内容。

类型“选择 npx。 “启动命令“中填入——

1
npx -y @modelcontextprotocol/server-sequential-thinking

如果不使用HTTP 反向代理服务,这里就应该允许"端口外部访问”。

因为之前已经启用了 OpenResty ,访问这个 MCP server 的请求,都会经反向代理转给真实服务。

这里真实服务端口(8002)自然也不用暴露到外网。

上面"SSE 路径"就是 MCP server 的服务 路径。

来到 “网站” -> “HTTPS 域名” -> “反向代理” 中可以看到,1Panel 自动为MCP 服务追加了 一条反向代理配置。(下图第二条)

此时,测试一下这个MCP server 地址——

服务正常响应了。

“网站日志"中 也可以看到访问记录。

怎么样是不是很简单?分分钟一个 MCP server 服务就启动和上线了。

后续文章中,也会提到这个 thinking 工具的使用案例。

4. 运行自己的MCP 源码

上面的方式,本质是把他人写好并打包发布 的 MCP server代码拉取到 1Panel 的supergateway 容器中运行。

那能不能把自己写好的服务,也跑在1Panel 上呢?例如之前文章 MCP 案例: 求职助手 中的代码。

这就相当于有一个 7*24 的服务跑在云主机上,不用每次运行MCP server时 输入那一长串启动命令——

1
uv --directory S:\trae_pj\jobsearch_mcp_server-1.0.0\src\jobsearch_mcp_server run jobsearch-mcp-server

说干就干。

测试下,来可以通过 git 拉取项目源代码到1Panel 的supergateway 容器中,并以 uvx 方式更新并运行我们自己的代码 。

效果如下。

1Panel 配置方面不复杂,一页图就能看明白——

“类型” 选择uvx,“启动命令"就这么一行。

1
/bin/sh -c "apk add git && uvx --from git+http://lab.nxlan.cn:3008/pdream/jobsearch-mcp-server.git jobsearch-mcp-server --transport stdio"

这里使用了 gitea 的 docker版本,用于管理项目代码。它可以兼容 git 命令。

并且页面也类似于 github 的效果,属于轻量化的本地部署方案,回头有机会再讲讲这块的搭建和设置。

为什么说 uvx 可以刷新最新代码呢?看下日志就会发现,它每次重启都会从gitea 上同步下源码。

这样每次改好源码,在"AI” -> “MCP” -> “Servers” 下重启jobsearch 这个应用,就自动完成了更新和发布。

Step4 反向代理服务配置

1. 自动创建的反代服务

“网站” -> “HTTPS 域名” -> “反向代理” 中可以看到:

之前启用mcp server 时,1Panel 自动帮我们创建好的两个反向代理配置。

具体配置长啥样呢?点击 “源文” 就可以看到详细配置。这里以 jobsearch 这个服务为例——

1
2
3
4
5
6
7
location ^~ /jobsearch {
    proxy_pass http://127.0.0.1:8005/jobsearch; 
    proxy_buffering off; 
    proxy_http_version 1.1; 
    proxy_set_header Connection ''; 
    chunked_transfer_encoding off; 
}

简单地说就是,当客户端浏览器访问站点 (https://lab.nxlan.cn) 时,反向代理服务会根据客户端请求的路径进行请求转发。例如这里: 当匹配客户请求含有 “/jobsearch” 就转发至 “http://127.0.0.1:8005/jobsearch”。

同理,当匹配客户请求含有 “/sequential-thinking” 就转发至 “http://127.0.0.1:8000/sequential-thinking”。

1Panel自动创建的代理设置是简单场景下的配置,没问题就不用手动修改。

可是,还有些特殊的web 应用,就不能使用默认的配置。

2. ddns-go 的反代配置

本文涉及需要手动修改的配置,有两个——

  1. 一个是前面安装的 ddns-go, 它也是有web 管理界面的 。
  2. 还有一个就是 OpenClaw 小龙虾了。

先看ddns-go 的——

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17

location ^~ /ddns {
    # 1. 请求时:去掉 /ddns 前缀,发给后端
    rewrite ^/ddns(/.*)$ $1 break; 
    # 2. 响应时:如果后端想重定向到 /,就把它修正为 /ddns/
    # 这条规则能修正所有类似 /login -> /ddns/login 的重定向
    proxy_redirect / /ddns/; 
    
    # --- 其他代理设置 ---
    proxy_pass http://127.0.0.1:9876; 
    proxy_set_header Host $host; 
    proxy_set_header X-Real-IP $remote_addr; 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header X-Forwarded-Proto $scheme; 
    add_header Cache-Control no-cache; 
    proxy_ssl_server_name off; 
}

如果在原始服务中写死了请求 url ,所以当用户发起这种请求到达代理服务时,会破坏默认的转发规则。

此时,需要改写用户请求。——ddns-go 就是这样的例子。

正常的请求:

用户请求网页 https://lab.nxlan.cn/ddns –> 反代 –>原始服务 http://127.0.0.1:9876

异常的请求:(因为 ddns-go 的登录页面 路径为 /login)

用户请求网页 https://lab.nxlan.cn/login –> 反代 –> ??? 这是什么玩意 我这没有这个规则

修正规则后的请求:

用户请求网页 https://lab.nxlan.cn/login –> 反代 –> 规则重定向到 https://lab.nxlan.cn/ddns/login –> 反代发现匹配现有/ddns 路由的规则 –> 原始服务 http://127.0.0.1:9876/login

策略应用后,客户端看到的页面效果就是——

3. OpenClaw 的反代配置

OpenClaw 则是另一种情况:

  1. 从安全性的角度考量,建议开启gateway 中的 “allowedOrigins” 校验。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
"gateway": {
    "port": 18789,
    "mode": "local",
    "bind": "lan",
    "controlUi": {
      "allowedOrigins": [
        "https://lab.nxlan.cn",
        "http://127.0.0.1:18789",
        "http://localhost:18789"
      ],
      "dangerouslyAllowHostHeaderOriginFallback": false,
      "dangerouslyDisableDeviceAuth": false
    },
    "auth": {
      "mode": "token",
      "token": "${OPENCLAW_GATEWAY_TOKEN}",
    }
}

该校验内容来自哪里呢?

需要在代理服务器上追加这部分请求信息,由"X-Forwarded-Proto"和 “X-Forwarded-For” 字段组成。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
location ^~ / {
    proxy_pass http://127.0.0.1:18789; 
    proxy_http_version 1.1; 
    proxy_set_header Upgrade $http_upgrade; 
    proxy_set_header Connection "upgrade"; 
    proxy_set_header Host $host; 
    proxy_set_header X-Real-IP $remote_addr; 
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 
    proxy_set_header X-Forwarded-Proto $scheme; 
    proxy_read_timeout 3600; 
    proxy_send_timeout 3600; 
    proxy_ssl_server_name off; 
    proxy_ssl_name $proxy_host; 
}

此外还需要追加 OpenClaw 用到的 WebSocket 的支持。

后续,其他应用,也可以参考这两种方式灵活调整。毕竟,谁还没有一两个 AI 助手呢?

Step5 一键安装“龙虾”

龙虾最近不是很火么,恰好最近1Panel 丰富了龙虾安装配置,这里简单提一下。

1. 管理模型

“AI” -> “模型” -> “模型帐号” 中关联自己的 大模型API 帐号。

2. 创建配置

基础配置 图形化 点击就可以快速完成。

3. 下载官方通信插件

图形化功能适配,还比较方便,这里以飞书为例。

当然,复杂的配置还是得修改 openclaw.json 这个主配置文件。

4. 血泪史

OpenClaw的配置文件是核心,但是它的接口适配做得很难用。

每一项优化起来太麻烦了——

目前最让我记忆深刻的有两点:

  1. 我是在使用小龙虾 两个星期后,才知道 feishu 通信渠道插件有两个(一个是 openclaw 社区的,一个是feishu 自己的);

  2. 每次 OpenClaw 版本大更新后 配置文件的格式就变得乱七八糟,有的配置之前和现在完全不一样——毫无友好性

    如果为了修复 升级后的配置问题,大概要花上半天时间 继续调试它。

    目前 1Panel 官方维护 一套他们根据 OpenClaw 发布后自动打包的 docker 镜像。

    https://hub.docker.com/r/1panel/openclaw

所以,有需要的话 还是找成功的案例 去复制一下吧,自己折腾还是太折磨了

最后,祝AI 时代每个人不用更焦虑,做自己的主人,而不是交给AI 做主。

Licensed under CC BY-NC-SA 4.0