execute functions in specific order - go

I have the following function
func (c *Connection) ConnectVpn() error {
cmd := exec.Command(c.Command, c.Args...)
var password bytes.Buffer
password.WriteString(os.Getenv("PASSWORD"))
cmd.Stdin = &password
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
err := cmd.Start()
if err != nil {
return err
}
return err
}
This function call the openconnect binary and connects in a private vpn to be able to reach a specific server (it works fine).
The problem is that when I call cmd.Start() it creates a thread and allows me to execute the another function named checkCertificate() but then this function is called before the vpn connects so it fails.
When I try to let the VPN connects and use cmd.RUN() this process do not run on background so the process never finish and it never tells to cmd.Wait() it finished because it shouldn't finish.
days, err := domain.CheckCertificate()
if err != nil {
log.Fatalln(err)
}
I have tried to use channels to try to sync the results between them but when I do this the checkCertificate() function keeps being executed before the VPN executes and I can't reach the server I need.
Any idea in how I could let the ConnectVPN() function be running on foreground and even so send some signal to my other function to say vpn is connected now, please run?
I have tried to send the openconnect to background with cmd.Process.Signal(syscall.SIGTSTP) but when I bring it back it breaks the main function.

I have implemented the function to check if this is connected as was suggested before.
Now it only triggers the other functions if this VPN is connected.
package main
import (
"fmt"
"gitlabCertCheck/request"
"gitlabCertCheck/teams"
"gitlabCertCheck/vpn"
"net/http"
"os"
"strconv"
)
func checker() {
client := http.Client{}
_, err := client.Get(os.Getenv("HTTPS_HOST"))
if err != nil {
checker()
}
return
}
func main() {
args := []string{"--user", os.Getenv("USERNAME"), "--authgroup", "default", "--background",os.Getenv("VPN_SERVER")}
conn := vpn.NewConnection("openconnect", args)
err := conn.ConnectVpn()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
checker()
client := request.Request{}
domain := client.NewRequest(os.Getenv("HOST"), os.Getenv("PORT"))
days, err := domain.CheckCertificate()
if err != nil {
fmt.Println(err)
}
card := teams.Card{}
card.CustomCard.Title = "Certificate Alert"
card.CustomCard.Text = "Certificate will expire in " + strconv.Itoa(days) + " days"
card.NewCard(card.CustomCard)
err = card.SendMessageCard(os.Getenv("TEAMS_WEBHOOK"))
if err != nil {
fmt.Println(err)
}
}
Thank you everyone

Related

Stopwatch go lang

i have a firewall that sends a discord webhook whenever an attack is detected and whenever the attack is no longer detected. I want to add something where it starts a stopwatch whenever it sends the webhook for attack detected. then stops the stopwatch whenever its no longer detected so that it sends how long the attack lasted in seconds with the no longer detected webhook.
This is for when its detected.
fmt.Println("Rlimit Final", rLimit)
cmd := exec.Command("php", "start.php", strconv.Itoa(reqs), strconv.Itoa(rps), strconv.Itoa(requested), strconv.Itoa(passedcaptcha), "ONLINE", "200", "FIREWALL")
cmd.Run()```
/*and this is when it's no longer detected:*/
if rps <= 20 && mitigation != 0 {
cmd := exec.Command("php", "end.php", strconv.Itoa(totalreqs), strconv.Itoa(largerNumber), strconv.Itoa(totalver), strconv.Itoa(passedcaptcha), "ONLINE", "200", "FIREWALL")
cmd.Run()
Could be something like this, as Burak suggested. Note, that it implies that you have only one firewall which can have only one attack, and the webhooks are located on the same instance, so the attackStartTime.tmp file is reachable for attackEnd.
package main
import (
"fmt"
"io/ioutil"
"log"
"time"
)
func main() {
attackStart()
time.Sleep(time.Second*3)
attackEnd()
}
func attackStart() {
//... my cmd PHP code
fileName := "/tmp/attackStartTime.tmp"
timeAttackStarted := []byte(time.Now().Local().Format(time.RFC3339))
if err := ioutil.WriteFile(fileName, timeAttackStarted, 0666); err != nil {
log.Fatal(err)
}
}
func attackEnd() {
//... my cmd PHP code
fileName := "/tmp/attackStartTime.tmp"
filecontent, err := ioutil.ReadFile(fileName)
timeAttackEnded := time.Now().Local()
if err != nil {
log.Fatal(err)
}
timeAttackStarted, err := time.Parse(time.RFC3339, string(filecontent))
if err != nil {
log.Fatal(err)
}
duration := timeAttackEnded.Sub(timeAttackStarted)
fmt.Printf("attack started at %v:\nattack ended: %v\nduration(seconds): %v\n",timeAttackStarted, timeAttackEnded, duration.Seconds())
}

Why does net.Conn.close() seem to be closing at the wrong time?

I'm trying to read and write some commands from a TCP client. I want to close a connection after the last function has been executed but for some reason, it seems like the server disconnects the connection in the middle of the function even when explicitly placed afterward.
package main
import (
"bufio"
"fmt"
"io"
"log"
"net"
"strconv"
"strings"
"time"
)
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
}
go handleConn(conn)
conn.Close()
}
}
func handleConn(someconnection net.Conn) {
func1(someconnection)
func2(someconnection) //connection drops in the middle of executing this part
}
func func2(someconnection net.Conn) {
//send message(a string)
_, err := io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
//await reply
//send another message
_, err = io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
//await reply
//send another message, connection tends to close somewhere here
_, err = io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
//await,send
_, err = io.WriteString(someconnection, do something)
if err != nil {
log.Fatal(err)
}
//await, read and print message
c := bufio.NewReader(someconnection)
buff1 := make([]byte, maxclientmessagelength)
buff1, err = c.ReadBytes(delimiter)
fmt.Printf("\n%s\n", buff1)
_, err = io.WriteString(someconnection, dosomething)
if err != nil {
log.Fatal(err)
}
}
That means the client trying to communicate backward simply isn't able to communicate but the program runs to the end.
Update 1:
Made some progress by placing the deferred close statement to when the connection was first acquired.
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
}
defer conn.Close()
go handleConn(conn)
}}
Now it doesn't necessarily close within the second I hope it to close but at least it now runs all the way through.
Goroutines are asynchronous so after calling handleConn here:
go handleConn(conn)
conn.Close()
the main function continues to execute and closes the connection.
Try just calling the handleConn function regularly (without the go).
The conn.Close needs to de done AFTER handleConn has done its work. You could communicate the back to the main thread using channels but that would be too complex (and also block execution of main thread). This is how it should be done
func main() {
listener, err := net.Listen("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
for {
conn, err := listener.Accept()
if err != nil {
log.Print(err)
}
go handleConn(conn)
// REMOVE BELOW LINE
// conn.Close()
}
}
Add conn.Close inside handleConn
func handleConn(someconnection net.Conn) {
// ADD BELOW LINE
defer someconnection.Close()
func1(someconnection)
func2(someconnection)
}
This makes sure conn.Close is called AFTER func1 and func2 are done executing

How to works with Golang echo framework and Telegram bot?

I want to use "telegram bot" with "echo framework" (When the server started, echo and telegram bot work together). I used the below code, but when I ran that, the telegram bot didn't start.
My main.go:
package main
import (
"database/sql"
"log"
"net/http"
"strings"
tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
"github.com/labstack/echo"
_ "github.com/mattn/go-sqlite3"
)
func main() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
_ = e.Start(":1323")
db, err := sql.Open("sqlite3", "./criticism.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
bot, err := tgbotapi.NewBotAPI("")
if err != nil {
log.Fatal(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := bot.GetUpdatesChan(u)
for update := range updates {
if update.Message == nil {
continue
}
gp_msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Hi")
bot.Send(gp_msg)
}
}
The problem is that when you start the echo server, then the code does not go any further.
In order to use both of them, you need to separate each of them into a different thread and also stop your program to finish and stop everything.
The simplest way is to separate the web server and telegram bot and start them separately:
func StartEcho() {
e := echo.New()
e.GET("/", func(c echo.Context) error {
return c.String(http.StatusOK, "Hello, World!")
})
_ = e.Start(":1323")
}
func StartBot() {
bot, err := tgbotapi.NewBotAPI("")
if err != nil {
log.Fatal(err)
}
bot.Debug = true
log.Printf("Authorized on account %s", bot.Self.UserName)
u := tgbotapi.NewUpdate(0)
u.Timeout = 60
updates, err := bot.GetUpdatesChan(u)
for update := range updates {
if update.Message == nil {
continue
}
gp_msg := tgbotapi.NewMessage(update.Message.Chat.ID, "Hi")
bot.Send(gp_msg)
}
}
And then call them:
func main() {
# Start the echo server in a separate thread
go StartEcho()
db, err := sql.Open("sqlite3", "./criticism.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
# Start the bot in a separate thread
go StartBot()
# To stop the program to finish and close
select{}
# You can also use https://golang.org/pkg/sync/#WaitGroup instead.
}
Or you can just run the last one in the main thread:
func main() {
# Start the echo server in a separate thread
go StartEcho()
db, err := sql.Open("sqlite3", "./criticism.db")
if err != nil {
log.Fatal(err)
}
defer db.Close()
# Start the bot
StartBot()
}
But if the last one stops, so will your entire program and the echo server with it. So you have to recover any panics and don't allow it to stop.

How do I execute a command on a remote machine in a golang CLI?

How do I execute a command on a remote machine in a golang CLI? I need to write a golang CLI that can SSH into a remote machine via a key and execute a shell command. Furthermore, I need to be able to do this one hop away. e.g. SSH into a machine (like a cloud bastion) and then SSH into another, internal, machine and execute a shell command.
I haven't (yet) found any examples for this.
You can run commands on a remote machine over SSH using the "golang.org/x/crypto/ssh" package.
Here is an example function demonstrating simple usage of running a single command on a remote machine and returning the output:
//e.g. output, err := remoteRun("root", "MY_IP", "PRIVATE_KEY", "ls")
func remoteRun(user string, addr string, privateKey string, cmd string) (string, error) {
// privateKey could be read from a file, or retrieved from another storage
// source, such as the Secret Service / GNOME Keyring
key, err := ssh.ParsePrivateKey([]byte(privateKey))
if err != nil {
return "", err
}
// Authentication
config := &ssh.ClientConfig{
User: user,
// https://github.com/golang/go/issues/19767
// as clientConfig is non-permissive by default
// you can set ssh.InsercureIgnoreHostKey to allow any host
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Auth: []ssh.AuthMethod{
ssh.PublicKeys(key),
},
//alternatively, you could use a password
/*
Auth: []ssh.AuthMethod{
ssh.Password("PASSWORD"),
},
*/
}
// Connect
client, err := ssh.Dial("tcp", net.JoinHostPort(addr, "22"), config)
if err != nil {
return "", err
}
// Create a session. It is one session per command.
session, err := client.NewSession()
if err != nil {
return "", err
}
defer session.Close()
var b bytes.Buffer // import "bytes"
session.Stdout = &b // get output
// you can also pass what gets input to the stdin, allowing you to pipe
// content from client to server
// session.Stdin = bytes.NewBufferString("My input")
// Finally, run the command
err = session.Run(cmd)
return b.String(), err
}
Try with os/exec https://golang.org/pkg/os/exec/ to execute a ssh
package main
import (
"bytes"
"log"
"os/exec"
)
func main() {
cmd := exec.Command("ssh", "remote-machine", "bash-command")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
To jump over machines use the ProxyCommand directive in a ssh config file.
Host remote_machine_name
ProxyCommand ssh -q bastion nc remote_machine_ip 22
The other solutions here will work, but I'll throw out another option you could try: simplessh. I think it is easier to use. For this question, I would use option 3 below where you can ssh using your key.
Option 1: SSH to a machine with a password, then run a command
import (
"log"
"github.com/sfreiberg/simplessh"
)
func main() error {
var client *simplessh.Client
var err error
if client, err = simplessh.ConnectWithPassword("hostname_to_ssh_to", "username", "password"); err != nil {
return err
}
defer client.Close()
// Now run the commands on the remote machine:
if _, err := client.Exec("cat /tmp/somefile"); err != nil {
log.Println(err)
}
return nil
}
Option 2: SSH to a machine using a set of possible passwords, then run a command
import (
"log"
"github.com/sfreiberg/simplessh"
)
type access struct {
login string
password string
}
var loginAccess []access
func init() {
// Initialize all password to try
loginAccess = append(loginAccess, access{"root", "rootpassword1"})
loginAccess = append(loginAccess, access{"someuser", "newpassword"})
}
func main() error {
var client *simplessh.Client
var err error
// Try to connect with first password, then tried second else fails gracefully
for _, credentials := range loginAccess {
if client, err = simplessh.ConnectWithPassword("hostname_to_ssh_to", credentials.login, credentials.password); err == nil {
break
}
}
if err != nil {
return err
}
defer client.Close()
// Now run the commands on the remote machine:
if _, err := client.Exec("cat /tmp/somefile"); err != nil {
log.Println(err)
}
return nil
}
Option 3: SSH to a machine using your key
import (
"log"
"github.com/sfreiberg/simplessh"
)
func SshAndRunCommand() error {
var client *simplessh.Client
var err error
// Option A: Using a specific private key path:
//if client, err = simplessh.ConnectWithKeyFile("hostname_to_ssh_to", "username", "/home/user/.ssh/id_rsa"); err != nil {
// Option B: Using your default private key at $HOME/.ssh/id_rsa:
//if client, err = simplessh.ConnectWithKeyFile("hostname_to_ssh_to", "username"); err != nil {
// Option C: Use the current user to ssh and the default private key file:
if client, err = simplessh.ConnectWithKeyFile("hostname_to_ssh_to"); err != nil {
return err
}
defer client.Close()
// Now run the commands on the remote machine:
if _, err := client.Exec("cat /tmp/somefile"); err != nil {
log.Println(err)
}
return nil
}
golang SSH executes shell command with timeout option
import (
"bytes"
"context"
"errors"
"fmt"
"golang.org/x/crypto/ssh"
"time"
)
func SshRemoteRunCommandWithTimeout(sshClient *ssh.Client, command string, timeout time.Duration) (string, error) {
if timeout < 1 {
return "", errors.New("timeout must be valid")
}
session, err := sshClient.NewSession()
if err != nil {
return "", err
}
defer session.Close()
ctx, cancelFunc := context.WithTimeout(context.Background(), timeout)
defer cancelFunc()
resChan := make(chan string, 1)
errChan := make(chan error, 1)
go func() {
// run shell script
if output, err := session.CombinedOutput(command); err != nil {
errChan <- err
} else {
resChan <- string(output)
}
}()
select {
case err := <-errChan:
return "", err
case ms := <-resChan:
return ms, nil
case <-ctx.Done():
return "", ctx.Err()
}
}
Try the package https://github.com/appleboy/easyssh-proxy
package main
import (
"fmt"
"time"
"github.com/appleboy/easyssh-proxy"
)
func main() {
// Create MakeConfig instance with remote username, server address and path to private key.
ssh := &easyssh.MakeConfig{
User: "appleboy",
Server: "example.com",
// Optional key or Password without either we try to contact your agent SOCKET
//Password: "password",
// Paste your source content of private key
// Key: `-----BEGIN RSA PRIVATE KEY-----
// MIIEpAIBAAKCAQEA4e2D/qPN08pzTac+a8ZmlP1ziJOXk45CynMPtva0rtK/RB26
// 7XC9wlRna4b3Ln8ew3q1ZcBjXwD4ppbTlmwAfQIaZTGJUgQbdsO9YA==
// -----END RSA PRIVATE KEY-----
// `,
KeyPath: "/Users/username/.ssh/id_rsa",
Port: "22",
Timeout: 60 * time.Second,
}
// Call Run method with command you want to run on remote server.
stdout, stderr, done, err := ssh.Run("ls -al", 60*time.Second)
// Handle errors
if err != nil {
panic("Can't run remote command: " + err.Error())
} else {
fmt.Println("don is :", done, "stdout is :", stdout, "; stderr is :", stderr)
}
}
See more example.

Paging output from Go

I'm trying print to stdout from golang using $PAGER or manually invoking more or less to allow the user to easily scroll through a lot of options. How can I achieve this?
You can use the os/exec package to start a process that runs less (or whatever is in $PAGER) and then pipe a string to its standard input. The following worked for me:
func main() {
// Could read $PAGER rather than hardcoding the path.
cmd := exec.Command("/usr/bin/less")
// Feed it with the string you want to display.
cmd.Stdin = strings.NewReader("The text you want to show.")
// This is crucial - otherwise it will write to a null device.
cmd.Stdout = os.Stdout
// Fork off a process and wait for it to terminate.
err := cmd.Run()
if err != nil {
log.Fatal(err)
}
}
I assume you already have output being printed from your program to stdout that you want to have captured and sent to your pager, you don't want to
rewrite the I/O to use another input stream like the other responses require.
You can create an os.Pipe which works the same as running a command with "|less" by attaching one side to your pager and the other side to stdout like this:
// Create a pipe for a pager to use
r, w, err := os.Pipe()
if err != nil {
panic("You probably want to fail more gracefully than this")
}
// Capture STDOUT for the Pager. Keep the old
// value so we can restore it later.
stdout := os.Stdout
os.Stdout = w
// Create the pager process to execute and attach
// the appropriate I/O streams.
pager := exec.Command("less")
pager.Stdin = r
pager.Stdout = stdout // the pager uses the original stdout, not the pipe
pager.Stderr = os.Stderr
// Defer a function that closes the pipe and invokes
// the pager, then restores os.Stdout after this function
// returns and we've finished capturing output.
//
// Note that it's very important that the pipe is closed,
// so that EOF is sent to the pager, otherwise weird things
// will happen.
defer func() {
// Close the pipe
w.Close()
// Run the pager
if err := pager.Run(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
// restore stdout
os.Stdout = stdout
}()
Here is a somewhat naive cat example that uses $PAGER when set.
package main
import (
"io"
"log"
"os"
"os/exec"
)
func main() {
var out io.WriteCloser
var cmd *exec.Cmd
if len(os.Args) != 2 {
log.Fatal("Wrong number of args: gcat <file>")
}
fileName := os.Args[1]
file, err := os.Open(fileName)
if err != nil {
log.Fatal("Error opening file: ", err)
}
pager := os.Getenv("PAGER")
if pager != "" {
cmd = exec.Command(pager)
var err error
out, err = cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
if err := cmd.Start(); err != nil {
log.Fatal("Unable to start $PAGER: ", err)
}
} else {
out = os.Stdout
}
_, err = io.Copy(out, file)
if err != nil {
log.Fatal(err)
}
file.Close()
out.Close()
if cmd != nil {
if err := cmd.Wait(); err != nil {
log.Fatal("Error waiting for cmd: ", err)
}
}
}
This version creates an io.Writer called pager for all the output that you want paged (you can assign it to os.Stdout if you like) and correctly closes that and waits for the $PAGER when main() returns.
import (
"fmt"
"io"
"log"
"os"
"os/exec"
)
var pager io.WriteCloser
func main() {
var cmd *exec.Cmd
cmd, pager = runPager()
defer func() {
pager.Close()
cmd.Wait()
}()
fmt.Fprintln(pager, "Hello, 世界")
}
func runPager() (*exec.Cmd, io.WriteCloser) {
pager := os.Getenv("PAGER")
if pager == "" {
pager = "more"
}
cmd := exec.Command(pager)
out, err := cmd.StdinPipe()
if err != nil {
log.Fatal(err)
}
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {
log.Fatal(err)
}
return cmd, out
}

Resources