diff --git a/cmd/agent/agent.go b/cmd/agent/agent.go index 0332db1..1ea0f69 100755 --- a/cmd/agent/agent.go +++ b/cmd/agent/agent.go @@ -291,6 +291,16 @@ func main() { if insecure { dialer.TLSClientConfig = &tls.Config{InsecureSkipVerify: true} } + + // Authentiocation + + authorizedKeys, err := NewAuthorizedPublicKeys(authorizedKeysFile) + if err != nil { + os.Exit(1) + } + + // Connect to server + conn, _, err := dialer.Dial(wsURL, nil) if err != nil { log.Println("WebSocket connection error:", err) @@ -327,18 +337,7 @@ func main() { panic(err) } - // Authentiocation - - authorizedKeys, err := NewAuthorizedPublicKeys(authorizedKeysFile) - if err != nil { - os.Exit(1) - } // initial check - pubkeys, _ := authorizedKeys.Parse() - if len(pubkeys) == 0 { - log.Printf("No public keys found in '%s', exiting", authorizedKeysFile) - os.Exit(1) - } go comms.ListenForServerEvents(commChannel) diff --git a/cmd/converge/converge.go b/cmd/converge/converge.go index 0d73952..f93a228 100644 --- a/cmd/converge/converge.go +++ b/cmd/converge/converge.go @@ -1,7 +1,6 @@ package main import ( - "converge/pkg/models" "converge/pkg/server/converge" "converge/pkg/support/websocketutil" "fmt" @@ -95,9 +94,10 @@ func main() { printHelp("") } - notifications := make(chan *models.State, 10) + notifications := NewStateNotifier() admin := converge.NewAdmin(notifications) - websessions := converge.NewWebSessions(notifications) + websessions := converge.NewWebSessions(notifications.webNotificationChannel) + setupPrometheus(notifications.prometheusNotificationChannel) // For agents connecting registrationService := websocketutil.WebSocketService{ @@ -148,7 +148,6 @@ func main() { // websocket endpoints - // TODO remove, simulate contextpath context := HttpContext{path: contextpath} context.HandleFunc("agent/", registrationService.Handle) diff --git a/cmd/converge/notifier.go b/cmd/converge/notifier.go new file mode 100644 index 0000000..b266315 --- /dev/null +++ b/cmd/converge/notifier.go @@ -0,0 +1,20 @@ +package main + +import "converge/pkg/models" + +type StateNotifier struct { + webNotificationChannel chan *models.State + prometheusNotificationChannel chan *models.State +} + +func NewStateNotifier() *StateNotifier { + return &StateNotifier{ + webNotificationChannel: make(chan *models.State, 10), + prometheusNotificationChannel: make(chan *models.State, 10), + } +} + +func (notifier StateNotifier) Publish(state *models.State) { + notifier.webNotificationChannel <- state + notifier.prometheusNotificationChannel <- state +} diff --git a/cmd/converge/prometheus.go b/cmd/converge/prometheus.go new file mode 100644 index 0000000..48fd9b3 --- /dev/null +++ b/cmd/converge/prometheus.go @@ -0,0 +1,95 @@ +package main + +import ( + "converge/pkg/models" + "github.com/prometheus/client_golang/prometheus" + "github.com/prometheus/client_golang/prometheus/promauto" + "github.com/prometheus/client_golang/prometheus/promhttp" + "log" + "net/http" + "strconv" +) + +const NAMESPACE = "converge" + +var ( + agentCount = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: NAMESPACE, + Name: "agent_count", + Help: "Current number of agents", + }) + clientCount = promauto.NewGauge(prometheus.GaugeOpts{ + Namespace: NAMESPACE, + Name: "client_count", + Help: "Current number of clients", + }) + + agentInfo = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: NAMESPACE, + Name: "agent_info", + Help: "A flexible gauge with dynamic labels, always set to 1", + }, + []string{"id", "os"}, // Label names + ) + clientInfo = promauto.NewGaugeVec( + prometheus.GaugeOpts{ + Namespace: NAMESPACE, + Name: "client_info", + Help: "A flexible gauge with dynamic labels, always set to 1", + }, + []string{"id", "agentid"}, // Label names + ) +) + +func agentLabels(agent models.Agent) prometheus.Labels { + return prometheus.Labels{ + "id": agent.PublicId, + "os": agent.AgentInfo.OS, + } +} + +func clientLabels(client models.Client) prometheus.Labels { + return prometheus.Labels{ + "id": strconv.Itoa(client.ClientId), + "agentid": client.PublicId, + } +} + +func agentActive(agent models.Agent) { + agentInfo.With(agentLabels(agent)).Set(1) +} + +func clientActive(client models.Client) { + clientInfo.With(clientLabels(client)).Set(1) +} + +func setupPrometheus(notifications chan *models.State) { + go func() { + for { + state := <-notifications + updateMetrics(state) + } + }() + http.Handle("/metrics", promhttp.Handler()) +} + +func updateMetrics(state *models.State) { + // This implemnetation has a small probability that the metric will be in a partially + // initialized state. This is however unlikely. It would lead to in incorrect determination + // that an agent or client is not available. However, each agent and client will have a UID + // so that is still possible to identify the client or agent even though some values might + // become 0. + + log.Printf("Got notification %v", *state) + agentCount.Set(float64(len(state.Agents))) + agentInfo.Reset() + for _, agent := range state.Agents { + agentActive(agent) + } + clientCount.Set(float64(len(state.Clients))) + clientInfo.Reset() + for _, client := range state.Clients { + clientActive(client) + } +} diff --git a/cmd/converge/usage.go b/cmd/converge/usage.go index 419a594..cae5df1 100644 --- a/cmd/converge/usage.go +++ b/cmd/converge/usage.go @@ -29,22 +29,22 @@ func generateCLIExammple(w http.ResponseWriter, r *http.Request) { remoteShells := r.Form["remote-shell"] localShells := r.Form["local-shell"] keysString := r.FormValue("ssh-keys") - sshPublicKeys := make([]string, 0) - for _, line := range strings.Split(keysString, "\n") { - line := strings.TrimSpace(line) - if line != "" { - sshPublicKeys = append(sshPublicKeys, line) - } - } access := getConvergeAccess(r) - usageInputs := templates.NewUsageInputs(id, sshPublicKeys, remoteShells, localShells) + downloadCommand := r.FormValue("download-command") + + sshPublicKeys := strings.Split(keysString, "\n") + usageInputs := templates.NewUsageInputs(id, sshPublicKeys, remoteShells, localShells, downloadCommand) matched, _ := regexp.MatchString("^[a-zA-Z0-9-_]+$", id) if !matched { usageInputs.ErrorMessages = append(usageInputs.ErrorMessages, "ID may consist only of alphanumeric characters, '-', and '_'") } validPubKeys := 0 for index, pubkey := range sshPublicKeys { + pubkey = strings.TrimSpace(pubkey) + if pubkey == "" { + continue + } _, _, _, _, err := ssh.ParseAuthorizedKey([]byte(pubkey)) if err != nil { keysummary := pubkey @@ -52,14 +52,14 @@ func generateCLIExammple(w http.ResponseWriter, r *http.Request) { keysummary = keysummary[:20] + " ... " + keysummary[len(pubkey)-20:] } usageInputs.ErrorMessages = append(usageInputs.ErrorMessages, - fmt.Sprintf("ssh public key %d: %s: %s", index, keysummary, err.Error())) + fmt.Sprintf("ssh public key line %d: %s: %s", index+1, keysummary, err.Error())) } else { validPubKeys++ } } if validPubKeys == 0 { usageInputs.ErrorMessages = append(usageInputs.ErrorMessages, - "No valid public keys configured, password authentication will be used which is less secure.") + "No valid public keys configured. Without these the agent will not work.") } err = templates.ShellUsage(access, usageInputs).Render(r.Context(), w) diff --git a/cmd/wsproxy/wsproxy.go b/cmd/wsproxy/wsproxy.go index 143914d..9b91c99 100644 --- a/cmd/wsproxy/wsproxy.go +++ b/cmd/wsproxy/wsproxy.go @@ -43,7 +43,7 @@ func printHelp(msg string) { if msg != "" { fmt.Fprintf(os.Stderr, "ERROR: %s\n\n", msg) } - usage := "Usage: wsproxy [--insecure] ws[s]://[:port]/client/\n\n" + + usage := "Usage: wsproxy [--id ] [--insecure] ws[s]://[:port]/client/\n" + "\n" + "Here is the rendez-vous id of a continuous integration job\n" + "\n" + diff --git a/go.mod b/go.mod index 8969114..cb454ed 100755 --- a/go.mod +++ b/go.mod @@ -18,6 +18,13 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/kr/fs v0.1.0 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.48.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect golang.org/x/sys v0.22.0 // indirect + google.golang.org/protobuf v1.33.0 // indirect ) diff --git a/go.sum b/go.sum index f66c644..cd8d6e5 100755 --- a/go.sum +++ b/go.sum @@ -6,6 +6,10 @@ github.com/a-h/templ v0.2.747 h1:D0dQ2lxC3W7Dxl6fxQ/1zZHBQslSkTSvl5FxP/CfdKg= github.com/a-h/templ v0.2.747/go.mod h1:69ObQIbrcuwPCU32ohNaWce3Cb7qM5GMiqN1K+2yop4= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8= github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= github.com/creack/pty v1.1.21/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -27,6 +31,14 @@ github.com/pkg/sftp v1.13.6 h1:JFZT4XbOU7l77xGSpOdW+pwIMqP044IyjXX6FGyEKFo= github.com/pkg/sftp v1.13.6/go.mod h1:tz1ryNURKu77RL+GuCzmoJYxQczL3wLNNpPWagdg4Qk= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= +github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= @@ -68,6 +80,8 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= +google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/pkg/server/converge/admin.go b/pkg/server/converge/admin.go index f9d5866..2545ce8 100644 --- a/pkg/server/converge/admin.go +++ b/pkg/server/converge/admin.go @@ -59,10 +59,10 @@ type Admin struct { mutex sync.Mutex agents map[string]*AgentConnection clients []*ClientConnection - notifications chan *models.State + notifications Notifier } -func NewAdmin(notifications chan *models.State) *Admin { +func NewAdmin(notifications Notifier) *Admin { admin := Admin{ mutex: sync.Mutex{}, agents: make(map[string]*AgentConnection), @@ -120,7 +120,7 @@ func (admin *Admin) logStatus() { notification := admin.createNotification() notification.Ascii = strings.Join(lines, "\n") - admin.notifications <- notification + admin.notifications.Publish(notification) } func (admin *Admin) getFreeId(publicId string) (string, error) { diff --git a/pkg/server/converge/notifier.go b/pkg/server/converge/notifier.go new file mode 100644 index 0000000..361ca9a --- /dev/null +++ b/pkg/server/converge/notifier.go @@ -0,0 +1,7 @@ +package converge + +import "converge/pkg/models" + +type Notifier interface { + Publish(state *models.State) +} diff --git a/pkg/server/templates/about.templ b/pkg/server/templates/about.templ index a6a72f1..1c5761f 100644 --- a/pkg/server/templates/about.templ +++ b/pkg/server/templates/about.templ @@ -16,56 +16,10 @@ templ About() { by configuring the build job to connect to a Converge server using an agent program. The agent program can be downloaded from within the CI job using curl or wget. Next, an end-user can connect to the Converge server, a rendez-vous server, that connects - the client and server together. + the client and server together based on a common identifier specified by both client and + server.

-

other tools

- -

Using available existing tools such as - breakpoint in combination - with a websocket tunneling tool such as - wstunnel a similar solution can be - obtained. There are however some problems with these solutions that converge is - trying to address: -

- -

-

    -
  • deployment: Breakpoint uses an embedded SSH server which is a really good idea but - uses the QUIC protocol for connecting to a rendez-vous server. The rendez-vous server than - exposes a random port for every client. This make deployment on kubernetes really hard - where fixed ports must be used and QUIC is also not a widely supported protocol.
  • -
  • The problem with the random ports can be solved by using wstunnel running together - with breakpoint server in a kubernetes pod, where wstunnel can forward traffic over an - extern websocket connection to the local random port that breakpoint server is listening on.
  • -
  • breakpoint leaves it open on how users install the breakpoint executable (agent).
  • -
  • Because of the hacky nature of this setup, it is very difficult for users to use - and troubleshoot when things go wrong.
  • -
- -

- Converve server addresses these issues in the following ways: -
    -
  • Use the websocket protocol both for agents and for clients, providing a fixed port and - a supported protocol for kubernetes deploymment.
  • -
  • Providing online documentation where the instructions take into account the - hostname and protocol where converge is running allowing users to cut and paste - instructions that can be used without modification. In the usage page the users - can even generate the correct agent startup commands and client connection commands - based on the type of shell they are connecting to.
  • -
  • Converge server provides out of the box downloads of required software. This makes sure - client and server are always up to date. In addition a protocol version check is done.
  • -
  • User-friendly error messages can be given to users in most case when things do not work - out because of wsproxy, an SSH proxy command that also talk to the server - to tell the user if a connection is accepted and if not why not.
  • -
  • A live screen showing the current sessions that are running.
  • -
  • Interactivity in the user's session with notifications about timeouts and a very - simple inactivity timmeout mechanism.
  • -
  • Possibility for the user to define his own shell.
  • -
  • Support for unix like bash shells and command prompt and powershell.
  • -
-

-

how it works

@@ -73,22 +27,21 @@ templ About() {

The steps involved are as follows:

    -
  • The agent connects to converge server. If no id is specified than a new id will - be generated. The ids specified by different agents must be unique. If the agent - specifies an id that is already in use, then a new id will be generated. - When started the agent will echo the commands to connect to it in its output. +
  • The agent connects to converge server and specifies an id, the so-called rendez-vous id, + identifying the agent. + The agent outputs ane example command that can be used to connect to this agent.
  • -
  • - Since the emmbedded SSH server in the agent will allow multiple clients to connect - to it, it wants to listen for copnnections. By default it cannot do this, it just - setup a connection to the converge server, but the converge server can in general - not connect back to it because of networking. Therefore, a multiplexing library is - used to establish multiple virtual connections over a single TCP connection. - The agent can now listen for connections from clients. +
  • The agent sets up multiplexing of connections together with converge server + which allows it to listen on incoming connections.
  • -
  • The agent connects to the converge server using the commmand specified by the agent. - The converge server can then match the agent with the client based on the id and - the connectio at network level is established. +
  • This is used by the agent for running an embedded SSH server nad listening for + incoming connection requests from clients. +
  • +
  • The client/user connects to the converge server using the commmand specified by the agent. + This uses the same id as that used by the agent. The converge server can now match these + ids an set up an end-to-end connection from client to agent. The role of converge server + is simply in matching these ids and connecting the two websocket connections (from agent + and from client) together by copying data between them as it arrives.
  • The embedded SSH server now performs authentication, after successful login, a shell is spwaned and the network connection of the user is connected to it. @@ -99,6 +52,15 @@ templ About() {

+

With regards to the rendez-vous id there are the following remarks: +

    +
  • If no id is specified than an id is generated.
  • +
  • If the agent uses an id already in use by another agent, then converge server will + generate a new id.
  • +
+ The agent will always print the id and command required to connect to it to standard output. +

+

Security

@@ -107,13 +69,13 @@ templ About() { data between client and agent.

-

Currently converge server still supports password based login but this will be disabled. - Image two people configuring an agent with the same id where one of the agents actually - gets it and other gets a new id. Now, with a password each user can access each other's - agents. This is of course highly confusing and undesirable. Converge server already support - authorized keys but this is not yet mandatory. I is made extremely easy through the - usage page to configure this, so the additional complexity should - not be an issue. +

Using authorized keys is a secure way of connectiong. When running the agent, the authorized keys + must be put in a file, allowing only the designated users to connect. The file containing authorized keys + can also be edited during a session with the agent, allowing more people to be added when required without + having to start over again. + Using authorized keys is made easy through the + usage page, which provides the exact commands to execute based + on the target environmnet.

SSH and SFTP

@@ -128,14 +90,13 @@ templ About() { There is a timeout mechanism in the agent such that jobs do not hang indefinitely waiting for a connection. This mechanism is useful to make sure build agents do not keep build agents occupied for a long time. By default, the agent exits with status 0 when - the first client exits after logging in. + the first client exits after logging in. The timeout is an inactivity timeout which is reset + every time the user presses a key on the keyboard.

When the user touches a .hold file, the agent keeps waiting for connections even - after the last client logs out, taking into account the timeout. + after the last client logs out, taking into account the timeout. By default the agent + exits when the last user has logged out.

-

The sessions have an inactivity timeout. Any keypress on the keyboard by a user - is interpreted as activity.

-

Remote shell usage

@@ -150,6 +111,59 @@ templ About() { The agent sets a agentdir environment variable that points to the directory where the agent is running.

+ +

other tools

+ +

Using available existing tools such as + breakpoint in combination + with a websocket tunneling tool such as + wstunnel a similar solution can be + obtained. There are however some problems with these solutions that converge is + trying to address: +

+ +

+

    +
  • Breakpoint uses an embedded SSH server which is a really good idea but + uses the QUIC protocol for connecting to a rendez-vous server. The rendez-vous server than + exposes a random port for every client. This make deployment on kubernetes really hard + where fixed ports must be used and QUIC is also not a widely supported protocol.
  • +
  • The problem with the random ports can be solved by using wstunnel running together + with breakpoint server in a kubernetes pod, where wstunnel can forward traffic over an + extern websocket connection to the local random port that breakpoint server is listening on.
  • +
  • breakpoint leaves it open on how users install the breakpoint executable (agent).
  • +
  • Because of the hacky nature of this setup, it is very difficult for users to use + and troubleshoot when things go wrong.
  • +
+ +

+ Converve server addresses these issues in the following ways: +
    +
  • Use the websocket protocol both for agents and for clients, providing a fixed port and + a supported protocol for kubernetes deploymment. Websockets are also supported by + kubernetes ingress controllers so this makes it easy to deploy on kubernetes.
  • +
  • Providing online documentation where the instructions take into account the + hostname and protocol where converge is running allowing users to cut and paste + instructions that can be used without modification. In the usage page the users + can even generate the correct agent startup commands and client connection commands + based on the type of shell they are connecting to.
  • +
  • Converge server provides out of the box downloads of required software. This makes sure + client and server are always up to date and can be downloaded in any continuous integration + job without having to package the required executables in an ad-hoc way. + In addition a protocol version check is done.
  • +
  • User-friendly error messages can be given to users in most case when things do not work + out because of wsproxy, an SSH proxy command that also talk to the server + to tell the user if a connection is accepted and if not why not.
  • +
  • A live screen showing the current sessions that are running. The sessionw webpage provides + additional feedback about the running sessions.
  • +
  • Interactivity in the user's session with notifications about timeouts and a very + simple inactivity timeout mechanism.
  • +
  • Possibility for the user to define his own shell.
  • +
  • Support for unix like bash shells and command prompt and powershell.
  • +
+

+

+ } diff --git a/pkg/server/templates/constants.go b/pkg/server/templates/constants.go index 6bf968a..3decb2f 100644 --- a/pkg/server/templates/constants.go +++ b/pkg/server/templates/constants.go @@ -3,3 +3,11 @@ package templates const BASH = "*.sh" const CMD = "cmd" const POWERSHELL = "powershell" + +const CURL = "curl" +const WGET = "wget" + +var DOWNLOAD_COMMAND = map[string]string{ + CURL: "curl -o", + WGET: "wget -O", +} diff --git a/pkg/server/templates/downloads.templ b/pkg/server/templates/downloads.templ index c9125b9..de45ec5 100644 --- a/pkg/server/templates/downloads.templ +++ b/pkg/server/templates/downloads.templ @@ -25,7 +25,7 @@ templ Downloads() { wsproxy wsproxy wsproxy.exe - SSH proxy command that can be directly used by ssh + SSH proxy command that allows ssh to connect using websockets. tcptows diff --git a/pkg/server/templates/usage.templ b/pkg/server/templates/usage.templ index 7a75626..263f719 100644 --- a/pkg/server/templates/usage.templ +++ b/pkg/server/templates/usage.templ @@ -19,7 +19,7 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) { if usageInputs.RemoteShells[BASH] { @templ.Raw(addSshKeys(BASH, usageInputs.SshKeys))
- curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent > agent
+ {DOWNLOAD_COMMAND[usageInputs.DownloadCommand]} agent http{access.Secure}://{access.BaseUrl}/downloads/agent
chmod 755 agent
./agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}
rm -f agent
@@ -27,15 +27,15 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) { } if usageInputs.RemoteShells[CMD] { @templ.Raw(addSshKeys(CMD, usageInputs.SshKeys))
- curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe
- agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}
+ {DOWNLOAD_COMMAND[usageInputs.DownloadCommand]} agent.exe http{access.Secure}://{access.BaseUrl}/downloads/agent.exe
+ .\agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}
del agent.exe
} if usageInputs.RemoteShells[POWERSHELL] { @templ.Raw(addSshKeys(POWERSHELL, usageInputs.SshKeys))
- curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe
- agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}
+ {DOWNLOAD_COMMAND[usageInputs.DownloadCommand]} agent.exe http{access.Secure}://{access.BaseUrl}/downloads/agent.exe
+ .\agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}
del agent.exe
} @@ -54,6 +54,13 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {

Connecting to the agent

+

+ You need to install wsproxy, which is available in the downloads section and + make it availebla in your path. This utility needs to be downloaded only once, if a newer version of + wsproxy is needed it will tell you about that. +

+ +

The embedded ssh server in the agent supports both ssh and sftp.

@@ -64,13 +71,6 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) { sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.BaseUrl}/client/{usageInputs.Id}" {"localhost"}
- -

This requires the wsproxy utility which is available in the - downloads section. This utility must be downloaded - only once since it is quite generic. It will warn you when it a newer version must - be downloaded. -

-

For other ssh clients that do not support the openssh ProxyCommand option, there is another way to connect. In this method, a local port forwarder is started that forwards a local port to the webserver. Then you can start an ssh client that connects to the local tcp port. @@ -183,6 +183,13 @@ templ Usage(access models.ConvergeAccess) { + + + + + + +