├── README.md ├── bot.go └── config.json /README.md: -------------------------------------------------------------------------------- 1 | ## Telegram shell Bot 2 | With this service, you can send ssh commands via bot telegrams 3 | 4 | -------------------- 5 | ## Start 6 | 7 | 1. Clone git repository and install dependence 8 | 9 | 2. Write settings to file config.json 10 | 11 | 3. Run 12 | 13 | ## Settings 14 | 15 | User - for ssh user connection 16 | 17 | Host - for ssh host connection 18 | 19 | Port - for ssh port connection 20 | 21 | Cert - key or password from authorization, depends on the mode 22 | 23 | Token - this token to access the [HTTP API telegram](https://telegram.me/botfather) 24 | 25 | Proxy - address of proxy server 26 | 27 | Mode - authMethod 28 | 29 | - 1 - Password mode 30 | - 2 - Key mode 31 | -------------------------------------------------------------------------------- /bot.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "golang.org/x/crypto/ssh" 7 | "io/ioutil" 8 | "log" 9 | "net" 10 | "os" 11 | "path/filepath" 12 | "strings" 13 | "time" 14 | 15 | "github.com/idcooldi/telegram-bot-api" 16 | ) 17 | 18 | const ( 19 | //Password type mode autorization on enter password 20 | password = 1 21 | //Key type mode autorization on public_key 22 | key = 2 23 | //DefTimeout timeout by default 24 | defTimeout = 3 // second 25 | ) 26 | 27 | //Config структура для Json "Config" 28 | type config struct { 29 | SSH struct { 30 | User string `json:"user"` 31 | Host string `json:"host"` 32 | Port int `json:"port"` 33 | Cert string `json:"cert"` 34 | Token string `json:"token"` 35 | Proxy string `json:"proxy"` 36 | Mode int `json:"mode"` 37 | } `json:"ssh"` 38 | } 39 | 40 | //SSH struct for config 41 | type shell struct { 42 | IP string 43 | User string 44 | Cert string //password or key file path 45 | Port int 46 | session *ssh.Session 47 | client *ssh.Client 48 | } 49 | 50 | var conf config 51 | 52 | //Conf func for download file wich config 53 | func readConfing() { 54 | 55 | dir, err := filepath.Abs(filepath.Dir(os.Args[0])) 56 | if err != nil { 57 | log.Printf("Error pwd:%v\n", err) 58 | } 59 | configFile, err := ioutil.ReadFile(dir + "/config.json") 60 | if err != nil { 61 | panic(err) 62 | } 63 | err = json.Unmarshal(configFile, &conf) 64 | if err != nil { 65 | log.Printf("Error Unmarshal%v\n", err) 66 | } 67 | } 68 | func (sshClient *shell) readPublicKeyFile(file string) ssh.AuthMethod { 69 | buffer, err := ioutil.ReadFile(file) 70 | if err != nil { 71 | return nil 72 | } 73 | key, err := ssh.ParsePrivateKey(buffer) 74 | if err != nil { 75 | return nil 76 | } 77 | return ssh.PublicKeys(key) 78 | } 79 | 80 | //Connect - create connecting session of ssh 81 | func (sshClient *shell) connect(mode int) { 82 | 83 | var sshConfig *ssh.ClientConfig 84 | var auth []ssh.AuthMethod 85 | if mode == password { 86 | auth = []ssh.AuthMethod{ssh.Password(sshClient.Cert)} 87 | } else if mode == key { 88 | auth = []ssh.AuthMethod{sshClient.readPublicKeyFile(sshClient.Cert)} 89 | } else { 90 | log.Println("does not support mode: ", mode) 91 | 92 | return 93 | } 94 | 95 | sshConfig = &ssh.ClientConfig{ 96 | User: sshClient.User, 97 | Auth: auth, 98 | HostKeyCallback: func(hostname string, remote net.Addr, key ssh.PublicKey) error { 99 | return nil 100 | }, 101 | Timeout: time.Second * defTimeout, 102 | } 103 | 104 | client, err := ssh.Dial("tcp", fmt.Sprintf("%s:%d", sshClient.IP, sshClient.Port), sshConfig) 105 | if err != nil { 106 | fmt.Println(err) 107 | 108 | return 109 | } 110 | session, err := client.NewSession() 111 | if err != nil { 112 | log.Printf("Error of NewSession %v\n", err) 113 | err := client.Close() 114 | if err != nil { 115 | log.Printf("Error of close client NewSession%v\n", err) 116 | } 117 | 118 | return 119 | } 120 | 121 | sshClient.session = session 122 | sshClient.client = client 123 | } 124 | 125 | //RunCmd do command line 126 | func (sshClient *shell) RunCmd(cmd string) string { 127 | sshClient.connect(conf.SSH.Mode) 128 | out, err := sshClient.session.CombinedOutput(cmd) 129 | if err != nil { 130 | log.Printf("Error of RunCmd %v\n", err) 131 | } 132 | sshClient.Close() 133 | 134 | return string(out) 135 | } 136 | 137 | //Close close session of ssh 138 | func (sshClient *shell) Close() { 139 | err := sshClient.session.Close() 140 | if err != nil { 141 | log.Printf("Error of close session %v\n", err) 142 | } 143 | err = sshClient.client.Close() 144 | if err != nil { 145 | log.Printf("Error of close client %v\n", err) 146 | } 147 | } 148 | 149 | //demo 150 | func main() { 151 | readConfing() 152 | client := &shell{ 153 | IP: conf.SSH.Host, 154 | User: conf.SSH.User, 155 | Port: conf.SSH.Port, 156 | Cert: conf.SSH.Cert, 157 | } 158 | bot, err := tgbotapi.NewBotAPI(conf.SSH.Token, "socks5", conf.SSH.Proxy, nil) 159 | if err != nil { 160 | log.Panic(err) 161 | } 162 | bot.Debug = true 163 | log.Printf("Authorized on account %s", bot.Self.UserName) 164 | u := tgbotapi.NewUpdate(0) 165 | u.Timeout = 60 166 | updates, err := bot.GetUpdatesChan(u) 167 | if err != nil { 168 | log.Printf("Error of GetUpdatesChan %v\n", err) 169 | } 170 | //client.Connect(Key) 171 | for update := range updates { 172 | if update.Message == nil { 173 | continue 174 | } 175 | s := strings.TrimPrefix(update.Message.Text, "/") 176 | if update.Message.IsCommand() { 177 | msg := tgbotapi.NewMessage(update.Message.Chat.ID, "") 178 | ms := client.RunCmd(s) 179 | msg.Text = ms 180 | _, err := bot.Send(msg) 181 | if err != nil { 182 | log.Printf("Error of send Bot %v\n", err) 183 | } 184 | } 185 | } 186 | } 187 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "ssh":{ 3 | "user": "root", 4 | "host": "127.0.0.1", 5 | "port": 22, 6 | "cert": "/home/user/.ssh/id_rsa", 7 | "token": "457874546:AAE8MmgL-_Fuk7WT_u8v8TG55xiwFY8Ds", 8 | "proxy": "127.0.0.1:9050", 9 | "mode": 2 10 | } 11 | } --------------------------------------------------------------------------------