美文网首页
系统编写练习:命令执行系统

系统编写练习:命令执行系统

作者: 我就是小政政 | 来源:发表于2020-05-19 00:54 被阅读0次
需求:

编辑常用的命令,在某台主机上远程发送命令到其他主机并执行。能够查看操作记录、执行状态。

后续扩展

开发累积命令,小命令组合->大命令,中间件,远程部署、升级、监控、运维多个不同产品集群,多用户,CICD流程,安全防护,开发质量分析等

系统:

centos7

语言:

golang、shell等

通用字段:

id、created_at、modified_at、deleted_at

主机表(host)

user(账号)、ip、passwd

操作表(operation)

name、description、language(golang、shell)、input(输入字段名称及类型)、impl(硬编码实现、shell语句)【通过input达到的可能的扩展:多版本、安装、卸载、升级、回滚、重试】

操作记录表(operation_record)

target(user:1234@ip)、operation_id、operation_name、params(参数)、result(结果)
配置代理服务器

下载golang依赖包时,如果不能翻墙,需要配置代理服务器来加速包的下载安装

阿里云: export GOPROXY=https://mirrors.aliyun.com/goproxy/
nexus社区:export GOPROXY=https://gonexus.dev
goproxy.io:export GOPROXY=https://goproxy.io/
athens:export GOPROXY=https://athens.azurefd.net
官方提供的(jfrog,golang):
export GOPROXY=https://gocenter.io 
export GOPROXY=https://proxy.golang.org
七牛云:export GOPROXY=https://goproxy.cn 
go mod管理

go mod 管理
echo 'package main // import "infra_cbb"' >main.go
go mod init 初始化
go mod tidy 整理依赖包
go mod vendor 项目中创建vendor目录来保存依赖包

包搜索顺序
  1. vendor的层级搜索
    规则是:
    从引用文件所在的vendor路径下面搜索,
    如果没有找到,那么从上层目录的vendor路径下面搜索,
    直到src的vendor路径下面搜索。
  1. modules
    Go 1.11版本支持临时环境变量GO111MODULE,通过该环境变量来控制依赖包的管理方式。当GO111MODULE的值为on时,那么就会使用modules功能,
这种模式下,$GOPATH不再作为build时导入的角色,依赖包会存放在$GOPATH/pkg/mod目录下。
工程中的依赖包也会从此目录下查找。
有关该功能的介绍,可以看Go1.1.1新功能module的介绍及使用。
  1. 查找顺序

GO111MODULE=off时,如果一个包在vendor和$GOPATH下都存在,那么使用顺序为:

优先使用vendor目录下面的包,
如果vendor下面没有搜索到,再搜索$GOPATH/src下面的包,
如果$GOPATH下面没有搜索到,那么搜索$GOROOT/src下面的包,
要么完整使用vendor下面的包,要么完整使用$GOPATH下面的包,不会混合使用。
常用替换

go.mod中加入

replace (
    cloud.google.com/go => github.com/googleapis/google-cloud-go v0.43.0

    golang.org/x/crypto => github.com/golang/crypto v0.0.0-20190701094942-4def268fd1a4

    golang.org/x/exp => github.com/golang/exp v0.0.0-20190731235908-ec7cb31e5a56

    golang.org/x/image => github.com/golang/image v0.0.0-20190802002840-cff245a6509b

    golang.org/x/lint => github.com/golang/lint v0.0.0-20190409202823-959b441ac422

    golang.org/x/mobile => github.com/golang/mobile v0.0.0-20190806162312-597adff16ade

    golang.org/x/mod => github.com/golang/mod v0.1.0

    golang.org/x/net => github.com/golang/net v0.0.0-20190724013045-ca1201d0de80

    golang.org/x/oauth2 => github.com/golang/oauth2 v0.0.0-20190604053449-0f29369cfe45

    golang.org/x/sync => github.com/golang/sync v0.0.0-20190423024810-112230192c58

    golang.org/x/sys => github.com/golang/sys v0.0.0-20190804053845-51ab0e2deafa

    golang.org/x/text => github.com/golang/text v0.3.2

    golang.org/x/time => github.com/golang/time v0.0.0-20190308202827-9d24e82272b4

    golang.org/x/tools => github.com/golang/tools v0.0.0-20190808195139-e713427fea3f

    golang.org/x/xerrors => github.com/golang/xerrors v0.0.0-20190717185122-a985d3407aa7

    google.golang.org/api => github.com/googleapis/google-api-go-client v0.7.0

    google.golang.org/appengine => github.com/golang/appengine v1.6.1

    google.golang.org/genproto => github.com/google/go-genproto v0.0.0-20190801165951-fa694d86fc64

    google.golang.org/grpc => github.com/grpc/grpc-go v1.22.1
)
通过xorm逆向生成models代码

下载编译xorm
go get github.com/go-sql-driver/mysql
go get github.com/go-xorm/cmd/xorm
执行
../../../../../bin/xorm reverse mysql "root:root@tcp(192.168.199.107:3308)/infra_cbb?charset=utf8" templates/goxorm ~/workspace_personal/infra_cbb

说明
"root:root@tcp(192.168.199.107:3308)/infra_cbb?charset=utf8" 
"账号:密码@tcp(ip:port)/database?charset=utf8"

这样生成了host.go、operation.go、operation_record.go 先放个models文件夹里

指针与取地址

知道一个变量的内存地址,就可以用指针去操作它
A类型,* A 指针类型,* A a 指针类型的变量a(a只能存地址),B类型,B b B类型的变量b(b存放具体值),B{xxx}B类型实例对象,&B{xxx} B类型实例对象的地址

封装启动器

思想:将各个启动项封装为对象,加入启动列表,程序启动调用各个对象的启动方法。声明启动上下文,将启动项的对象实例写入。

  1. 创建common文件夹放一些基础类
  2. 封装个自己的日志系统,日志是快速定位问题,同时也是明确责任是否甩锅的基础中的基础,必须逻辑清晰,后面日志分析也方便。先简单定义两个,在common文件夹里编写log.go
    go get github.com/sirupsen/logrus
package common

import (
    "fmt"
    "github.com/sirupsen/logrus"
)

// 流程名
const flowBoot = "启动器"

func logInfo(flow, operation, input interface{} ,args ...interface{}) {
    template := fmt.Sprintf("流程:%v,操作:%v,状态:成功,输入:%v", flow, operation, input)
    logrus.Infof(template, args...)
}

func logPanic(flow, operation, input, err interface{},args ...interface{}) {
    template := fmt.Sprintf("流程:%v,操作:%v,状态:失败,输入:%v,错误:%v", flow, operation, input, err)
    logrus.Panicf(template, args...)
}


  1. 在common文件夹里starter.go编写Starter接口,统一启动器的方法,先简单写几个方法
type Starter interface {
    Name() string // 名称
    Load() // 加载
    Instance() interface{}// 获取客户端实例
    Order() int // 加载顺序
}
  1. starter.go里继续编写启动注册器(内部就是个数组),接收启动器的注册、被调用时查询(add、get),因为启动器们有优先级,所以单独为[]Stater定义了类型并实现了sort功能需要的接口Len、Less、Swap
/**
 * 启动器注册器
 */
type starterRegister struct {
    starters starters
}

/**
 * 注册方法
 */
func (this *starterRegister) Add(starter Starter)  {
    this.starters = append(this.starters, starter)
}

func (this *starterRegister)Get() starters {
    return this.starters
}

// 实例
var StarterRegister = new(starterRegister)

// 数组实现排序相关方法
type starters []Starter

func (this starters) Len() int {
    return len(this)
}
func (this starters) Less(i, j int) bool {
    return this[i].Order() < this[j].Order()
}
func (this starters) Swap(i, j int) {
    this[i], this[j] = this[j], this[I]
}

  1. 在common文件夹里编写boot.go,定义一个程序启动器+全局上下文(为了像ioc容器那样有个全局容器),定义Run方法调度启动器
package common

import (
    "sort"
)

// 定义app上下文
type appCtx map[string]interface{}

// app上下文实例
var AppCtx appCtx = make(appCtx)

// 定义一个启动器
type BootApplication struct {}

// 启动器运行
func (this *BootApplication)Run()  {
    starters:=StarterRegister.Get()
    sort.Sort(starters)
    for _, starter := range starters {
        starter.Load()
        if starter.Instance() != nil {
            AppCtx[starter.Name()]=starter.Instance()
            logInfo(flowBoot,"%v写入AppCtx","nil",starter.Name())
        }
    }
}
  1. 编写程序入口,创建文件夹brun,里面创建main.go调一下程序启动器
package main // import "infra_cbb"
import "infra_cbb/common"

func main() {
    logrus.Info("程序启动^_^")
    new(common.BootApplication).Run()
}
配置加载启动器

因为用配置文件比较通用、方便,更好的动态配置略微麻烦些,先放一个配置文件在brun文件夹里config.yaml(yaml格式也很通用,用这个什么结构化都支持了)。在common文件夹里创建starter_viper.go,里面init方法会自动执行,直接注册到启动注册器数组里
go get github.com/spf13/viper

package common

import (
    "github.com/spf13/viper"
)

/**
 * 注册到启动注册器
 */
func init() {
    StarterRegister.Add(new(ViperStarter))
}

type ViperStarter struct {}

func (v *ViperStarter) Name() string {
    return "ViperStarter"
}

func (v *ViperStarter) Load() {
    viper.SetConfigName("config")
    viper.SetConfigType("yaml")
    viper.AddConfigPath("brun/")
    err := viper.ReadInConfig()
    if err != nil {
        logPanic(flowBoot,"加载%v","configFile->%v",
            err,v.Name(),viper.ConfigFileUsed())
    }
    logInfo(flowBoot,"加载%v","configFile->%v",v.Name(),viper.ConfigFileUsed())
}

func (v *ViperStarter) Instance() interface{} {
    return nil
}

func (v *ViperStarter) Order() int {
    return 0 // 顺位0
}
xorm数据库ORM包
  1. 因为用到数据库这个必须有,先在brun文件夹里的config.yaml编写配置
db:
  driverName: mysql
  database: infra_cbb
  uname: root
  passwd: root
  addr: 192.168.199.107:3308
  connMaxLifetime: 12
  maxIdleConns: 1
  maxOpenConns: 3
  showSQL: true
  1. 在common文件夹编写starter_xorm.go,把xorm的单例客户端加入全局上下文
    go get github.com/go-sql-driver/mysql
    go get github.com/go-xorm/xorm
package common

import (
    "fmt"
    _ "github.com/go-sql-driver/mysql"
    "github.com/go-xorm/xorm"
    "github.com/spf13/viper"
    "time"
    "xorm.io/core"
)

/**
 * 注册到启动注册器
 */
func init() {
    StarterRegister.Add(new(XormStarter))
}

type XormStarter struct{
    engine *xorm.Engine
}

func (x *XormStarter) Name() string {
    return "XormStarter"
}

func (x *XormStarter) Load() {
    // 获取配置
    driverName := viper.GetString("db.driverName")
    uname := viper.GetString("db.uname")
    passwd := viper.GetString("db.passwd")
    addr := viper.GetString("db.addr")
    database := viper.GetString("db.database")
    showSQL := viper.GetBool("db.showSQL")
    maxOpenConn := viper.GetInt("db.maxOpenConns")
    maxIdleConns:= viper.GetInt("db.maxIdleConns")
    connMaxLifetime:=viper.GetDuration("db.connMaxLifetime")
    dataSource := fmt.Sprintf("%v:%v@tcp(%v)/%v?charset=utf8&parseTime=true&loc=Local",
        uname, passwd, addr, database)
    sqlLogLevel := core.LOG_DEBUG
    // 创建连接
    engine, err := xorm.NewEngine(driverName, dataSource)
    if err != nil {
        logPanic(flowBoot,"加载%v","driverName->%v;dataSource->%v",
            err,x.Name(),driverName,dataSource)
    }
    //设置日志显示
    engine.ShowSQL(showSQL)
    engine.SetLogLevel(sqlLogLevel)
    //设置连接池
    engine.SetMaxOpenConns(maxOpenConn)
    engine.SetMaxIdleConns(maxIdleConns)
    engine.SetConnMaxLifetime(connMaxLifetime * time.Hour)

    x.engine = engine
    logInfo(flowBoot,"加载%v",
        "driverName->%v;dataSource->%v;showSQL->%v;SQLLogLevel->%v;maxOpenConn->%v;maxIdleConns->%v;connMaxLifetime->%v",
        x.Name(),driverName,dataSource,showSQL,sqlLogLevel,maxOpenConn,maxIdleConns,connMaxLifetime * time.Hour)
}

func (x *XormStarter) Instance() interface{} {
    return x.engine // 返回单例
}

func (x *XormStarter) Order() int {
    return 1 // 顺位1
}
验证启动器

执行main.go debug一下


image.png

通过查看自己打印的日志,看看程序的各个变量都对了没有

目前代码目录
image.png

存在问题:可以看到我们要依赖注入时,须将实力放入appCtx这个map中,用到时再根据key获取并强转类型,key容易写错,强制转换也比较麻烦。
于是,搜索了一些依赖注入的框架,发现go-spring还不错,下面使用该包进行项目的改造。

相关文章

  • 系统编写练习:命令执行系统

    需求: 编辑常用的命令,在某台主机上远程发送命令到其他主机并执行。能够查看操作记录、执行状态。 后续扩展 开发累积...

  • 开发部署

    将可执行程序定义为系统服务1.编写.service文件 2.编写Makefile文件 3.执行make命令安装vi...

  • 2.命令执行漏洞

    定义:攻击者可以随意执行系统命令,命令可以连接执行:&,||,| 命令执行漏洞:直接调用操作系统命令 代码执行漏洞...

  • python----基于TCP+subprocess远程连接

    一 * 远程执行命令的程序 1.subprocess 模块 shell=True 告诉系统把次命令作为系统命令执行...

  • 分别写远程系统命令执行与代码执行漏洞原理

    分别写远程系统命令执行与代码执行漏洞原理 远程命令执行原理:应用系统从设计上需要给用户提供指定的远程命令操作的接口...

  • Python 沙箱逃逸 Payload 收集

    读取文件 在 linecache 中寻找 os 模块执行系统命令 在自模块中寻找 os 模块执行系统命令 读取重要...

  • Python调用外部系统命令

    利用Python调用外部系统命令的方法可以提高编码效率。调用外部系统命令完成后可以通过获取命令执行返回结果码、执行...

  • Java 命令执行

    前言 在学习了反射之后赶紧看Runtime类执行系统命令的一些方式。 执行命令的几种方式 RunTime类执行系统...

  • 命令执行漏洞

    命令执行 利用条件 应用调用执行系统命令的函数 将用户输入作为系统命令的参数拼接到了命令行中 没有对用户输入进行过...

  • 命令执行漏洞

    命令执行 利用条件 应用调用执行系统命令的函数 将用户输入作为系统命令的参数拼接到了命令行中 没有对用户输入进行过...

网友评论

      本文标题:系统编写练习:命令执行系统

      本文链接:https://www.haomeiwen.com/subject/tfnmohtx.html