Go Lang exec/spawn a ssh session - ssh

I'm trying to work out the mechanism to fork/start a ssh terminal session
i.e I want to be logged into remote server (my keys are on server) if I execute this program.
Right now it just executes but nothing happens.
package main
import (
"os/exec"
"os"
)
func main() {
cmd := exec.Command("ssh","root#SERVER-IP")
cmd.Stdout = os.Stdout
//cmd.Stderr = os.Stderr
cmd.Run()
}

cmd.Run waits for the command to complete. Your ssh session should (normally) not exit without user interaction. Therefore your program blocks, since it waits for the ssh process to finish.
You may want to either
also redirect Stdin, so you can interact with the ssh session
execute ssh me#server somecommand. In the last form a specific command gets executed and the output of this command gets redirected.
take a look at the ssh package

I've done library that can cover your requiremenets: https://github.com/shagabutdinov/shell; checkout if it helps or not.
You can use this library to start ssh session and execute the commands:
key, err := ssh.ParsePrivateKey([]byte(YOUR_PRIVATE_KEY))
if(err != nil) {
panic(err)
}
shell, err = shell.NewRemote(shell.RemoteConfig{
Host: "root#example.com:22",
Auth: []ssh.AuthMethod{ssh.PublicKeys(key)},
})
if(err != nil) {
panic(err)
}
shell.Run("cat /etc/hostname", func(_ int, message string) {
log.Println(message) // example.com
})
This is simple wrapper over golang ssh library that helps to execute consequent commands over /bin/sh.

Related

Getting `Request had invalid authentication credentials` when using service account json key file in Go app

I'm developing a Go app on a GCP project and I'm using google cloud logging service. I'm having problems in running the app since it's saying that I have invalid authentication credentials when I'm using a service account json key.
Here's the code snippet having the error:
c, cErr := Load(".env")
if cErr != nil {
log.Fatalf("could not load config: %s", cErr)
return
}
// initializes logger which writes to stdout
ctx := context.Background()
opt := option.WithCredentialsFile(c.GoogleApplicationCredentials);
loggerClient, clientErr := logging.NewClient(ctx, "poc-projects-01", opt)
if clientErr != nil {
log.Fatal(clientErr)
}
And here's the definition of the Load() function:
func Load(file string) (*Config, error) {
viper.SetConfigFile(file)
viper.AddConfigPath(".")
viper.AutomaticEnv()
if err := viper.ReadInConfig(); err != nil {
return nil, err
}
c := &Config{
GoogleApplicationCredentials: viper.GetString("GOOGLE_APPLICATION_CREDENTIALS"),
}
return c, nil
}
I have a .env file with the following content:
GOOGLE_APPLICATION_CREDENTIALS=json/path-to-json.json
I don't know why it's saying token expired even though this is the only service account json key I have on GCP, and on my local machine.
Can you run gcloud auth application-default login and make sure you have that set to the right project.
Check whether GOOGLEAPPLICATIONSCREDENTALS is set with valid JSON key and the environment variable are correctly set, for checking run the below command
echo $GOOGLE_APPLICATION_CREDENTIALS
If the command does not return the correct path to the JSON key, you can set the environment variable with this command:
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/json/key.json
Once you have verified that the JSON key is valid and the environment variable is correctly set, you should be able to run your application.Alternatively, you can try to delete the .env file and then recreate it with the service account json key, which should re-generate the token and make it valid.
Attaching troubleshooting documentation for reference.

Using Deno to interact through SSH hangs on p.output()

I'm looking to create an SSH sub-process and then interact with the server. I'm hung up on a basic step which is to simply wait until the SSH process has connected. I know that this ssh command connects fine because when I run it with inherit instead of piped, the ssh shell shows up as expected.
If I understand correctly, p.output() listens for stdout until it reaches EOF. I'm assuming that when SSH has connected, it streams the stdout, but does not EOF, and so p.output() never gets called.
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const p=Deno.run({
cmd: ["ssh", "root#mywebsite"],
stdout: "piped",
stderr: "piped",
stdin: "piped"
});
const command = (cmd : string) => p.stdin.write(encoder.encode(cmd))
const getOutput = async () => decoder.decode(await p.output())
await p.output() // <----- Hangs here
await command("cd /home/dev/www")
await command("ls -la")
console.log(await getOutput())
await p.status()
console.log("done")
It hangs because .output will resolve once the entire process output has been read, meaning that until ssh command has finished, it will not resolve.
Also have in mind that you need to add \n at the end of each command, otherwise it'll never be triggered.
await command("cd /home\n");
await command("ls -la\n");
// if you don't finish the ssh session, .output will never resolve
await command("exit\n");
// now it will work correctly
console.log(await getOutput());
In any case if you don't want to close the session to read the output of a given command, what you need to do is use p.stdout.readable or p.stdout.read(buf) instead.
for await const(const chunk of p.stdout.readable) {
// parse chunk and do something
}

Gulp task to SSH and then mysqldump

So I've got this scenario where I have separate Web server and MySQL server, and I can only connect to the MySQL server from the web server.
So basically everytime I have to go like:
step 1: 'ssh -i ~/somecert.pem ubuntu#1.2.3.4'
step 2: 'mysqldump -u root -p'password' -h 6.7.8.9 database_name > output.sql'
I'm new to gulp and my aim was to create a task that could automate all this, so running one gulp task would automatically deliver me the SQL file.
This would make the developer life a lot easier since it would just take a command to download the latest db dump.
This is where I got so far (gulpfile.js):
////////////////////////////////////////////////////////////////////
// Run: 'gulp download-db' to get latest SQL dump from production //
// File will be put under the 'dumps' folder //
////////////////////////////////////////////////////////////////////
// Load stuff
'use strict'
var gulp = require('gulp')
var GulpSSH = require('gulp-ssh')
var fs = require('fs');
// Function to get home path
function getUserHome() {
return process.env.HOME || process.env.USERPROFILE;
}
var homepath = getUserHome();
///////////////////////////////////////
// SETTINGS (change if needed) //
///////////////////////////////////////
var config = {
// SSH connection
host: '1.2.3.4',
port: 22,
username: 'ubuntu',
//password: '1337p4ssw0rd', // Uncomment if needed
privateKey: fs.readFileSync( homepath + '/certs/somecert.pem'), // Uncomment if needed
// MySQL connection
db_host: 'localhost',
db_name: 'clients_db',
db_username: 'root',
db_password: 'dbp4ssw0rd',
}
////////////////////////////////////////////////
// Core script, don't need to touch from here //
////////////////////////////////////////////////
// Set up SSH connector
var gulpSSH = new GulpSSH({
ignoreErrors: true,
sshConfig: config
})
// Run the mysqldump
gulp.task('download-db', function(){
return gulpSSH
// runs the mysql dump
.exec(['mysqldump -u '+config.db_username+' -p\''+config.db_password+'\' -h '+config.db_host+' '+config.db_name+''], {filePath: 'dump.sql'})
// pipes output into local folder
.pipe(gulp.dest('dumps'))
})
// Run search/replace "optional"
SSH into the web server runs fine, but I have an issue when trying to get the mysqldump, I'm getting this message:
events.js:85
throw er; // Unhandled 'error' event
^
Error: Warning:
If I try the same mysqldump command manually from the server SSH, I get:
Warning: mysqldump: unknown variable 'loose-local-infile=1'
Followed by the correct mylsql dump info.
So I think this warning message is messing up my script, I would like to ignore warnings in cases like this, but don't know how to do it or if it's possible.
Also I read that using the password directly in the command line is not really good practice.
Ideally, I would like to have all the config vars loaded from another file, but this is my first gulp task and not really familiar with how I would do that.
Can someone with experience in Gulp orient me towards a good way of getting this thing done? Or do you think I shouldn't be using Gulp for this at all?
Thanks!
As I suspected, that warning message was preventing the gulp task from finalizing, I got rid of it by commenting the: loose-local-infile=1 From /etc/mysql/my.cnf

SSH through multiple hosts in go

Is it possible to SSH to another host while in an SSH session in golang? I tried chaining some stuff together like this, but the printout says the remote addr of client 2 is 0.0.0.0 and an error is given when I try to execute anything on an ssh.Session from client2.
host1 := "host1.com"
host2 := "host2.com"
client1, err := ssh.Dial("tcp", host, config)
if err != nil {
panic("Failed to dial: " + err.Error())
}
conn, err := client1.Dial("tcp", host2)
if err != nil {
panic(err)
}
sshConn, newChan, requestChan, err := ssh.NewClientConn(conn, host2, config)
if err != nil {
panic(err)
}
client2 := ssh.NewClient(sshConn, newChan, requestChan)
fmt.Println("Client 2 RemoteAddr():", client2.RemoteAddr())
The problem is that you are running all of this code on the same host. Let's call the host you run your app on host0 for convenience.
You start out making a connection from host0 to host1. That works fine. What you seem to want to do next is make a connection from host1 to host2. To do that you need to run the SSH code on host1. But your app, which is and always has been running on host0, is not in the place to do that.
You will need to put a program on host1 that does the connection to host2 and then send a command down the SSH connection to host1 that will start that program.

SCP Client in Go

I was struggling with and ssh client for golang but was told that the ciphers for the freesshd server was incompatible with the ssh client for Go, so I just installed another one (PowerShell Server) and I can successfully connect to the server.
My problem is not over because I now need to transfer files from local to remote, and this can only be done through scp. I was directed to this scp client for go and have two issues.
I run it and get this:
Where or how can I access the contents of id_rsa needed for privateKey? I just went in my .ssh folder and saw a github_rsa and used that private key, I'm sure that this is not the correct one to use but wouldn't I see some kind of error or invalid private key and not the result above?
The code you were directed to is broken. The example also uses the public key authentication, which is not necessarily your only option. If you can allow password authentication instead, you can make it a bit easier for yourself.
I just made an upload myself by modifying the example you used:
package main
import (
"code.google.com/p/go.crypto/ssh"
"fmt"
)
type password string
func (p password) Password(_ string) (string, error) {
return string(p), nil
}
func main() {
// Dial code is taken from the ssh package example
config := &ssh.ClientConfig{
User: "username",
Auth: []ssh.ClientAuth{
ssh.ClientAuthPassword(password("password")),
},
}
client, err := ssh.Dial("tcp", "127.0.0.1:22", config)
if err != nil {
panic("Failed to dial: " + err.Error())
}
session, err := client.NewSession()
if err != nil {
panic("Failed to create session: " + err.Error())
}
defer session.Close()
go func() {
w, _ := session.StdinPipe()
defer w.Close()
content := "123456789\n"
fmt.Fprintln(w, "C0644", len(content), "testfile")
fmt.Fprint(w, content)
fmt.Fprint(w, "\x00")
}()
if err := session.Run("/usr/bin/scp -qrt ./"); err != nil {
panic("Failed to run: " + err.Error())
}
}