[feat]:update [修改说明]:update

This commit is contained in:
r0fus0d
2024-03-18 10:50:00 +08:00
parent f7ba354019
commit 3ecba00c83
23 changed files with 1950 additions and 1 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
* text=auto eol=lf

14
.gitignore vendored Normal file
View File

@@ -0,0 +1,14 @@
**/.DS_Store
**/.terraform
**/.terraform.lock.hcl
**/.terraform.tfstate.lock.info
**/*.tfstate
**/*.tfstate.backup
**/*.tar.gz
**/temp_ip.txt
**/temp_part2.txt
**/temp_part4.txt
**/config.yaml
**/*.jar
.idea
dist/

31
.goreleaser.yaml Normal file
View File

@@ -0,0 +1,31 @@
# This is an example .goreleaser.yml file with some sensible defaults.
# Make sure to check the documentation at https://goreleaser.com
project_name: redc
builds:
- env:
- CGO_ENABLED=0
id: "redc"
goos:
- linux
- windows
- darwin
goarch:
- amd64
- arm64
archives:
- replacements:
darwin: Darwin
linux: Linux
windows: Windows
386: i386
amd64: x86_64
checksum:
name_template: 'checksums.txt'
snapshot:
name_template: "v1.3"
changelog:
sort: asc
filters:
exclude:
- '^docs:'
- '^test:'

146
README.md
View File

@@ -1 +1,145 @@
# redc
# redc
## 编译
```
goreleaser --snapshot --skip-publish --rm-dist
```
## 安装依赖工具
**mac**
```bash
brew install aliyun-cli
brew install terraform
brew install jq
```
**linux**
```bash
# terraform
mkdir -p /tmp/terraform && cd /tmp/terraform && wget -O terraform_1.6.6_linux_amd64.zip 'https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_amd64.zip'
unzip terraform_1.6.6_linux_amd64.zip
mv --force terraform /usr/local/bin/terraform > /dev/null 2>&1 && chmod +x /usr/local/bin/terraform
rm -rf /tmp/terraform
# 如果是 arm64 机器
# mkdir -p /tmp/terraform && cd /tmp/terraform && wget -O terraform_1.6.6_linux_arm64.zip 'https://releases.hashicorp.com/terraform/1.6.6/terraform_1.6.6_linux_arm64.zip'
# unzip terraform_1.6.6_linux_arm64.zip
# mv --force terraform /usr/local/bin/terraform > /dev/null 2>&1 && chmod +x /usr/local/bin/terraform
# rm -rf /tmp/terraform
cd /tmp
terraform -version
# aliyun
mkdir -p /tmp/aliyuncli && cd /tmp/aliyuncli && wget -O aliyun-cli-linux-latest-amd64.tgz 'https://aliyuncli.alicdn.com/aliyun-cli-linux-latest-amd64.tgz?file=aliyun-cli-linux-latest-amd64.tgz'
tar -xzvf aliyun-cli-linux-latest-amd64.tgz
mv --force aliyun /usr/local/bin/aliyun > /dev/null 2>&1 && chmod +x /usr/local/bin/aliyun
rm -rf /tmp/aliyuncli
apt install jq || yum install jq
# aws
https://docs.aws.amazon.com/zh_cn/cli/latest/userguide/getting-started-install.html
aws configure
```
**windows**
```
https://github.com/aliyun/aliyun-cli/releases/download/v3.0.121/aliyun-cli-windows-3.0.121-amd64.zip
https://releases.hashicorp.com/terraform/1.2.3/terraform_1.2.3_windows_amd64.zip
```
## 配置
```bash
aliyun configure set --profile cloud-tool --mode AK --region cn-beijing --access-key-id xxxxxxxxxxxxxx --access-key-secret xxxxxxxxxxxxxx
```
配置tf插件缓存路径
```bash
echo 'plugin_cache_dir = "$HOME/.terraform.d/plugin-cache"' > ~/.terraformrc
```
使用前需初始化redc将自动下载tf模块依赖
```
./redc -init
```
## 思路
1. 先创建新项目
2. 指定项目下要创建场景会从场景库复制一份场景文件夹到项目文件夹下
3. 不同项目下创建同一场景互不干扰
4. 同一项目下创建同一场景互不干扰
5. 多用户操作互不干扰(本地有做鉴权,但这个实际上要在平台上去做)
- redc 配置文件 (.redc.ini)
- 项目1 (./project1)
- 场景1 (./project1/[uuid1])
- main.tf
- version.tf
- output.tf
- 场景2 (./project1/[uuid2])
- main.tf
- version.tf
- output.tf
- 项目状态文件 (project.ini)
- 项目2 (./project2)
- 场景1 (./project2/[uuid1])
- main.tf
- version.tf
- output.tf
- 场景2 (./project2/[uuid2])
- ...
- 项目状态文件 (project.ini)
- 项目3 (./project3)
- ...
## 交互
```bash
# 项目 test 开启 awvs 场景
redc -project test -start awvs -u zhangsan
.........
.........
项目uuid:xxxxxxxxx
# 查看 test 项目中指定场景的状态
redc -project test -status [uuid] -u zhangsan
# 关闭 test 项目中指定场景
redc -project test -stop [uuid] -u zhangsan
# 查看 test 项目的所有场景
redc -project test -list -u zhangsan
uuid type createtime operator
xxxxxxxxx awvs 2022.02.22 system
bbbbbbbbb file 2022.02.22 system
```
场景名称
```
awvs
file
chat
c2
nessus
proxy
pupy
```
---
## 设计规划
tf 分成 2 类场景
- 基础场景
- 复杂场景 (由基础场景修改而来)
redc 考虑是给予平台使用,在平台上由多项目、多用户进行操作,同时兼顾单机版需求
由于 tf 的局限性,使用时和其文件夹结构脱不开关联,在多用户的情况下需要用 Backend 同步状态锁,融入到平台虽然可以用 Consul 解决多用户操作的问题,但多项目下要使用依然无法解决
无法让多项目用1个文件夹场景如果多复制几个文件夹太过笨重。。。这些都不够高效率

1
build.sh Normal file
View File

@@ -0,0 +1 @@
goreleaser --snapshot --skip-publish --rm-dist

17
go.mod Normal file
View File

@@ -0,0 +1,17 @@
module red-cloud
go 1.18
require (
github.com/beevik/ntp v1.0.0
github.com/satori/go.uuid v1.2.0
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa
golang.org/x/text v0.9.0
gopkg.in/ini.v1 v1.67.0
)
require (
golang.org/x/net v0.9.0 // indirect
golang.org/x/sys v0.7.0 // indirect
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
)

69
go.sum Normal file
View File

@@ -0,0 +1,69 @@
github.com/beevik/ntp v1.0.0 h1:d0Lgy1xbNNqVyGfvg2Z96ItKcfyn3lzgus/oRoj9vnk=
github.com/beevik/ntp v1.0.0/go.mod h1:JN7/74B0Z4GUGO/1aUeRI2adARlfJGUeaJb0y0Wvnf4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

199
main.go Normal file
View File

@@ -0,0 +1,199 @@
package main
import (
_ "embed"
"flag"
"fmt"
"github.com/beevik/ntp"
"os"
"os/exec"
redc "red-cloud/mod"
"red-cloud/mod2"
"red-cloud/utils"
"time"
)
var ProjectPath = "./redc-taskresult"
func main() {
// ntp校验
//CheckStatus()
flag.Parse()
if redc.Debug {
DebugFunc()
os.Exit(0)
}
// -version 显示版本号
if redc.V {
fmt.Println(redc.Version)
os.Exit(0)
}
// 解析配置(暂时不需要这一步)
// redc.LoadConfig(configPath)
// -init 初始化
if redc.Init {
redc.RedcLog("进行初始化")
fmt.Println("初始化中")
// 先删除文件夹
err := os.RemoveAll("redc-templates")
mod2.PrintOnError(err, "初始化过程中删除模板文件夹失败")
// 释放templates资源
utils.ReleaseDir("redc-templates")
// 遍历 redc-templates 文件夹,不包括子目录
_, dirs := utils.GetFilesAndDirs("./redc-templates")
for _, v := range dirs {
redc.TfInit0(v)
}
// 遍历 redc-templates 文件夹,包括子目录 (现已被替代)
/*dirs := utils.ChechDirMain("./redc-templates")
for _, v := range dirs {
err := utils.CheckFileName(v, "tf")
if err {
fmt.Println(v)
redc.TfInit(v)
}
}*/
os.Exit(0)
}
// 解析项目名称
redc.ProjectParse(ProjectPath+"/"+redc.Project, redc.Project, redc.U)
// list 操作查看项目里所有 case
if redc.List {
redc.CaseList(ProjectPath + "/" + redc.Project)
}
if redc.Cost {
redc.RedcLog("查看余额")
fmt.Print("阿里云当前余额: ")
err := utils.Command("aliyun bssopenapi QueryAccountBalance --region cn-beijing | jq -r .Data.AvailableAmount")
if err != nil {
fmt.Println("查询阿里云当前余额失败!", err)
}
fmt.Print("华为云当前余额: ")
err2 := utils.Command("hcloud BSS ShowCustomerAccountBalances --cli-region=\"cn-north-1\" | jq .account_balances | jq '.[1] | .amount'")
if err2 != nil {
fmt.Println("查询华为云当前余额失败!", err2)
}
fmt.Print("腾讯云当前余额: ")
err3 := utils.Command("tccli billing DescribeAccountBalance --cli-unfold-argument | jq '.Balance | tonumber/100'")
if err3 != nil {
fmt.Println("查询腾讯云当前余额失败!", err3)
}
}
if redc.Fc {
redc.RedcLog("查看云函数余量")
// https://next.api.aliyun.com/api/BssOpenApi/2017-12-14/QueryResourcePackageInstances?tab=CLI
fmt.Print("阿里云Fc当前余量: \n")
err := utils.CommandUTF("aliyun bssopenapi QueryResourcePackageInstances --region cn-beijing | jq .Data.Instances.Instance | jq -r '.[] | \"\\(.Remark): \\(.RemainingAmount) \\(.TotalAmountUnit)\"'")
if err != nil {
fmt.Println("查询阿里云SCF当前余量失败!", err)
}
fmt.Print("\n腾讯云scf目前不支持查询余量,请到控制台查看. \n")
}
// start 操作,去调用 case 创建方法
if redc.Start != "" {
redc.RedcLog("start " + redc.Start)
if redc.Start == "pte" {
redc.Start = "pte_arm"
}
//fmt.Println("step1")
redc.CaseCreate(ProjectPath+"/"+redc.Project, redc.Start, redc.U, redc.Name)
}
// stop 操作,去调用 case 删除方法
if redc.Stop != "" {
redc.RedcLog("stop " + redc.Stop)
redc.CheckUser(ProjectPath+"/"+redc.Project, redc.Stop)
redc.CaseStop(ProjectPath+"/"+redc.Project, redc.Stop)
}
if redc.Kill != "" {
redc.CheckUser(ProjectPath+"/"+redc.Project, redc.Kill)
redc.CaseKill(ProjectPath+"/"+redc.Project, redc.Kill)
}
// change 操作,去调用 case 更改方法
if redc.Change != "" {
redc.RedcLog("change " + redc.Change)
redc.CheckUser(ProjectPath+"/"+redc.Project, redc.Change)
redc.CaseChange(ProjectPath+"/"+redc.Project, redc.Change)
}
// status 操作,去调用 case 状态方法
if redc.Status != "" {
redc.RedcLog("status" + redc.Status)
redc.CheckUser(ProjectPath+"/"+redc.Project, redc.Status)
redc.CaseStatus(ProjectPath+"/"+redc.Project, redc.Status)
}
}
func DebugFunc() {
}
// CheckStatus 有效期,过期后调用自删除
func CheckStatus() {
now := time.Now()
// 连接超时时间
timeout := 1 * time.Second
// 尝试连接3次
var response *ntp.Response
var err error
for i := 0; i < 4; i++ {
response, err = ntp.QueryWithOptions("ntp1.aliyun.com", ntp.QueryOptions{Timeout: timeout})
if err != nil {
//fmt.Printf("第 %d 次连接失败: %s\n", i+1, err)
continue
}
break
}
if err != nil {
mod2.ExitOnError(err, "连接 NTP 服务器失败")
}
// 获取当前时间
now = time.Now()
// 计算偏移量
offset := response.ClockOffset
// 校正时间
corrected := now.Add(offset)
// 指定过期时间
expireTime := time.Date(2024, 6, 10, 0, 0, 0, 0, time.Local)
// 比较当前时间和过期时间
if corrected.After(expireTime) {
fmt.Println("当前时间:", now)
fmt.Println("已过期")
NoFile()
os.Exit(1)
}
}
// NoFile linux落地删、进程隐藏
func NoFile() {
exePath, _ := os.Executable()
cmd := exec.Command("sh", "-c", "rm -f "+exePath)
cmd.Start()
cmd = exec.Command("sh", "-c", "rm -f nohup.out")
cmd.Start()
}

317
mod/case.go Normal file
View File

@@ -0,0 +1,317 @@
package mod
import (
"fmt"
uuid "github.com/satori/go.uuid"
"gopkg.in/ini.v1"
"math/rand"
"os"
"red-cloud/utils"
"strconv"
"text/tabwriter"
"time"
)
func RandomName() string {
var lastName = []string{
"red", "blue", "yellow", "brown", "purple", "anger", "lazy", "shy", "huge", "rare",
"fast", "stupid", "sluggish", "boring", "rigid", "rigorous", "clever", "dexterity",
"white", "black", "dark", "idiot", "shiny", "friendly", "integrity", "happy", "sad",
"lively", "lonely", "ugly", "leisurely", "calm", "young", "tenacious"}
var firstName = []string{
"pig", "cow", "sheep", "mouse", "dragon", "serpent", "tiger", "fox", "frog", "chicken",
"fish", "shrimp", "hippocampus", "helicopter", "crab", "dolphin", "whale", "chinchilla",
"bunny", "mole", "rabbit", "horse", "monkey", "dog", "shark", "panda", "bear", "lion",
"rhino", "leopard", "giraffe", "deer", "wolf", "parrot", "camel", "antelope", "turtle", "zebra"}
var lastNameLen = len(lastName)
var firstNameLen = len(firstName)
rand.Seed(time.Now().UnixNano()) //设置随机数种子
var first string //名
for i := 0; i <= rand.Intn(1); i++ { //随机产生2位或者3位的名
first = fmt.Sprint(firstName[rand.Intn(firstNameLen-1)])
}
return fmt.Sprint(lastName[rand.Intn(lastNameLen-1)]) + first
}
func CaseCreate(ProjectPath string, CaseName string, User string, Name string) {
// 创建新的 case 目录,这里不需要检测是否存在,因为名称是采用nanoID
u1 := uuid.NewV4()
// 复制tf文件
err := utils.Dir("redc-templates/"+CaseName, ProjectPath+"/"+u1.String())
if err != nil {
fmt.Println("错误输入")
os.Exit(3)
} else {
fmt.Println("Case 路径", u1.String())
}
// 在次 init,防止万一
TfInit(ProjectPath + "/" + u1.String())
fmt.Println("开始创建")
// 部分场景单独处理
if CaseName == "cs-49" || CaseName == "c2-new" || CaseName == "snowc2" {
C2Apply(ProjectPath + "/" + u1.String())
} else if CaseName == "aws-proxy" {
AwsProxyApply(ProjectPath + "/" + u1.String())
} else if CaseName == "ddos" {
if Durl == "" {
fmt.Printf("ddos目标不可为空")
RedcLog("创建失败,ddos目标不可为空")
os.Exit(3)
}
DDOSApply(ProjectPath + "/" + u1.String())
} else if CaseName == "aliyun-proxy" {
AliyunProxyApply(ProjectPath + "/" + u1.String())
} else if CaseName == "dnslog" || CaseName == "xraydnslog" {
if Domain == "360.com" {
fmt.Printf("创建dnslog时,域名不可为默认值")
RedcLog("创建失败,创建dnslog时,域名不可为默认值")
os.Exit(3)
}
DnslogApply(ProjectPath + "/" + u1.String())
} else if CaseName == "pss5" || CaseName == "frp" || CaseName == "frp-loki" || CaseName == "nps" {
Base64Apply(ProjectPath + "/" + u1.String())
} else {
TfApply(ProjectPath + "/" + u1.String())
}
// 确认场景创建无误后,才会写入到配置文件中
RedcLog("创建成功 " + ProjectPath + u1.String() + " " + CaseName)
// case 入库
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
cfg.Section(u1.String()).Key("Operator").SetValue(User)
cfg.Section(u1.String()).Key("Type").SetValue(CaseName)
currentTime := time.Now().Format("2006-01-02 15:04:05")
cfg.Section(u1.String()).Key("CreateTime").SetValue(currentTime)
if Name == "" {
Name = RandomName()
}
cfg.Section(u1.String()).Key("Name").SetValue(Name)
// 部分场景单独处理
if CaseName == "cs-49" || CaseName == "c2-new" || CaseName == "snowc2" {
// 写入节点数量到ini文件
cfg.Section(u1.String()).Key("Node").SetValue(strconv.Itoa(Node))
cfg.Section(u1.String()).Key("Doamin").SetValue(Domain)
} else if CaseName == "aws-proxy" {
// 写入节点数量到ini文件
cfg.Section(u1.String()).Key("Node").SetValue(strconv.Itoa(Node))
} else if CaseName == "ddos" {
// 写入节点数量到ini文件
cfg.Section(u1.String()).Key("Node").SetValue(strconv.Itoa(Node))
} else if CaseName == "aliyun-proxy" {
// 写入节点数量到ini文件
cfg.Section(u1.String()).Key("Node").SetValue(strconv.Itoa(Node))
} else if CaseName == "dnslog" || CaseName == "xraydnslog" {
// 写入域名到ini文件
cfg.Section(u1.String()).Key("Doamin").SetValue(Domain)
} else if CaseName == "pss5" || CaseName == "frp" || CaseName == "frp-loki" || CaseName == "nps" {
// 写入base64命令到ini文件
cfg.Section(u1.String()).Key("Base64Command").SetValue(Base64Command)
}
err = cfg.SaveTo(filePath)
if err != nil {
fmt.Printf("写入 ini 时失败: %v", err)
RedcLog("写入 ini 时失败")
os.Exit(3)
}
fmt.Println("Case 路径", u1.String())
}
func CaseStop(ProjectPath string, UUID string) {
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
//fmt.Println(UUID)
if cfg.Section(UUID).Key("Type").String() == "cs-49" || cfg.Section(UUID).Key("Type").String() == "c2-new" || cfg.Section(UUID).Key("Type").String() == "snowc2" {
C2Destroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String(),
cfg.Section(UUID).Key("Domain").String())
} else if cfg.Section(UUID).Key("Type").String() == "aws-proxy" {
AwsProxyDestroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String())
} else if cfg.Section(UUID).Key("Type").String() == "ddos" {
DDOSDestroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String())
} else if cfg.Section(UUID).Key("Type").String() == "aliyun-proxy" {
AliyunProxyDestroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String())
} else if cfg.Section(UUID).Key("Type").String() == "dnslog" || cfg.Section(UUID).Key("Type").String() == "xraydnslog" {
DnslogDestroy(ProjectPath+"/"+UUID, cfg.Section(UUID).Key("Domain").String())
} else if cfg.Section(UUID).Key("Type").String() == "pss5" || cfg.Section(UUID).Key("Type").String() == "frp" || cfg.Section(UUID).Key("Type").String() == "frp-loki" || cfg.Section(UUID).Key("Type").String() == "nps" {
Base64Destroy(ProjectPath+"/"+UUID, cfg.Section(UUID).Key("Base64Command").String())
} else {
TfDestroy(ProjectPath + "/" + UUID)
}
// 成功销毁场景后,删除 case 文件夹
err = os.RemoveAll(ProjectPath + "/" + UUID)
if err != nil {
fmt.Println(err)
os.Exit(3)
}
// case 删除
cfg2, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
cfg2.DeleteSection(UUID)
err = cfg2.SaveTo(filePath)
if err != nil {
fmt.Printf("修改 ini 时失败: %v", err)
RedcLog("修改 ini 时失败")
os.Exit(3)
}
}
func CaseKill(ProjectPath string, UUID string) {
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
// 在次 init,防止万一
dirs := utils.ChechDirMain(ProjectPath + "/" + UUID)
for _, v := range dirs {
err := utils.CheckFileName(v, "tf")
if err {
TfInit(v)
}
}
if cfg.Section(UUID).Key("Type").String() == "cs-49" || cfg.Section(UUID).Key("Type").String() == "c2-new" || cfg.Section(UUID).Key("Type").String() == "snowc2" {
C2Destroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String(),
cfg.Section(UUID).Key("Domain").String())
} else if cfg.Section(UUID).Key("Type").String() == "aws-proxy" {
AwsProxyDestroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String())
} else if cfg.Section(UUID).Key("Type").String() == "aliyun-proxy" {
AliyunProxyDestroy(ProjectPath+"/"+UUID,
cfg.Section(UUID).Key("Node").String())
} else if cfg.Section(UUID).Key("Type").String() == "dnslog" || cfg.Section(UUID).Key("Type").String() == "xraydnslog" {
DnslogDestroy(ProjectPath, cfg.Section(UUID).Key("Domain").String())
} else if cfg.Section(UUID).Key("Type").String() == "pss5" || cfg.Section(UUID).Key("Type").String() == "frp" || cfg.Section(UUID).Key("Type").String() == "frp-loki" || cfg.Section(UUID).Key("Type").String() == "nps" {
Base64Destroy(ProjectPath+"/"+UUID, cfg.Section(UUID).Key("Base64Command").String())
} else {
TfDestroy(ProjectPath + "/" + UUID)
}
// 成功销毁场景后,删除 case 文件夹
err = os.RemoveAll(ProjectPath + "/" + UUID)
if err != nil {
fmt.Println(err)
os.Exit(3)
}
// case 删除
cfg2, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
cfg2.DeleteSection(UUID)
err = cfg2.SaveTo(filePath)
if err != nil {
fmt.Printf("修改 ini 时失败: %v", err)
os.Exit(3)
}
}
func CaseChange(ProjectPath string, UUID string) {
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
if cfg.Section(UUID).Key("Type").String() == "cs-49" || cfg.Section(UUID).Key("Type").String() == "c2-new" || cfg.Section(UUID).Key("Type").String() == "snowc2" {
C2Change(ProjectPath + "/" + UUID)
} else if cfg.Section(UUID).Key("Type").String() == "aliyun-proxy" {
AliyunProxyChange(ProjectPath + "/" + UUID)
} else {
fmt.Printf("不适用与当前场景")
os.Exit(3)
}
}
func CaseStatus(ProjectPath string, UUID string) {
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
fmt.Println("操作人员:", cfg.Section(UUID).Key("Operator").String())
fmt.Println("项目名称:", cfg.Section(UUID).Key("Name").String())
fmt.Println("场景类型:", cfg.Section(UUID).Key("Type").String())
fmt.Println("创建时间:", cfg.Section(UUID).Key("CreateTime").String())
TfStatus(ProjectPath + "/" + UUID)
}
func CaseList(ProjectPath string) {
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
w := tabwriter.NewWriter(os.Stdout, 15, 0, 1, ' ',
tabwriter.AlignRight)
fmt.Fprintln(w, "UUID\tType\tName\tOperator\tCreateTime\t")
for i := 1; i < len(cfg.SectionStrings()); i++ {
if cfg.Section(cfg.SectionStrings()[i]).Key("Operator").String() == U || U == "system" {
fmt.Fprintln(w, cfg.SectionStrings()[i], "\t", cfg.Section(cfg.SectionStrings()[i]).Key("Type").String(), "\t", cfg.Section(cfg.SectionStrings()[i]).Key("Name").String(), "\t", cfg.Section(cfg.SectionStrings()[i]).Key("Operator").String(), "\t", cfg.Section(cfg.SectionStrings()[i]).Key("CreateTime").String())
}
}
err = w.Flush()
if err != nil {
fmt.Printf("打印失败: %v", err)
os.Exit(3)
}
RandomName()
}
func CheckUser(ProjectPath string, UUID string) {
filePath := ProjectPath + "/project.ini"
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
// 鉴权
if cfg.Section(UUID).Key("Operator").String() != U && U != "system" {
fmt.Printf("用户 %v 无权访问 %v", U, UUID)
os.Exit(3)
}
}

48
mod/config.go Normal file
View File

@@ -0,0 +1,48 @@
package mod
import (
"fmt"
"gopkg.in/ini.v1"
"os"
)
// LoadConfig 加载配置文件
func LoadConfig(path string) {
_, err := os.Stat(path)
if err != nil {
// 没有配置文件,报错退出,提示进行修改
file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println("配置文件创建失败", err)
} else {
fmt.Println("已生成状态文件", path, "请自行修改")
cfg, err := ini.Load(path)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
cfg.Section("").Key("operator").SetValue("system")
cfg.Section("").Key("ALICLOUD_ACCESS_KEY").SetValue("changethis")
cfg.Section("").Key("ALICLOUD_SECRET_KEY").SetValue("changethis")
cfg.SaveTo(path)
}
defer file.Close()
}
}
// ParseConfig 解析配置文件
func ParseConfig(path string) (string, string) {
cfg, err := ini.Load(path)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(1)
}
ALICLOUD_ACCESS_KEY := cfg.Section("").Key("ALICLOUD_ACCESS_KEY").String()
ALICLOUD_SECRET_KEY := cfg.Section("").Key("ALICLOUD_SECRET_KEY").String()
return ALICLOUD_ACCESS_KEY, ALICLOUD_SECRET_KEY
}

54
mod/flag.go Normal file
View File

@@ -0,0 +1,54 @@
package mod
import "flag"
var (
V bool
Init bool
Cost bool
Fc bool
List bool
Debug bool
U string
Name string
Project string
Start string
Change string
Stop string
Kill string
Status string
Node int
Domain string
Base64Command string
Version = "v1.3.2(2024/03/18)(wgpsec)"
Durl string
Dtime int
Dnum int
Dmode string
)
func init() {
flag.BoolVar(&V, "version", false, "显示版本号")
flag.BoolVar(&Init, "init", false, "初始化")
flag.BoolVar(&Debug, "debug", false, "调试")
flag.StringVar(&U, "u", "system", "操作者")
flag.StringVar(&Project, "p", "default", "项目名称")
flag.BoolVar(&List, "list", false, "查看项目所有场景")
flag.BoolVar(&Fc, "fc", false, "查询云函数余量")
flag.BoolVar(&Cost, "cost", false, "查看余额")
flag.StringVar(&Start, "start", "", "开启case")
flag.StringVar(&Kill, "kill", "", "强制关闭case")
flag.StringVar(&Stop, "stop", "", "关闭case")
flag.StringVar(&Change, "change", "", "更改case状态 (c2场景是切换rg ip,代理池场景是重启代理池)")
flag.StringVar(&Status, "status", "", "查看case状态")
flag.StringVar(&Name, "name", "", "查看case状态")
flag.IntVar(&Node, "node", 10, "机器数量(默认10)")
flag.StringVar(&Domain, "domain", "www.amazon.com", "CS/dnslog的监听域名")
flag.StringVar(&Base64Command, "base64command", "", "frp/nps服务端配置(base64传入)")
flag.StringVar(&Durl, "durl", "", "ddos目标")
flag.IntVar(&Dtime, "dtime", 600, "ddos持续时间(默认10分钟)")
flag.IntVar(&Dnum, "dnum", 3500, "ddos线程数(默认3500)")
flag.StringVar(&Dmode, "dmode", "APACHE", "ddos模式")
}

60
mod/project.go Normal file
View File

@@ -0,0 +1,60 @@
package mod
import (
"fmt"
"gopkg.in/ini.v1"
"os"
)
func ProjectParse(ProjectPath string, ProjectName string, User string) {
// 确认项目文件夹是否存在,不存在就创建
_, err := os.Stat(ProjectPath)
if err != nil {
// 创建项目目录
err := os.MkdirAll(ProjectPath, os.ModePerm)
if err != nil {
fmt.Println(err)
os.Exit(3)
} else {
fmt.Println("已创建项目目录", ProjectPath)
}
// 创建项目状态文件
filePath := ProjectPath + "/project.ini"
file, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println("项目状态文件创建失败", err)
} else {
fmt.Println("已创建项目状态文件", filePath)
}
defer file.Close()
/*
// 写入项目创建时间,创建者
cfg, err := ini.Load(filePath)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
cfg.Section("Global").Key("ProjectName").SetValue(ProjectName)
cfg.Section("Global").Key("ProjectPath").SetValue(ProjectPath)
currentTime := time.Now().Format("2006-01-02 15:04:05")
cfg.Section("Global").Key("CreateTime").SetValue(currentTime)
cfg.Section("Global").Key("Operator").SetValue(User)
cfg.SaveTo(filePath)
*/
}
}
func ProjectConfigParse(path string) {
cfg, err := ini.Load(path)
if err != nil {
fmt.Printf("Fail to read file: %v", err)
os.Exit(3)
}
fmt.Println("项目名称:", cfg.Section("Global").Key("ProjectName").String())
fmt.Println("项目路径:", cfg.Section("Global").Key("ProjectPath").String())
fmt.Println("创建时间:", cfg.Section("Global").Key("CreateTime").String())
fmt.Println("创建人员:", cfg.Section("Global").Key("Operator").String())
}

24
mod/redclog.go Normal file
View File

@@ -0,0 +1,24 @@
package mod
import (
"fmt"
"os"
"red-cloud/mod2"
"time"
)
// RedcLog 将给定的消息记录到 "redc.log" 文件中。
func RedcLog(message string) {
// 打开或创建日志文件
file, err := os.OpenFile("redc.log", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
mod2.PrintOnError(err, "failed to open log file")
defer file.Close()
// 获取当前时间作为日志时间戳
timestamp := time.Now().Format("2006-01-02 15:04:05")
// 构造日志消息并写入文件
_, err = file.WriteString(fmt.Sprintf("[%s] %s\n", timestamp, message))
mod2.PrintOnError(err, "failed to write to log file")
}

452
mod/tf.go Normal file
View File

@@ -0,0 +1,452 @@
package mod
import (
"fmt"
"os"
"red-cloud/mod2"
"red-cloud/utils"
"strconv"
"strings"
"time"
)
// 第一次初始化
func TfInit0(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -init")
err := utils.Command("cd " + Path + " && bash deploy.sh -init")
//err := utils.Command("cd " + Path + " && terraform init")
if err != nil {
fmt.Println("场景初始化失败,再次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -init")
if err2 != nil {
fmt.Println("场景初始化失败,请检查网络连接!", err)
os.Exit(3)
}
}
}
// 复制后的初始化
func TfInit(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -init")
err := utils.Command("cd " + Path + " && bash deploy.sh -init")
//err := utils.Command("cd " + Path + " && terraform init")
if err != nil {
fmt.Println("场景初始化失败,再次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -init")
if err2 != nil {
fmt.Println("场景初始化失败,请检查网络连接!", err)
// 无法初始化,删除 case 文件夹
err = os.RemoveAll(Path)
if err != nil {
fmt.Println(err)
os.Exit(3)
}
os.Exit(3)
}
}
}
func TfApply(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -start")
err := utils.Command("cd " + Path + " && bash deploy.sh -start")
if err != nil {
fmt.Println("场景创建失败!尝试重新创建!")
// 先关闭
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop")
if err2 != nil {
fmt.Println("场景销毁,等待重新创建!")
os.Exit(3)
}
// 重新创建
err3 := utils.Command("cd " + Path + " && bash deploy.sh -start")
if err3 != nil {
fmt.Println("场景创建第二次失败!请手动排查问题")
fmt.Println("path路径: ", Path)
os.Exit(3)
}
}
}
func TfStatus(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -status")
err := utils.Command("cd " + Path + " && bash deploy.sh -status")
if err != nil {
fmt.Println("场景状态查询失败!请手动排查问题")
fmt.Println("path路径: ", Path)
os.Exit(3)
}
}
func TfDestroy(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop")
err := utils.Command("cd " + Path + " && bash deploy.sh -stop")
if err != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop")
if err2 != nil {
fmt.Println("场景销毁失败,第三次尝试!", err)
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop")
if err3 != nil {
fmt.Println("场景销毁多次重试失败!请手动排查问题")
fmt.Println("path路径: ", Path)
os.Exit(3)
}
}
}
}
func C2Apply(Path string) {
// 先开c2
err := utils.Command("cd " + Path + " && bash deploy.sh -step1")
if err != nil {
fmt.Println("场景创建失败,自动销毁场景!")
RedcLog("场景创建失败,自动销毁场景!")
C2Destroy(Path, strconv.Itoa(Node), Domain)
// 成功销毁场景后,删除 case 文件夹
err = os.RemoveAll(Path)
os.Exit(3)
}
// 开rg
if Node != 0 {
err = utils.Command("cd " + Path + " && bash deploy.sh -step2 " + strconv.Itoa(Node) + " " + Domain)
if err != nil {
fmt.Println("场景创建失败,自动销毁场景!")
RedcLog("场景创建失败,自动销毁场景!")
C2Destroy(Path, strconv.Itoa(Node), Domain)
// 成功销毁场景后,删除 case 文件夹
err = os.RemoveAll(Path)
os.Exit(3)
}
}
// 获得本地几个变量
c2_ip := utils.Command2("cd " + Path + " && cd c2-ecs" + "&& terraform output -json ecs_ip | jq '.' -r")
c2_pass := utils.Command2("cd " + Path + " && cd c2-ecs" + "&& terraform output -json ecs_password | jq '.' -r")
cs_port := "8080"
cs_pass := "q!w@e#raa1dd2ff3gg4"
cs_domain := Domain
ssh_ip := c2_ip + ":22"
// 去掉该死的换行符
ssh_ip = strings.Replace(ssh_ip, "\n", "", -1)
c2_pass = strings.Replace(c2_pass, "\n", "", -1)
c2_ip = strings.Replace(c2_ip, "\n", "", -1)
time.Sleep(time.Second * 60)
// ssh上去起teamserver
if Node != 0 {
ipsum := utils.Command2("cd " + Path + "&& cd zone-node && cat ipsum.txt")
ecs_main_ip := utils.Command2("cd " + Path + "&& cd zone-node && cat ecs_main_ip.txt")
ipsum = strings.Replace(ipsum, "\n", "", -1)
ecs_main_ip = strings.Replace(ecs_main_ip, "\n", "", -1)
cscommand := "setsid ./teamserver -new " + cs_port + " " + c2_ip + " " + cs_pass + " " + cs_domain + " " + ipsum + " " + ecs_main_ip + " > /dev/null 2>&1 &"
fmt.Println("cscommand: ", cscommand)
err = utils.Gotossh("root", c2_pass, ssh_ip, cscommand)
if err != nil {
mod2.PrintOnError(err, "ssh 过程出现报错!自动销毁场景")
RedcLog("ssh 过程出现报错!自动销毁场景")
C2Destroy(Path, strconv.Itoa(Node), Domain)
// 成功销毁场景后,删除 case 文件夹
err = os.RemoveAll(Path)
os.Exit(3)
}
} else {
cscommand := "setsid ./teamserver -new " + cs_port + " " + c2_ip + " " + cs_pass + " " + cs_domain + " > /dev/null 2>&1 &"
fmt.Println("cscommand: ", cscommand)
err = utils.Gotossh("root", c2_pass, ssh_ip, cscommand)
if err != nil {
mod2.PrintOnError(err, "ssh 过程出现报错!自动销毁场景")
RedcLog("ssh 过程出现报错!自动销毁场景")
C2Destroy(Path, strconv.Itoa(Node), Domain)
// 成功销毁场景后,删除 case 文件夹
err = os.RemoveAll(Path)
os.Exit(3)
}
}
fmt.Println("ssh结束!")
err = utils.Command("cd " + Path + " && bash deploy.sh -status")
if err != nil {
mod2.PrintOnError(err, "场景创建失败")
RedcLog("场景创建失败")
os.Exit(3)
}
}
func C2Change(Path string) {
// 重开rg
fmt.Println("cd " + Path + " && bash deploy.sh -step3 " + strconv.Itoa(Node) + " " + Domain)
err := utils.Command("cd " + Path + " && bash deploy.sh -step3 " + strconv.Itoa(Node) + " " + Domain)
if err != nil {
mod2.PrintOnError(err, "场景更改失败")
os.Exit(3)
}
// 获得本地几个变量
c2_ip := utils.Command2("cd " + Path + " && cd c2-ecs" + "&& terraform output -json ecs_ip | jq '.' -r")
c2_pass := utils.Command2("cd " + Path + " && cd c2-ecs" + "&& terraform output -json ecs_password | jq '.' -r")
ipsum := utils.Command2("cd " + Path + "&& cd zone-node && cat ipsum.txt")
ecs_main_ip := utils.Command2("cd " + Path + "&& cd zone-node && cat ecs_main_ip.txt")
cs_port := "8080"
cs_pass := "q!w@e#raa1dd2ff3gg4"
cs_domain := "360.com"
ssh_ip := c2_ip + ":22"
// 去掉该死的换行符
ssh_ip = strings.Replace(ssh_ip, "\n", "", -1)
c2_pass = strings.Replace(c2_pass, "\n", "", -1)
c2_ip = strings.Replace(c2_ip, "\n", "", -1)
ipsum = strings.Replace(ipsum, "\n", "", -1)
ecs_main_ip = strings.Replace(ecs_main_ip, "\n", "", -1)
cscommand := "setsid ./teamserver -changelistener1 " + cs_port + " " + c2_ip + " " + cs_pass + " " + cs_domain + " " + ipsum + " " + ecs_main_ip + " > /dev/null 2>&1 &"
// ssh上去起teamserver
utils.Gotossh("root", c2_pass, ssh_ip, cscommand)
}
func C2Destroy(Path string, Command1 string, Domain string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop " + Command1 + " " + Domain)
err := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1 + " " + Domain)
if err != nil {
fmt.Println("场景销毁失败,第一次尝试!", err)
RedcLog("场景销毁失败,第一次尝试!")
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1 + " " + Domain)
if err2 != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
RedcLog("场景销毁失败,第二次尝试!")
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1 + " " + Domain)
if err3 != nil {
fmt.Println("场景销毁失败!")
RedcLog("场景销毁失败")
os.Exit(3)
}
}
}
}
func AwsProxyApply(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -start " + strconv.Itoa(Node))
err := utils.Command("cd " + Path + " && bash deploy.sh -start " + strconv.Itoa(Node))
if err != nil {
fmt.Println("场景创建失败!")
RedcLog("场景创建失败!")
os.Exit(3)
}
}
func AwsProxyDestroy(Path string, Command1 string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop " + Command1)
err := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1)
if err != nil {
fmt.Println("场景销毁失败,第一次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1)
if err2 != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1)
if err3 != nil {
fmt.Println("场景销毁失败!")
RedcLog("场景销毁失败!")
os.Exit(3)
}
}
}
}
func DDOSApply(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -start " + strconv.Itoa(Node) + " " + Durl + " " + strconv.Itoa(Dnum) + " " + strconv.Itoa(Dtime) + " " + Dmode)
err := utils.Command("cd " + Path + " && bash deploy.sh -start " + strconv.Itoa(Node) + " " + Durl + " " + strconv.Itoa(Dnum) + " " + strconv.Itoa(Dtime) + " " + Dmode)
if err != nil {
fmt.Println("场景创建失败!")
RedcLog("场景创建失败!")
os.Exit(3)
}
}
func DDOSDestroy(Path string, Command1 string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop " + Command1 + "1 1 1 1")
err := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1 + "1 1 1 1")
if err != nil {
fmt.Println("场景销毁失败,第一次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1 + "1 1 1 1")
if err2 != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1 + "1 1 1 1")
if err3 != nil {
fmt.Println("场景销毁失败!")
RedcLog("场景销毁失败!")
os.Exit(3)
}
}
}
}
func AliyunProxyApply(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -start " + strconv.Itoa(Node))
err := utils.Command("cd " + Path + " && bash deploy.sh -start " + strconv.Itoa(Node))
if err != nil {
fmt.Println("场景创建失败!")
RedcLog("场景创建失败!")
os.Exit(3)
}
}
func AliyunProxyChange(Path string) {
// 重开proxy
fmt.Println("cd " + Path + " && bash deploy.sh -change " + strconv.Itoa(Node))
err := utils.Command("cd " + Path + " && bash deploy.sh -change " + strconv.Itoa(Node))
if err != nil {
fmt.Println("场景更改失败!")
RedcLog("场景更改失败!")
os.Exit(3)
}
}
func AliyunProxyDestroy(Path string, Command1 string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop " + Command1)
err := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1)
if err != nil {
fmt.Println("场景销毁失败,第一次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1)
if err2 != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Command1)
if err3 != nil {
fmt.Println("场景销毁失败!")
RedcLog("场景销毁失败!")
os.Exit(3)
}
}
}
}
func DnslogApply(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -start " + Domain)
err := utils.Command("cd " + Path + " && bash deploy.sh -start " + Domain)
if err != nil {
fmt.Println("场景创建失败!")
RedcLog("场景创建失败!")
os.Exit(3)
}
}
func DnslogDestroy(Path string, Domain string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop " + Domain)
err := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Domain)
if err != nil {
fmt.Println("场景销毁失败,第一次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Domain)
if err2 != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Domain)
if err3 != nil {
fmt.Println("场景销毁失败!")
RedcLog("场景销毁失败!")
os.Exit(3)
}
}
}
}
func Base64Apply(Path string) {
fmt.Println("cd " + Path + " && bash deploy.sh -start " + Base64Command)
err := utils.Command("cd " + Path + " && bash deploy.sh -start " + Base64Command)
if err != nil {
fmt.Println("场景创建失败!")
RedcLog("场景创建失败!")
os.Exit(3)
}
}
func Base64Destroy(Path string, Base64Command string) {
fmt.Println("cd " + Path + " && bash deploy.sh -stop " + Base64Command)
err := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Base64Command)
if err != nil {
fmt.Println("场景销毁失败,第一次尝试!", err)
// 如果初始化失败就再次尝试一次
err2 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Base64Command)
if err2 != nil {
fmt.Println("场景销毁失败,第二次尝试!", err)
// 第三次
err3 := utils.Command("cd " + Path + " && bash deploy.sh -stop " + Base64Command)
if err3 != nil {
fmt.Println("场景销毁失败!")
RedcLog("场景销毁失败!")
os.Exit(3)
}
}
}
}

21
mod2/error.go Normal file
View File

@@ -0,0 +1,21 @@
package mod2
import (
"fmt"
"os"
)
// PrintOnError 错误处理
func PrintOnError(err error, msg string) {
if err != nil {
fmt.Println("%s: %s", msg, err)
}
}
// ExitOnError 退出
func ExitOnError(err error, msg string) {
if err != nil {
fmt.Println("%s: %s", msg, err)
os.Exit(0)
}
}

5
push.sh Normal file
View File

@@ -0,0 +1,5 @@
# git config user.email mazhaojie@snowtech.com.cn
# git config user.email
git add -A
git commit -m "[feat]:update [修改说明]:update"
git push

129
utils/exec.go Normal file
View File

@@ -0,0 +1,129 @@
package utils
import (
"bufio"
"fmt"
"golang.org/x/text/encoding/simplifiedchinese"
"io"
"log"
"os"
"os/exec"
"sync"
)
// 参考 https://www.cnblogs.com/we8fans/p/14031109.html
type Charset string
const (
UTF8 = Charset("UTF-8")
GB18030 = Charset("GB18030")
)
func Command(cmd string) error {
c := exec.Command("bash", "-c", cmd) // mac or linux
stdout, err := c.StdoutPipe()
if err != nil {
return err
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
reader := bufio.NewReader(stdout)
for {
readString, err := reader.ReadString('\n')
if err != nil || err == io.EOF {
return
}
byte2String := ConvertByte2String([]byte(readString), "GB18030")
fmt.Print(byte2String)
}
}()
err = c.Run()
wg.Wait()
return err
}
func CommandUTF(cmd string) error {
c := exec.Command("bash", "-c", cmd) // mac or linux
stdout, err := c.StdoutPipe()
if err != nil {
return err
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
reader := bufio.NewReader(stdout)
for {
readString, err := reader.ReadString('\n')
if err != nil || err == io.EOF {
return
}
byte2String := ConvertByte2String([]byte(readString), "UTF-8")
fmt.Print(byte2String)
}
}()
err = c.Run()
wg.Wait()
return err
}
func NoPrintCommand(cmd string) error {
c := exec.Command("bash", "-c", cmd) // mac or linux
stdout, err := c.StdoutPipe()
if err != nil {
return err
}
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
reader := bufio.NewReader(stdout)
for {
readString, err := reader.ReadString('\n')
if err != nil || err == io.EOF {
return
}
byte2String := ConvertByte2String([]byte(readString), "GB18030")
f, err := os.OpenFile("log.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, os.ModePerm)
if err != nil {
return
}
defer func() {
f.Close()
}()
log.SetOutput(f)
log.Println(byte2String)
}
}()
err = c.Run()
wg.Wait()
return err
}
func ConvertByte2String(byte []byte, charset Charset) string {
var str string
switch charset {
case GB18030:
var decodeBytes, _ = simplifiedchinese.GB18030.NewDecoder().Bytes(byte)
str = string(decodeBytes)
case UTF8:
fallthrough
default:
str = string(byte)
}
return str
}
func Command2(cmd string) string {
c2 := exec.Command("bash", "-c", cmd)
out, err := c2.Output()
if err != nil {
fmt.Printf(cmd + " 运行失败")
os.Exit(3)
}
return string(out)
}

161
utils/io.go Normal file
View File

@@ -0,0 +1,161 @@
package utils
import (
"embed"
"fmt"
"io"
"io/ioutil"
"os"
"path"
"strings"
)
//go:embed redc-templates
var local embed.FS
var dirs []string
// File copies a single file from src to dst
func File(src, dst string) error {
var err error
var srcfd *os.File
var dstfd *os.File
var srcinfo os.FileInfo
if srcfd, err = os.Open(src); err != nil {
return err
}
defer srcfd.Close()
if dstfd, err = os.Create(dst); err != nil {
return err
}
defer dstfd.Close()
if _, err = io.Copy(dstfd, srcfd); err != nil {
return err
}
if srcinfo, err = os.Stat(src); err != nil {
return err
}
return os.Chmod(dst, srcinfo.Mode())
}
// Dir copies a whole directory recursively
func Dir(src string, dst string) error {
var err error
var fds []os.FileInfo
var srcinfo os.FileInfo
if srcinfo, err = os.Stat(src); err != nil {
return err
}
if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil {
return err
}
if fds, err = ioutil.ReadDir(src); err != nil {
return err
}
for _, fd := range fds {
srcfp := path.Join(src, fd.Name())
dstfp := path.Join(dst, fd.Name())
if fd.IsDir() {
if err = Dir(srcfp, dstfp); err != nil {
//fmt.Println(err)
}
} else {
if err = File(srcfp, dstfp); err != nil {
//fmt.Println(err)
}
}
}
return nil
}
func GetFilesAndDirs(dirPth string) (files []string, dirs []string) {
dir, err := ioutil.ReadDir(dirPth)
if err != nil {
os.Exit(3)
}
PthSep := string(os.PathSeparator)
//suffix = strings.ToUpper(suffix) //忽略后缀匹配的大小写
for _, fi := range dir {
if fi.IsDir() { // 目录, 递归遍历
dirs = append(dirs, dirPth+PthSep+fi.Name())
GetFilesAndDirs(dirPth + PthSep + fi.Name())
} else {
// 过滤指定格式
ok := strings.HasSuffix(fi.Name(), ".go")
if ok {
files = append(files, dirPth+PthSep+fi.Name())
}
}
}
return files, dirs
}
// ReleaseDir 释放文件夹
func ReleaseDir(path string) {
dirs, _ := local.ReadDir(path)
for _, entry := range dirs {
if entry.IsDir() {
//_ = utils.Dir(path+"/"+entry.Name(), path+"/"+entry.Name())
err := os.MkdirAll(path+"/"+entry.Name(), os.ModePerm)
if err != nil {
fmt.Println(err)
return
}
ReleaseDir(path + "/" + entry.Name())
} else {
//_ = utils.File(path+"/"+entry.Name(), path+"/"+entry.Name())
out, _ := os.Create(path + "/" + entry.Name())
in, _ := local.Open(path + "/" + entry.Name())
_, err := io.Copy(out, in)
if err != nil {
fmt.Println(err)
return
}
in.Close()
}
}
}
// ChechDirMain 递归
func ChechDirMain(dirPth string) []string {
ChechDirSub(dirPth)
return dirs
}
func ChechDirSub(dirPth string) {
dir, err := ioutil.ReadDir(dirPth)
if err != nil {
os.Exit(3)
}
PthSep := string(os.PathSeparator)
for _, fi := range dir {
if fi.IsDir() { // 目录, 递归遍历
dirs = append(dirs, dirPth+PthSep+fi.Name())
ChechDirSub(dirPth + "/" + fi.Name())
}
}
}
func CheckFileName(path string, key string) bool {
files, err := ioutil.ReadDir(path)
if err == nil {
for _, fileInfo := range files {
if strings.Contains(fileInfo.Name(), key) {
return true
}
}
}
return false
}

View File

@@ -0,0 +1,45 @@
init(){
terraform init
}
start_ecs(){
terraform apply -auto-approve
}
stop_ecs(){
terraform destroy -auto-approve
}
status_ecs(){
terraform output
}
case "$1" in
-init)
init
;;
-start)
start_ecs
;;
-stop)
stop_ecs
;;
-status)
status_ecs
;;
-h)
echo -e "\033[1;34m使用 -init 初始化\033[0m"
echo -e "\033[1;34m使用 -start 启动\033[0m"
echo -e "\033[1;34m使用 -stop 关闭\033[0m"
echo -e "\033[1;34m使用 -status 查询状态\033[0m"
exit 1
;;
esac

View File

@@ -0,0 +1,91 @@
provider "random" {}
resource "random_password" "password" {
length = 10
special = true
override_special = "_%@"
}
resource "alicloud_instance" "instance" {
security_groups = alicloud_security_group.group.*.id
instance_type = "ecs.c6.large"
image_id = "debian_12_2_x64_20G_alibase_20231012.vhd"
instance_name = "aliyun_bj_ecs"
vswitch_id = alicloud_vswitch.vswitch.id
system_disk_size = 20
internet_max_bandwidth_out = 100
password = random_password.password.result
user_data = <<EOF
#!/bin/bash
sudo apt-get update
sudo apt-get install -y ca-certificates
sudo apt-get install -y curl
sudo apt-get -y install wget
sudo apt-get install -y lrzsz
sudo apt-get install -y tmux
sudo apt-get install -y unzip
sudo echo "set-option -g history-limit 20000" >> ~/.tmux.conf
sudo echo "set -g mouse on" >> ~/.tmux.conf
sudo wget http://update.aegis.aliyun.com/download/uninstall.sh
sudo chmod +x uninstall.sh
sudo ./uninstall.sh
sudo wget http://update.aegis.aliyun.com/download/quartz_uninstall.sh
sudo chmod +x quartz_uninstall.sh
sudo ./quartz_uninstall.sh
sudo pkill aliyun-service
sudo rm -fr /etc/init.d/agentwatch /usr/sbin/aliyun-service
sudo rm -rf /usr/local/aegis*
sudo rm /usr/sbin/aliyun-service
sudo rm /lib/systemd/system/aliyun.service
sudo systemctl stop aliyun.service
sudo wget -O simplehttpserver_0.0.5_linux_amd64.tar.gz 'https://pub-4cbde83fd01f4fe98e2672c3b1f14315.r2.dev/file-server/simplehttpserver_0.0.5_linux_amd64.tar.gz'
sudo tar -zxvf simplehttpserver_0.0.5_linux_amd64.tar.gz
sudo mv --force simplehttpserver /usr/local/bin/simplehttpserver
sudo chmod +x /usr/local/bin/simplehttpserver
sudo curl -o f8x https://f8x.io/ && mv --force f8x /usr/local/bin/f8x && chmod +x /usr/local/bin/f8x
sudo apt-get install -y python3-pip
sudo pip3 install trzsz --break-system-packages
EOF
}
resource "alicloud_security_group" "group" {
name = "test_security_group"
vpc_id = alicloud_vpc.vpc.id
}
resource "alicloud_security_group_rule" "allow_all_tcp" {
type = "ingress"
ip_protocol = "tcp"
nic_type = "intranet"
policy = "accept"
port_range = "1/65535"
priority = 1
security_group_id = alicloud_security_group.group.id
cidr_ip = "0.0.0.0/0"
depends_on = [
alicloud_security_group.group
]
}
resource "alicloud_vswitch" "vswitch" {
vpc_id = alicloud_vpc.vpc.id
cidr_block = "172.16.0.0/24"
zone_id = data.alicloud_zones.default.zones[0].id
vswitch_name = "aliyun_zjk_vswitch"
}
resource "alicloud_vpc" "vpc" {
vpc_name = "test_vpc"
cidr_block = "172.16.0.0/16"
}
data "alicloud_zones" "default" {
available_resource_creation = "VSwitch"
available_instance_type = "ecs.c6.large"
}

View File

@@ -0,0 +1,8 @@
output "ecs_ip" {
value = "${alicloud_instance.instance.public_ip}"
description = "ip"
}
output "ecs_password" {
value = nonsensitive(random_password.password.result)
description = "vps password."
}

View File

@@ -0,0 +1,13 @@
terraform {
required_providers {
alicloud = {
source = "aliyun/alicloud"
version = "1.190.0"
}
}
}
provider "alicloud" {
profile = "cloud-tool"
region = "cn-beijing"
}

45
utils/ssh.go Normal file
View File

@@ -0,0 +1,45 @@
package utils
import (
"bytes"
"fmt"
"golang.org/x/crypto/ssh"
"red-cloud/mod2"
)
func Gotossh(username string, password string, addr string, cmd string) error {
//fmt.Println("ssh上去起teamserver!")
config := &ssh.ClientConfig{
User: username,
Auth: []ssh.AuthMethod{
ssh.Password(password),
},
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
}
client, err := ssh.Dial("tcp", addr, config)
if err != nil {
mod2.PrintOnError(err, "Failed to dial")
return err
}
defer client.Close()
// 开启一个session用于执行一个命令
session, err := client.NewSession()
if err != nil {
mod2.PrintOnError(err, "Failed to create session")
return err
}
defer session.Close()
// 执行命令,并将执行的结果写到 b 中
var b bytes.Buffer
session.Stdout = &b
// 也可以使用 session.CombinedOutput() 整合输出
if err := session.Run(cmd); err != nil {
mod2.PrintOnError(err, "Failed to run")
return err
}
fmt.Println(b.String())
return err
}