贫苦云主机用户的安全加固策略

8ad47c80dc90bc4d538d235692b3ee84.gif

上次去北京出差,为了便捷地访问家里内网中的一些服务,就在腾讯云服务器上部署了一个 frps 服务,在本地内网的 Openwrt 路由器上安装 frpc 客户端,将内网中的一台 Windows 服务器穿透到腾讯云服务器上。然后通过 Windows RDP 远程连接到这台 Windows 机器上,来使用内网的一些服务。之前也尝试过 WireGuard,但是使用了一段时间体验下来感觉还是通过内网穿透的方式比较稳定和流畅。于是最终的方式还是选用 frp 内网穿透的方案。

本着方便省事儿的原则就放心大胆地开放了云服务器的安全组规则。不幸的是,由于这样的疏忽,某一天我的 Windows 虚拟机被弱口令(admin2020)给爆破了。巨大的损失就是挂载到 Windows 虚拟机上的 NAS 存储被勒索病毒进行了加密 😂。不过好在最最重要的数据全部存到了 OneDrive 上,NAS 上损失的都是一些下载的电影、书籍以及一些 ISO、虚拟机模版之类的文件。一下子损失了 8TB 的数据很心疼,毕竟是自己辛辛苦苦搜集的资源,但是仔细想一下,这些资源都不是自己的,基本上都是从下载下来的,还可以通过同样的方式找回来。或许是之前看过《断舍离》的缘故,也看的开了,难受了一小会儿之后就好过来了。毕竟这个世界上,没有无法不能失去的东西,也没有非得必须要得到的东西。

有了此次教训,就开始考虑对手上的云主机资源进行安全加固,将一些与内网互通的云主机安全组/防火墙的通用去除了允许所有,只添加本地公网 IP 的允许规则。但是对于家庭宽带用户来讲,公网 IP 并不是一个固定的 IP,而是会一直不断变化,总不能每次变化之后再登录到云主机控制台手动添加一下吧。于是就想着有没有自动化的方式来自动添加和更新安全组/防火墙规则呢?

Terraform

第一个想到的方案便是 terraform,主流的云厂商都有对应的 provider 支持,腾讯云应该也是能够支持的。不过看了官方的 terraform-provider-tencentcloud[1] repo 文档之后,并没有找到给 lighthouse 主机配置防火墙规则的支持,遂放弃。

cfwctl

既然 terraform 不支持,那就自己造轮子写一个吧,就叫它 Cloud Firewall Control Tool,简称 cfwctl[2]。腾讯云官方的 API 文档还可以,还能在线生成代码,用起来也十分方便。考虑到会将该工具运行到运行到 arm64 的路由器上,因此跨平台运行 cfwctl 使用 golang 来实现无疑是个不错的选择,正好也能来练练手。

执行的操作其实很简单,先是通过某种方式获取本地机器的公网 IP,然后将该 IP 添加到对应实例的防火墙规则当中,并在规则描述中添加标识符来标记。目前自己只需要添加规则,先凑活着用一段时间,看下效果如何。目前只支持腾讯云的 lighthouse 实例,后续有机会再增加几个别的云厂商支持。

获取公网 IP

首先要获取到我们本地网路的公网 IP,由于公网 IP 可能一直是变化的,所以我们每次更新防火墙规则之前都需要获取最新的公网 IP。以下是具体实现的代码:

package main

import (
 "fmt"
 "io/ioutil"
 "net/http"
 "regexp"
)

// 定义 IPv4 的正则表达式,目的是从获取公网 IP 的  API 返回结果中过滤出 IPv4 地址
const ipv4_regex = `(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}`

// 定义几个几个提供获取公网 IPv4 地址的 API URL
var urlList = []string{
 "https://ip.me"
 "http://ip.sb",
 "http://ip.cip.cc",
 "http://myip.ipip.net",
}

func GetPublicIP() string {
 for _, url := range urlList {
    // 创建一个 http client
  client := &http.Client{}
    // 设置 client 的请求方法为 GET 以及请求的 URL
  request, err := http.NewRequest("GET", url, nil)
  if err != nil {
   continue
  }
    // 设置 Client 的 User-Agent 为 curl,不然一些 API 会返回 html 的结果
  request.Header.Set("User-Agent", "curl/7.54.0")
  resp, err := client.Do(request)
  if resp.StatusCode != 200 && err != nil {
   continue
  }
  defer resp.Body.Close()
  body, err := ioutil.ReadAll(resp.Body)
    // 从 API 返回结果中用正则匹配出 IPv4 地址
  reg := regexp.MustCompile(ipv4_regex)
  ipList := reg.FindAllString(string(body), -1)
    // 如果匹配结果中有 IPv4 地址,则返回第一个元素即可
  if len(ipList) > 0 {
   fmt.Printf("my public ip is %s\n", ipList[0])
   return ipList[0]
  }
 }
 return ""
}

添加 Firewall 规则

package main

import (
 "fmt"
 "os"

 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common"
 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/errors"
 "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common/profile"
 lighthouse "github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/lighthouse/v20200324"
)

// 定义 API 请求的 URL
const endpoint = "lighthouse.tencentcloudapi.com"

// 定义请求 API 的 Client 结构体
type Client struct {
 SecretId  string
 SecretKey string
 InstaceId string
 Region    string
 Endpoint  string
}

// 通过该方法从环境变量中读取 Ck Sk 等信息,并返回一个 client 对象
func NewClient() Client {
 client := Client{
  SecretId:  os.Getenv("TENCENTCLOUD_SECRET_ID"),
  SecretKey: os.Getenv("TENCENTCLOUD_SECRET_KEY"),
  InstaceId: os.Getenv("TENCENTCLOUD_INSTANCE_ID"),
  Region:    os.Getenv("TENCENTCLOUD_REGION"),
  Endpoint:  endpoint,
 }
 if client.SecretId == "" || client.SecretKey == "" || client.InstaceId == "" || client.Region == "" {
  panic("Please set TENCENTCLOUD_SECRET_ID, TENCENTCLOUD_SECRET_KEY, TENCENTCLOUD_INSTANCE_ID, TENCENTCLOUD_REGION")
 }
 return client
}

// 定义添加防火墙规则的方法
func (c Client) AddRules(firewallRules []*lighthouse.FirewallRule) {
 credential := common.NewCredential(c.SecretId, c.SecretKey)
 cpf := profile.NewClientProfile()
 cpf.HttpProfile.Endpoint = endpoint
 client, _ := lighthouse.NewClient(credential, c.Region, cpf)

 request := lighthouse.NewCreateFirewallRulesRequest()
 request.InstanceId = common.StringPtr(c.InstaceId)
 request.FirewallRules = firewallRules

 response, err := client.CreateFirewallRules(request)
 if _, ok := err.(*errors.TencentCloudSDKError); ok {
  fmt.Printf("An API error has returned: %s", err)
  return
 }
 if err != nil {
  panic(err)
 }
 fmt.Printf("%s", response.ToJsonString())
}

获取防火墙规则

func (c Client) GetRules() string {
 credential := common.NewCredential(c.SecretId, c.SecretKey)
 cpf := profile.NewClientProfile()
 cpf.HttpProfile.Endpoint = endpoint
 client, _ := lighthouse.NewClient(credential, c.Region, cpf)

 request := lighthouse.NewDescribeFirewallRulesRequest()

 request.InstanceId = common.StringPtr(c.InstaceId)

 response, err := client.DescribeFirewallRules(request)
 if _, ok := err.(*errors.TencentCloudSDKError); ok {
  fmt.Printf("An API error has returned: %s", err)
  return ""
 }
 if err != nil {
  panic(err)
 }
 return response.ToJsonString()
}

使用

  • 在本地的 ~/.bashrc 或者 ~/.zshrc文件中设置一些 AKSK 信息、实例 ID、region 信息等参数;

export TENCENTCLOUD_SECRET_ID="AKiiiplQntjJbcMp1"
export TENCENTCLOUD_SECRET_KEY="SKkkkiiwwlwjmmG5"
export TENCENTCLOUD_INSTANCE_ID="lhins-qjxazjaa"
export TENCENTCLOUD_REGION="ap-beijing"
  • 设置好环境变量之后,再编译运行看下能否成功

$ go build -o cfwctl
$ chmod +x cfwctl
$ cfwctl add

my public ip is 193.191.231.82
{"Response":{"RequestId":"30e71243-1793-112e-9e41-b310ec599b90"}}%
  • 如果是 arm64 的 OpenWrt 环境,在本地开发机上进行跨平台编译,然后将编译好的 cfwctl 二进制文件 scp 到路由器上,再添加 cron job 定时任务即可,这样就能自动定时更新防火墙规则来。

$ CGO_ENABLED=0  GOOS=linux  GOARCH=arm64 go build -o cfwctl

引用链接

[1]

terraform-provider-tencentcloud: https://github.com/tencentcloudstack/terraform-provider-tencentcloud

[2]

cfwctl: https://github.com/muzi502/cfwctl

原文链接:https://blog.k8s.li/cfwctl.html

bc04d3c290172a5e732f08135aff8aef.gif

335af10703fab9bd1ed08bcd1fc3a1cb.png

你可能还喜欢

点击下方图片即可阅读

ee135e6e0e273cb48e7bb9c55e952d55.png

我妈今年 70 岁,她用了 21 年的 Linux!

0200a6ae4943aabbecd0f0717ae2a585.gif

云原生是一种信仰 🤘

关注公众号

后台回复◉k8s◉获取史上最方便快捷的 Kubernetes 高可用部署工具,只需一条命令,连 ssh 都不需要!

011367c5f689f40034857a548a1d8be3.gif

d9a0b59da84e1a9b5f9f4b57a42c6fec.gif

点击 "阅读原文" 获取更好的阅读体验!

发现朋友圈变“安静”了吗?

a3b311abe5c4274330dde1ec96a7e789.gif

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值