From 5786966f1d13e9a7a368f9a01ddb71ca8211788c Mon Sep 17 00:00:00 2001 From: Ikramullah Latif Date: Thu, 25 Jul 2024 10:54:38 +0700 Subject: [PATCH] chore!(proxy): add proxy support for broker (#4) * feat(dev/cli): initialize cli * refactor(dev/cli): refactoring flag system * refactor(dev/cli): simplify print usage * feat(dev/cli): add custom http handler for verifying * feat(dev/server): add isPublic property on variable * feat(dev/cli): add status code validation for response * feat(dev/cli): add status code validation for response * feat(dev/cli): finalize auth command * feat(dev/cli): create base command for get * refactor(dev/cli): formatting auth and add remove default value * feat(dev/cli): create http request for get token before spawning echo instance * feat(dev/cli): implementing webserver on get command * refactor(dev/cli): pruning one file * feat(dev/image): add docker compose * fix(dev/server): modify dockerfile * fix(dev/server): pruning dockerfile and modify csproj * feat(dev/cli): add message on command before sending file * feat(dev/cli): add feature to upload * feat(dev/cli): add progress while uploading * feat(dev/cli): add slider for progressbar * feat(dev/cli): add validator for duplicate file * feat(dev/cli): set response header for keep-alive * refactor(dev/cli): remove unnecessary file * feat(dev/cli): create new base command for send * refactor(dev/server): add tunnel metadata on verify * feat(dev/server): improving entrypoint for creating base random passowrd for salting certificate (soon) * refactor(dev/server): add environment on docker compose * fix(dev/cli): change parsing and add missing parameter * fix(dev/cli): fixing wrong pinning json * feat(dev/cli): add ssh tunnel generator args and implement on get variable * feat(dev/cli): make function to upload file fix(dev/cli): create command for gracefully kill tunnel after transfer * fix(dev/cli): fix error when ssh tunnel is up * feat(dev/cli): progress bar for uploading fix(dev/cli): make receiver only retrive one request from sender * refactor(dev/server): create new output for ssh proxy * feat(dev/cli): add proxy response on config fule * fix(dev/cli): proxy command support and use shell instead of binary execution --- client/cmd/auth.go | 16 +++-- client/cmd/shell.go | 10 ++- client/internal/rw_config_file.go | 5 +- client/internal/shell.go | 62 +++++++++++-------- server/MDrop.Broker/Constant.cs | 1 + .../Controllers/VerifyController.cs | 20 ++++-- server/docker-compose.yaml | 1 + 7 files changed, 74 insertions(+), 41 deletions(-) diff --git a/client/cmd/auth.go b/client/cmd/auth.go index 9581553..f28d70d 100644 --- a/client/cmd/auth.go +++ b/client/cmd/auth.go @@ -64,8 +64,9 @@ func AuthCommand(args []string) { URL: *url, Token: *token, Tunnel: internal.ConfigFileTunnel{ - Host: verifyData.TunnelHost, - Port: verifyData.TunnelPort, + Host: verifyData.Tunnel.Host, + Port: verifyData.Tunnel.Port, + Proxy: verifyData.Tunnel.Proxy, }, } err = config.WriteConfig() @@ -79,10 +80,13 @@ func AuthCommand(args []string) { } type VerifyJSONReturn struct { - Message string `json:"message"` - IsPublic bool `json:"isPublic"` - TunnelHost string `json:"tunnelHost"` - TunnelPort int `json:"tunnelPort"` + Message string `json:"message"` + IsPublic bool `json:"isPublic"` + Tunnel struct { + Host string `json:"host"` + Port int `json:"port"` + Proxy string `json:"proxy"` + } `json:"tunnel"` // This respon fired when the API is failed ErrorTitle string `json:"title"` diff --git a/client/cmd/shell.go b/client/cmd/shell.go index 4ec92bd..e197c9e 100644 --- a/client/cmd/shell.go +++ b/client/cmd/shell.go @@ -15,8 +15,8 @@ var sshErrGlobal chan error = make(chan error) var sshPidGlobal chan int = make(chan int) func StartShellTunnel(isRemote bool, c internal.ConfigFile, localPort int, remotePort int) { - args := strings.Split(internal.GenerateSSHArgs(isRemote, c, localPort, remotePort), " ") - cmd := exec.Command("ssh", args...) + args := internal.GenerateSSHArgs(isRemote, c, localPort, remotePort) + cmd := exec.Command("sh", "-c", args) stdout, err := cmd.StdoutPipe() stderr, err := cmd.StderrPipe() @@ -48,6 +48,12 @@ func StartShellTunnel(isRemote bool, c internal.ConfigFile, localPort int, remot if strings.Contains(m, "remote port forwarding failed") { sshErrGlobal <- errors.New("Duplicate remote on bridge server") } + if strings.Contains(m, "EXCEPTION") { + sshErrGlobal <- errors.New("Error from Server: "+m) + } + if strings.Contains(m, "exec") && strings.Contains(m, "not found") { + sshErrGlobal <- errors.New("Proxy app not found. Did you install it?") + } fmt.Println(m) } }() diff --git a/client/internal/rw_config_file.go b/client/internal/rw_config_file.go index b32c8f3..0870c49 100644 --- a/client/internal/rw_config_file.go +++ b/client/internal/rw_config_file.go @@ -13,8 +13,9 @@ var ( ) type ConfigFileTunnel struct { - Host string `json:"host"` - Port int `json:"port"` + Host string `json:"host"` + Port int `json:"port"` + Proxy string `json:"proxy"` } type ConfigFile struct { diff --git a/client/internal/shell.go b/client/internal/shell.go index d4eb923..b8124f7 100644 --- a/client/internal/shell.go +++ b/client/internal/shell.go @@ -2,35 +2,47 @@ package internal import ( "fmt" + "strconv" + "strings" ) -func GenerateSSHArgs(isRemote bool, c ConfigFile, localPort int, remotePort int) string { - args := fmt.Sprintf( - "-p %v -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no tunnel@%v", - c.Tunnel.Port, - c.Tunnel.Host, - ) - - flag := [4]int{} - remoteFlag := "" - flag[2] = remotePort +func GenerateSSHArgs(isRemote bool, c ConfigFile, localPort int, remotePort int) string{ + args := []string{ + "-o", + "UserKnownHostsFile=/dev/null", + "-o", + "StrictHostKeyChecking=no", + "tunnel@"+c.Tunnel.Host, + strconv.Itoa(remotePort), + } + generateProxyCommand(&args, c) + if isRemote { - remoteFlag = "-R" - flag[0] = remotePort - flag[1] = localPort + args = append( + []string{"-R", fmt.Sprintf("%v:127.0.0.1:%v", remotePort, localPort)}, + args..., + ) } else { - remoteFlag = "-L" - flag[1] = remotePort - flag[0] = localPort + args = append( + []string{"-L", fmt.Sprintf("%v:127.0.0.1:%v", localPort, remotePort)}, + args..., + ) } - args = fmt.Sprintf( - "%v %v:127.0.0.1:%v %v %v", - remoteFlag, - flag[0], - flag[1], - args, - flag[2], - ) - return args + return "ssh "+strings.Join(args, " ") +} + +func generateProxyCommand(args* []string, c ConfigFile) { + switch c.Tunnel.Proxy { + case "cloudflared": + *args = append( + []string{ + "-o", + fmt.Sprintf("ProxyCommand=\"cloudflared access ssh --hostname %v\"", c.Tunnel.Host), + }, + *args... + ) + default: + *args = append([]string{"-p", strconv.Itoa(c.Tunnel.Port)}, *args...) + } } diff --git a/server/MDrop.Broker/Constant.cs b/server/MDrop.Broker/Constant.cs index e97e193..b4537dd 100644 --- a/server/MDrop.Broker/Constant.cs +++ b/server/MDrop.Broker/Constant.cs @@ -5,6 +5,7 @@ public static class Constant { public static readonly string TunnelHost = Environment.GetEnvironmentVariable("TUNNEL_HOST") ?? "127.0.0.1"; public static readonly int TunnelPort = int.Parse(Environment.GetEnvironmentVariable("TUNNEL_PORT") ?? "2222"); + public static readonly string TunnelProxy = Environment.GetEnvironmentVariable("TUNNEL_PROXY") ?? ""; public static readonly string PrivateModeToken = Environment.GetEnvironmentVariable("PRIVATE_MODE_TOKEN") ?? ""; public static X509Certificate2 Certificate = X509Certificate2.CreateFromEncryptedPemFile( "cert.pem", diff --git a/server/MDrop.Broker/Controllers/VerifyController.cs b/server/MDrop.Broker/Controllers/VerifyController.cs index ce28db3..aba22d2 100644 --- a/server/MDrop.Broker/Controllers/VerifyController.cs +++ b/server/MDrop.Broker/Controllers/VerifyController.cs @@ -33,13 +33,21 @@ public class VerifyReturnJson [JsonPropertyName("message")] public string Message { get; set; } = ""; - [JsonPropertyName("tunnelHost")] - public string TunnelHost { get; set; } = Constant.TunnelHost; - - [JsonPropertyName("tunnelPort")] - public int TunnelPort { get; set; } = Constant.TunnelPort; - [JsonPropertyName("isPublic")] public bool IsPublic { get; set; } = false; + + [JsonPropertyName("tunnel")] + public TunnelProperty Tunnel { get; set; } = new TunnelProperty(); + public class TunnelProperty + { + [JsonPropertyName("host")] + public string Host { get; set; } = Constant.TunnelHost; + + [JsonPropertyName("port")] + public int Port { get; set; } = Constant.TunnelPort; + + [JsonPropertyName("proxy")] + public string Proxy { get; set; } = Constant.TunnelProxy; + } } } diff --git a/server/docker-compose.yaml b/server/docker-compose.yaml index 3a9014a..b756d0c 100644 --- a/server/docker-compose.yaml +++ b/server/docker-compose.yaml @@ -10,5 +10,6 @@ services: PRIVATE_MODE_TOKEN: "" TUNNEL_HOST: 127.0.0.1 TUNNEL_PORT: 2222 + TUNNEL_PROXY: "" ports: - 5000:5000