Go语言ssh群发linux命令

fyxichen · · 5495 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

闲着没事搞了小程序,在Window上面往linux机器上群发命令,

写的不好欢迎大家指正,还有就是用此工具发送恶意命令的人请离开,

编译了一份64_Window和linux的程序,供大家参考使用,Rm_tmp_cmd.exe 在win下面提供使用!

我的目录结构:

flagparse

      --------flag.go

Getconf

      --------getconfig.go

Maste

      --------scp.go

      --------ssh_client.go

Zip_File

      --------zip.go

maste.go

ssh.conf

命令文件:ssh.conf

第一行是需要群发的命令,用英文','隔开
然后下面是主机IP:PORT user password command ---->这个command的是只发给这个主机的命令,不会发到其他的主机上,可以理解为私有的命令
0`ls -a,df -h
1`127.0.0.1:5000`root`123456`ls -l,free -m
2`127.0.0.1:5000`root`123456`df -h,pwd
3`127.0.0.1:5506`root`123456`cd /mnt/ && python ping.py > 2015.txt &

flag.go
命令行解析参数文件:

package flagparse

import (
	"flag"
)

func Flag() (*string, *bool, *string, *bool) {

	var (
		sendfile       = flag.String("F", "", "此参数表示向所有标签主机发送文件,参数代表文件路径,例如:-F=/mnt/2015.go")
		Global_command = flag.Bool("G", false, "此参数代表是否执行全局命令,默认是不启用,如果启用使用:-G=true或-G")
		SendAll        = flag.String("S", "", "此参数代表向这些标签的主机发送命令:-S=1,2执行1,2标签的命令")
		PrintLog       = flag.Bool("P", false, "此参数默认表示是否印输出执行结果,如果打印日志使用:-P=true或-P")
	)
	flag.Parse()
	return sendfile, Global_command, SendAll, PrintLog
}

getconfig.go
解析配置文件(ssh.conf)

package Getconf

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strings"
)

//获取ssh.conf的配置文件,以map的形式把每行的数据返回
func GetConfig() map[string][]string {
	ProPath := os.Args[0]
	os.Chdir(filepath.Dir(ProPath))
	Config := make(map[string][]string)
	File, err := os.Open("ssh.conf")
	if err != nil {
		fmt.Println("From GetConfig :", err)
	}
	defer File.Close()
	Buf := bufio.NewReader(File)
	for {
		line, _, err := Buf.ReadLine()
		if err != nil {
			if err == io.EOF {
				break
			}
			fmt.Println("From GetConfig :", err)
			break
		}
		x := strings.Split(string(line), "`")
		Config[x[0]] = x[1:]
	}
	return Config
}

scp.go
用来发送文件:

package Maste

import (
	"Zip_File"
	"fmt"
	"go-ssh/ssh"
	"io"
	"os"
	"path/filepath"
	"strings"
)

func Use_Scp_Send(SendAll, Path *string, config map[string][]string, Ok, Err, Sta chan string) {
	FlagList := strings.Split(*SendAll, ",")
	Name := filepath.Base(*Path)
	SendFilePath := *Path
	OldPath, _ := os.Getwd()
	if File, e := os.Stat(SendFilePath); e == nil {
		if File.IsDir() {
			Zip_File.AddFilesToZip(SendFilePath, Name)
			SendFilePath = fmt.Sprintf("%s\\%s.zip", OldPath, Name)
			Name = fmt.Sprintf("%s.zip", Name)
		}
	} else {
		fmt.Println(SendFilePath, " :不存在!")
		os.Exit(2)
	}
	fmt.Println("发送的文件名:", Name)
	if *SendAll == "" {
		for i, _ := range config {
			if i != "0" {
				go connect(config[i][0], config[i][1], config[i][2], SendFilePath, Name, Ok, Err, Sta)
			}
		}
	} else {
		for _, i := range FlagList {
			go connect(config[i][0], config[i][1], config[i][2], SendFilePath, Name, Ok, Err, Sta)
		}
	}
}

func connect(ip, user, password, FilePath, Name string, Ok, Err, Sta chan string) {

	Auth := []ssh.AuthMethod{ssh.Password(password)}
	conf := ssh.ClientConfig{Auth: Auth, User: user}
	Client, err0 := ssh.Dial("tcp", ip, &conf)
	if err0 == nil {
		Ok <- fmt.Sprint(ip, "  连接状态:Ok")
	} else {
		Err <- fmt.Sprint(ip, "  连接错误:", err0)
	}
	defer Client.Close()

	conn, err1 := Client.NewSession()
	if err1 == nil {
		Ok <- fmt.Sprint(ip, "  会话状态:Ok")
	} else {
		Err <- fmt.Sprint(ip, "  会话错误:", err1)
	}
	defer conn.Close()
	go func() {
		Buf := make([]byte, 1024)
		w, _ := conn.StdinPipe()
		defer w.Close()
		File, _ := os.Open(FilePath)
		defer File.Close()
		info, _ := File.Stat()
		fmt.Fprintln(w, "C0644", info.Size(), Name)
		for {
			n, err := File.Read(Buf)
			fmt.Fprint(w, string(Buf[:n]))
			if err != nil {
				if err == io.EOF {
					return
				} else {
					panic(err)
				}
			}
		}
	}()
	err3 := conn.Run("scp -qrt /mnt/")  //会保存在mnt下面
	if err3.Error() == "Process exited with: 1. Reason was:  ()" {
		Ok <- fmt.Sprint(ip, "  执行状态:Ok")
		Sta <- "Ok"
	} else {
		Err <- fmt.Sprint(ip, "  执行错误:", err3)
	}
}

ssh_client.go
连接主机主程序

package Maste

import (
    "bytes"
    "fmt"
    "go-ssh/ssh"
)

func SSH_Client(ip_port, user, password string, command []string, P bool, Ok, Err, Res chan string) {
    PassWd := []ssh.AuthMethod{ssh.Password(password)}
    Conf := ssh.ClientConfig{User: user, Auth: PassWd}
    Client, err := ssh.Dial("tcp", ip_port, &Conf)
    if err == nil {
        Ok <- fmt.Sprint(ip_port, "  连接状态:Ok")
    } else {
        Err <- fmt.Sprint(ip_port, "  ErrorInfo:", err)
        return
    }
    defer Client.Close()
    for _, Command := range command {
        if session, err := Client.NewSession(); err == nil {
            defer session.Close()
            var Result bytes.Buffer
            session.Stderr = &Result
            session.Stdout = &Result
            err = session.Run(Command)
            if err == nil {
                Ok <- fmt.Sprint(ip_port, "  命令:", Command, "  执行状态:Ok")
            } else {
                Err <- fmt.Sprint(ip_port, "  ErrorInfo:", err)
            }
            if P {
                Res <- fmt.Sprint(ip_port, "  执行结果\n", Result.String())
            }
        }
    }
}

zip.go
使用发送文件之前会调用此函数来进行压缩

package Zip_File

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"path/filepath"
	//"strings"
)

func AddFilesToZip(Path, Name string) {
	//var PathName string
	File, _ := os.Create(fmt.Sprintf("%s.zip", Name))
	/*
				PS := strings.Split(Path, "/")
				if len(PS) == 2 {
					PathName = fmt.Sprintf("%s/", PS[0])
				} else {
					PathName = strings.Join(PS[:len(PS)-1], "/")
				}
			os.Chdir(PathName)
		Path = PS[len(PS)-1]
	*/
	os.Chdir(filepath.Dir(Path))
	Path = filepath.Base(Path)
	defer File.Close()
	Zip := zip.NewWriter(File)
	defer Zip.Close()
	walk := func(Path string, info os.FileInfo, err error) error {
		if err != nil {
			fmt.Println(err)
			return err
		}
		if info.IsDir() {
			return nil
		}
		Src, _ := os.Open(Path)
		defer Src.Close()
		h := &zip.FileHeader{Name: Path, Method: zip.Deflate, Flags: 0x800}
		FileName, _ := Zip.CreateHeader(h)
		io.Copy(FileName, Src)
		Zip.Flush()
		return nil
	}
	filepath.Walk(Path, walk)
}

maste.go
入口程序

package main

import (
	"Getconf"
	"Maste"
	"flagparse"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

var Rm_cmd_path, _ = os.Getwd()
var (
	timeout int         = 0
	TimeOut int         = 10
	Ok      chan string = make(chan string)
	Err     chan string = make(chan string)
	Res     chan string = make(chan string)
	Sta     chan string = make(chan string)
)

func main() {
	var Num int
	Sendfile, Global_command, SendAll, PrintLog := flagparse.Flag()
	config := Getconf.GetConfig()
	if *Sendfile != "" {
		TimeOut = 3600
		Maste.Use_Scp_Send(SendAll, Sendfile, config, Ok, Err, Sta)
	} else {
		SendCommand(SendAll, Global_command, PrintLog, config)
	}
	if *SendAll == "" {
		Num = len(config) - 1
	} else {
		Num = len(strings.Split(*SendAll, ","))
	}
	defer RmFile(Sendfile)
	for {
		Check_status(*PrintLog, Num, Sendfile)
	}
}

//根据参数发送命令
func SendCommand(SendAll *string, Global_command, PrintLog *bool, config map[string][]string) {
	FlagList := strings.Split(*SendAll, ",")
	if *SendAll == "" {
		for _, i := range FlagList {
			Com := Global_cmd(config["0"])
			go Maste.SSH_Client(config[i][0], config[i][1], config[i][2], Com, *PrintLog, Ok, Err, Res)
		}
	} else {
		for _, i := range FlagList {
			Com := Private_cmd(config["0"], config[i][3], *Global_command)
			go Maste.SSH_Client(config[i][0], config[i][1], config[i][2], Com, *PrintLog, Ok, Err, Res)
		}
	}
}

//解析ssh.conf中的command字段:
func Global_cmd(Global []string) []string {
	return strings.Split(Global[0], ",")
}

func Private_cmd(Global []string, Private string, Globlcomand bool) []string {
	if Globlcomand {
		Private_cmd := strings.Split(Private, ",")
		var cmd []string = Global_cmd(Global)
		for _, i := range Private_cmd {
			cmd = append(cmd, i)
		}
		return cmd
	} else {
		return strings.Split(Private, ",")
	}
}

//获取chan管道的状态.
func Check_status(P bool, Num int, Sendfile *string) {
	var Break int = 0
	if P {
		for {
			select {
			case x := <-Err:
				fmt.Println(x)
				timeout = TimeOut - 2
			case y := <-Ok:
				fmt.Println(y)
				timeout = 0
			case z := <-Res:
				fmt.Println(z)
				timeout = 0
			case <-Sta:
				Break = Break + 1
				if Break >= Num {
					RmFile(Sendfile)
					os.Exit(1)
				}
			default:
				if timeout > TimeOut {
					os.Exit(2)
				}
				timeout = timeout + 1
				time.Sleep(1e9)
			}
		}
	} else {
		for {
			select {
			case x := <-Err:
				fmt.Println(x)
				timeout = TimeOut - 2
			case y := <-Ok:
				fmt.Println(y)
				timeout = 0
			case <-Sta:
				Break = Break + 1
				if Break >= Num {
					RmFile(Sendfile)
					os.Exit(3)
				}
			default:
				if timeout > TimeOut {
					os.Exit(4)
				}
				timeout = timeout + 1
				time.Sleep(1e9)
			}
		}
	}
}
func RmFile(Sendfile *string) {
	os.Chdir(Rm_cmd_path)
	if info, err := os.Stat(*Sendfile); err == nil && info.IsDir() {
		Rm_Path := fmt.Sprintf("%s.zip", filepath.Base(*Sendfile))
		cmd := exec.Command("Rm_tmp_cmd.exe", Rm_Path)
		cmd.Run()
	}
}


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:fyxichen

查看原文:Go语言ssh群发linux命令

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

5495 次点击  
加入收藏 微博
1 回复  |  直到 2000-01-01 00:00:00
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传