golang实现远程控制主机

2023-09-18 23:03:50

ssh原理

说到ssh原理个人觉得解释最全的一张图是这张华为画的
在这里插入图片描述
Connection establishment
这一步就是建立tcp连接
version negotiation
这一步是ssh客户端(连接者)和被ssh服务端(连接者)进行协议的交换,比如ssh客户端支持那些加密协议,协商出最后使用的协议,还有就是协商ssh版本

Key Exchange
在说Key Exchange之前,我们要知道主机有2类的ssh-key分别是hostkey和user ssh key,每类key都分公钥和私钥,公钥可以加密,私钥可以解密

  • hostkey:

  • user ssh key:

首先hostkey,当ssh server安装的时候这个hostkey就会默认生成,在第一次连接对端的时候会显示的提醒我们叫我们确认时候继续连接如下

The authenticity of host '192.168.152.132 (192.168.152.132)' can't be established.
ECDSA key fingerprint is SHA256:MzAmI+qRcIEb0AS+6XMcAH5gtxnB779KpHRa1vOvAMs.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes	 

这是啥意思呢我们第一次连接ssh服务端,服务段会发送自己的hostkey的hash后的值给我们,我们如果选择yes,这段hash后的ssh服务端的hostkey被存入本地的.ssh/know_hosts中这是为了防止中间人攻击,有了这个hostkey之后,会使用diffie-hellman,这个算法用于为2端同时生成session key,因为diffie-hellman算法的特新session key必定相等,后续session建立后都会用这个session key进行加密传输

Key Authentication
随后是user ssh key,这个东西是用户用ssh-keygen生成的(也是ssh -i指定的identity_file]),当用户使用key认证而非密码认证的时候,这个就非常重要,ssh客户端将自己的public user ssh key发送给服务端(因为是session,用session可以进行加密),然后每当ssh客户端登录到服务端都会自己生成一个随机数用user 自己的userprivate key进行加密通过session传递给客户端,服务端再用接收客户端的公钥进行解密再发送回客户端,客户端进行check,是自己刚刚生成的key就发送验证成功的消息给服务端,最后验证通过,每当客户端向服务端进行数据传输都会使用之前的session key进行加密,服务段接收后用相同的session key进行解密

使用golang远程下发命令

package main

import (
        "fmt"
        "log"
        "os"

        "golang.org/x/crypto/ssh"
        "golang.org/x/crypto/ssh/knownhosts"
        //"golang.org/x/cryto/ssh"
)

func main() {
        //用于对方返回的key
        hostkey, err := knownhosts.New("/root/.ssh/known_hosts")
        if err != nil {
                log.Fatal("get knowhosts file error: %s", err.Error())
        }

        identify_file := "/root/.ssh/ansible"
        addr := "192.168.152.132:22"

        key, err := os.ReadFile(identify_file)
        if err != nil {
                log.Fatal("error,Can not Read file " + identify_file + " : " + err.Error())
        }

        //将私钥用PEM方式加密返回成签名
        signer, err := ssh.ParsePrivateKey(key)
        if err != nil {
                log.Fatal("unbale to parseprivatekey: ", err)
        }

        //设置连接peer时候的config
        config := &ssh.ClientConfig{
                User: "root",
                Auth: []ssh.AuthMethod{
                        ssh.PublicKeys(signer),
                },
                HostKeyCallback: hostkey,
        }

        //连接(key exchange,这里exhcange的是host key,用于认证session,而user key也就是自己手动生成的key用于验证用户)
        client, err := ssh.Dial("tcp", addr, config)
        if err != nil {
                log.Fatal("unable to connect: " + err.Error())
        }

        defer client.Close()

        session, err := client.NewSession()
        if err != nil {
                log.Fatal("new session error: %s", err.Error())
        }

        result, _ := session.Output("ip a")
        if err != nil {
                fmt.Fprintf(os.Stderr, "faile to run command, err:%s", err.Error())
        }

        fmt.Println(string(result))

}

代码非常简单,注意我们如果是第一次连接那么上述代码会执行失败,因为第一次连接,ssh server要返回自己的hostkey给客户端(我们执行代码的机器),客户端要在自己的know_host里面check,但是第一次连接know_host没有数据就会失败,所以可以将代码改成不安全的模式,也就是不check know_host,方法也很简单,就是将连接时候的config的HostKeyCallback对应的值改为ssh.InsecureIgnoreHostKey()如下

package main

import (
        "fmt"
        "log"
        "os"

        "golang.org/x/crypto/ssh"
        //"golang.org/x/crypto/ssh/knownhosts"
        //"golang.org/x/cryto/ssh"
)

func main() {
        //用于对方返回的key
        //hostkey, err := knownhosts.New("/root/.ssh/known_hosts")
        //if err != nil {
        //      log.Fatal("get knowhosts file error: %s", err.Error())
        //}

        identify_file := "/root/.ssh/ansible"
        addr := "192.168.152.132:22"

        key, err := os.ReadFile(identify_file)
        if err != nil {
                log.Fatal("error,Can not Read file " + identify_file + " : " + err.Error())
        }

        //将私钥用PEM方式加密返回成签名
        signer, err := ssh.ParsePrivateKey(key)
        if err != nil {
                log.Fatal("unbale to parseprivatekey: ", err)
        }

        //设置连接peer时候的config
        config := &ssh.ClientConfig{
                User: "root",
                Auth: []ssh.AuthMethod{
                        ssh.PublicKeys(signer),
                },
                HostKeyCallback: ssh.InsecureIgnoreHostKey(),
        }

        //连接(key exchange,这里exhcange的是host key,用于认证session,而user key也就是自己手动生成的key用于验证用户)
        client, err := ssh.Dial("tcp", addr, config)
        if err != nil {
                log.Fatal("unable to connect: " + err.Error())
        }

        defer client.Close()

        session, err := client.NewSession()
        if err != nil {
                log.Fatal("new session error: %s", err.Error())
        }

        result, _ := session.Output("ip a")
        if err != nil {
                fmt.Fprintf(os.Stderr, "faile to run command, err:%s", err.Error())
        }

        fmt.Println(string(result))

}

最后打印出结果

root@master:~/demo/ssh# ./ssh
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: ens32: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
    link/ether 00:0c:29:7e:76:1b brd ff:ff:ff:ff:ff:ff
    altname enp2s0
    inet 192.168.152.132/24 brd 192.168.152.255 scope global noprefixroute ens32
       valid_lft forever preferred_lft forever
    inet6 fe80::20c:29ff:fe7e:761b/64 scope link
       valid_lft forever preferred_lft forever

使用golang远程传输文件

在代码之前,我们要了解2个小的知识点

  • scp -t的用途
  • 文件形式

首先scp -t这个选项不管是man还是其他的公开官方资料中都很难找到其身影,我们先运行看是在干嘛

root@slaver1:/test# scp -t .


输入后就卡着不动,因为这个时候scp的程序在等待远端的scp程序通过tcp的22号端口向他发送文件…
所以scp -t是通过端口接收文件用的

在说一个文件的格式,一个文件开头第一行标记了文件的信息,比如权限,比如大小,比如文件名,后面的才是内容,最后以\x000结尾,所以我们传递的时候最好使用管道,先传递文件原信息,再传递文件的内容,最后传递\x000表示结尾,直接看代码

package main
import (
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"sync"

	"golang.org/x/crypto/ssh"
)

func main(){
	identify_file := "/root/.ssh/ansible"
	addr := "192.168.152.132:22"
	key, _ := os.ReadFile(identify_file)
	signer, _ := ssh.ParsePrivateKey(key) //用协商出来的动态session key进行加密
	config := &ssh.ClientConfig{
		User: "root",
		Auth: []ssh.AuthMethod{
			ssh.PublicKeys(signer),
		},
		HostKeyCallback: ssh.InsecureIgnoreHostKey(),
	}

	client, _ := ssh.Dial("tcp", addr, config)
	defer client.Close()

	session, _ := client.NewSession()
	defer session.Close()

	file, _ := os.Open("apply.sh")
	defer file.Close()
	stat, _ := file.Stat()

	wg := sync.WaitGroup{}
	wg.Add(1)

	go func() {
		hostIn, _ := session.StdinPipe()
		defer hostIn.Close()
		fmt.Fprintf(hostIn, "C0655 %d %s\n", stat.Size(), "apply.sh")//第一行输入文件元数据
		io.Copy(hostIn, file)	//copy 文件内容
		fmt.Fprint(hostIn, "\x000") //表示结束
		wg.Done()
	}()

	session.Run("/usr/bin/scp -t /test")
	wg.Wait()

}
更多推荐

C++之浅拷贝、深拷贝、拷贝构造函数、拷贝赋值运算符、自定义的深拷贝函数应用总结(二百二十九)

简介:CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀人生格言:人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.更多原创,欢迎关注:Android系统攻城狮1.前言本篇目的:理解C+

数据库锁及批量更新死锁处理

数据库锁锁间隙锁锁定的是一个间隙范围,而不会锁住某条记录。共享锁就是读锁,独占锁就是写锁,可以理解为读写锁,读读不互斥,读写互斥,写写互斥,共享锁(S锁)、独占锁(X锁)指的就是InnoDB上的行锁(记录锁)。意向锁是InnoDB引擎的一种特殊的表锁,在获取共享锁和独占锁之前必须拿到对应类型的意向锁。乐观锁和悲观锁更多

如何处理ChatGPT在文本生成中的语法错误和不合理性?

ChatGPT是一种强大的自然语言处理模型,但它并不是完美的,有时会产生语法错误或不合理的文本。这些问题可能会影响模型生成的内容的质量和可信度。在处理ChatGPT中的语法错误和不合理性时,有许多方法和策略可以采用,以下是一些详细的讨论:**1.数据清洗和预处理:**首先,可以通过对输入数据进行清洗和预处理来减少语法错

Ninja: Towards Transparent Tracing and Debugging on ARM【TEE的应用】

目录摘要引言贡献背景TrustZone和受信任的固件PMU和ETM相关工作x86上的透明恶意软件分析ARM上的动态分析工具基于仿真的系统硬件虚拟化裸机系统Trustzone相关的系统系统架构具体实现和评估可以看论文,这里不赘述了讨论总结作者:ZhenyuNingandFengweiZhang发布:USENIX时间:20

Hadoop NameNode执行命令工作流程

HadoopNameNode执行命令工作流程客户端API或者CLI与NameNode的交互命令数据的格式(1)预处理流程(2)创建NameNode与NameNodePrcServer流程(3)HDFSAPI以及CLI的命令到NameNode的工作执行流程(4)执行命令的参数流动客户端API或者CLI与NameNode的

使用LDA(线性判别公式)进行iris鸢尾花的分类

线性判别分析((LinearDiscriminantAnalysis,简称LDA)是一种经典的线性学习方法,在二分类问题上因为最早由[Fisher,1936]提出,亦称”Fisher判别分析“。并且LDA也是一种监督学习的降维技术,也就是说它的数据集的每个样本都有类别输出。这点与主成分和因子分析不同,因为它们是不考虑样

9、DVWA——XSS(Stored)

文章目录一、存储型XSS概述二、low2.1源码分析2.2通关分析三、medium3.1源码分析3.2通关思路四、high4.1源码分析4.2通关思路一、存储型XSS概述XSS,全称CrossSiteScripting,即跨站脚本攻击,某种意义上也是一种注入攻击,是指攻击者在页面中注入恶意的脚本代码,当受害者访问该页面

分布式系统中的选举,日志副本,安全等设计思想

链接:https://pan.baidu.com/s/1G9295khav7_k3dD9G0f_Kw?pwd=q216提取码:q216领导选举领导选举(Leaderelection)是在分布式系统中选择一个节点作为领导者或协调者的过程。分布式系统通常由多个节点组成,每个节点都可以执行特定的任务。然而,为了使系统有序运行

HCIE-容器docker

1、安装配置操作系统,使用CentOSstream8镜像之前:RHEL8.4发布了,CentOS紧随其后,发布CentOS8.4之后:CentOS走在前面,成为RHEL上游,再去发布RHEL制作模板,模板配置要求,cpu至少2个,内存建议4G,硬盘100G,网卡使用NAT模式。1.编辑网卡[root@tempnetwo

Java|List.subList 踩坑小记

很久以前在使用Java的List.subList方法时踩过一个坑,当时记了一条待办,要写一写这事,今天完成它。我们先来看一段代码://初始化list为{1,2,3,4,5}List<Integer>list=newArrayList<>();for(inti=1;i<=5;i++){list.add(i);}//取前3

传感器浮点数数据在串口通信中封包技术解析

一、项目实现要求根据项目要求,我们需要在多台机器人间进行数据通信,系统搭建如下:在机器人A上搭载大气压模块传感器和zigbee通信模块,在机器人B上搭载手势传感器和zigbee通信模块,在机器人C上搭载zigbee通信模块,要求A和B的传感器数据能够通过zigbee模块传输到机器人C并用以做进一步的控制处理。二、由于分

热文推荐