prometheus support step 1

updates to documentation.
This commit is contained in:
Erik Brakkee 2024-08-07 20:50:17 +02:00
parent 68804761bf
commit 9d1c6d6616
15 changed files with 296 additions and 123 deletions

View File

@ -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)

View File

@ -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)

20
cmd/converge/notifier.go Normal file
View File

@ -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
}

View File

@ -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)
}
}

View File

@ -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)

View File

@ -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]://<host>[:port]/client/<ID>\n\n" +
usage := "Usage: wsproxy [--id <ID>] [--insecure] ws[s]://<host>[:port]/client/<ID>\n" +
"\n" +
"Here <ID> is the rendez-vous id of a continuous integration job\n" +
"\n" +

7
go.mod
View File

@ -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
)

14
go.sum
View File

@ -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=

View File

@ -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) {

View File

@ -0,0 +1,7 @@
package converge
import "converge/pkg/models"
type Notifier interface {
Publish(state *models.State)
}

View File

@ -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.
</p>
<h2>other tools</h2>
<p>Using available existing tools such as
<a href="https://github.com/namespacelabs/breakpoint">breakpoint</a> in combination
with a websocket tunneling tool such as
<a href="https://github.com/erebe/wstunnel">wstunnel</a> a similar solution can be
obtained. There are however some problems with these solutions that converge is
trying to address:
</p>
<p>
<ul>
<li>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.</li>
<li>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.</li>
<li>breakpoint leaves it open on how users install the breakpoint executable (agent). </li>
<li>Because of the hacky nature of this setup, it is very difficult for users to use
and troubleshoot when things go wrong. </li>
</ul>
</p>
Converve server addresses these issues in the following ways:
<ul>
<li>Use the websocket protocol both for agents and for clients, providing a fixed port and
a supported protocol for kubernetes deploymment.</li>
<li>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. </li>
<li>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. </li>
<li>User-friendly error messages can be given to users in most case when things do not work
out because of <code>wsproxy</code>, an SSH proxy command that also talk to the server
to tell the user if a connection is accepted and if not why not. </li>
<li>A live screen showing the current sessions that are running. </li>
<li>Interactivity in the user's session with notifications about timeouts and a very
simple inactivity timmeout mechanism. </li>
<li>Possibility for the user to define his own shell. </li>
<li>Support for unix like bash shells and command prompt and powershell. </li>
</ul>
<p>
</p>
<h2>how it works</h2>
@ -73,22 +27,21 @@ templ About() {
<p>
The steps involved are as follows:
<ul>
<li>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.
<li>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.
</li>
<li>
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.
<li>The agent sets up multiplexing of connections together with converge server
which allows it to listen on incoming connections.
</li>
<li>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.
<li>This is used by the agent for running an embedded SSH server nad listening for
incoming connection requests from clients.
</li>
<li>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.
</li>
<li>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() {
</ul>
</p>
<p>With regards to the rendez-vous id there are the following remarks:
<ul>
<li> If no id is specified than an id is generated. </li>
<li> If the agent uses an id already in use by another agent, then converge server will
generate a new id. </li>
</ul>
The agent will always print the id and command required to connect to it to standard output.
</p>
<h2>Security</h2>
<p>
@ -107,13 +69,13 @@ templ About() {
data between client and agent.
</p>
<p>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
<a href="usage.html">usage</a> page to configure this, so the additional complexity should
not be an issue.
<p>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
<a href="usage.html">usage</a> page, which provides the exact commands to execute based
on the target environmnet.
</p>
<h2>SSH and SFTP</h2>
@ -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.
</p>
<p>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.
</p>
<p>The sessions have an inactivity timeout. Any keypress on the keyboard by a user
is interpreted as activity. </p>
<h2>Remote shell usage</h2>
@ -150,6 +111,59 @@ templ About() {
The agent sets a <coder>agentdir</coder> environment variable that points to
the directory where the agent is running.
</p>
<h2>other tools</h2>
<p>Using available existing tools such as
<a href="https://github.com/namespacelabs/breakpoint">breakpoint</a> in combination
with a websocket tunneling tool such as
<a href="https://github.com/erebe/wstunnel">wstunnel</a> a similar solution can be
obtained. There are however some problems with these solutions that converge is
trying to address:
</p>
<p>
<ul>
<li>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.</li>
<li>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.</li>
<li>breakpoint leaves it open on how users install the breakpoint executable (agent). </li>
<li>Because of the hacky nature of this setup, it is very difficult for users to use
and troubleshoot when things go wrong. </li>
</ul>
</p>
Converve server addresses these issues in the following ways:
<ul>
<li>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. </li>
<li>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. </li>
<li>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. </li>
<li>User-friendly error messages can be given to users in most case when things do not work
out because of <code>wsproxy</code>, an SSH proxy command that also talk to the server
to tell the user if a connection is accepted and if not why not. </li>
<li>A live screen showing the current sessions that are running. The sessionw webpage provides
additional feedback about the running sessions. </li>
<li>Interactivity in the user's session with notifications about timeouts and a very
simple inactivity timeout mechanism. </li>
<li>Possibility for the user to define his own shell. </li>
<li>Support for unix like bash shells and command prompt and powershell. </li>
</ul>
<p>
</p>
</div>
}

View File

@ -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",
}

View File

@ -25,7 +25,7 @@ templ Downloads() {
<td>wsproxy</td>
<td><a href="../downloads/wsproxy">wsproxy</a></td>
<td><a href="../downloads/wsproxy.exe">wsproxy.exe</a></td>
<td>SSH proxy command that can be directly used by ssh</td>
<td>SSH proxy command that allows ssh to connect using websockets.</td>
</tr>
<tr>
<td>tcptows</td>

View File

@ -19,7 +19,7 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
if usageInputs.RemoteShells[BASH] {
<code-sample id="run-agent-bash">@templ.Raw(addSshKeys(BASH, usageInputs.SshKeys)) <br/>
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent > agent <br/>
{DOWNLOAD_COMMAND[usageInputs.DownloadCommand]} agent http{access.Secure}://{access.BaseUrl}/downloads/agent<br/>
chmod 755 agent <br/>
./agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl}<br/>
rm -f agent <br/>
@ -27,15 +27,15 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
}
if usageInputs.RemoteShells[CMD] {
<code-sample id="run-agent-cmd">@templ.Raw(addSshKeys(CMD, usageInputs.SshKeys)) <br/>
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe <br/>
agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl} <br/>
{DOWNLOAD_COMMAND[usageInputs.DownloadCommand]} agent.exe http{access.Secure}://{access.BaseUrl}/downloads/agent.exe <br/>
.\agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl} <br/>
del agent.exe <br/>
</code-sample>
}
if usageInputs.RemoteShells[POWERSHELL] {
<code-sample id="run-agent-powershell">@templ.Raw(addSshKeys(POWERSHELL, usageInputs.SshKeys)) <br/>
curl --fail-with-body http{access.Secure}://{access.BaseUrl}/downloads/agent.exe > agent.exe <br/>
agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl} <br/>
{DOWNLOAD_COMMAND[usageInputs.DownloadCommand]} agent.exe http{access.Secure}://{access.BaseUrl}/downloads/agent.exe <br/>
.\agent --id {usageInputs.Id} ws{access.Secure}://{access.BaseUrl} <br/>
del agent.exe <br/>
</code-sample>
}
@ -54,6 +54,13 @@ templ AgentUsage(access models.ConvergeAccess, usageInputs UsageInputs) {
<h2>Connecting to the agent</h2>
<p>
You need to install <code>wsproxy</code>, which is available in the <a href="downloads.html">downloads</a> section and
make it availebla in your path. This utility needs to be downloaded only once, if a newer version of
<code>wsproxy</code> is needed it will tell you about that.
</p>
<p>The embedded ssh server in the agent supports both ssh and sftp.
</p>
@ -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"}
</code-sample>
<p>This requires the <code>wsproxy</code> utility which is available in the
<a href="downloads.html">downloads</a> 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.
</p>
<p>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) {
<input id="remote-shell-2" name="remote-shell" type="radio" value={POWERSHELL}> <label for="remote-shell-2">power shell</label>
</td>
</tr>
<tr>
<td class="minimal-width"><label for="download-command">download method</label></td>
<td>
<input checked id="download-command-0" name="download-command" type="radio" value={CURL}> <label for="download-command-0">curl</label>
<input id="download-command-1" name="download-command" type="radio" value={WGET}> <label for="download-command-1">wget</label>
</td>
</tr>
<!--tr>
<td class="minimal-width"><label for="local-shell">local environment</label></td>
<td>

View File

@ -9,16 +9,19 @@ type UsageInputs struct {
SshKeys []string
ErrorMessages []string
RemoteShells map[string]bool
LocalShells map[string]bool
RemoteShells map[string]bool
LocalShells map[string]bool
DownloadCommand string
}
func NewUsageInputs(id string, sshPublicKeys []string, remoteShells []string, localShells []string) UsageInputs {
func NewUsageInputs(id string, sshPublicKeys []string, remoteShells []string, localShells []string,
downloadCommand string) UsageInputs {
inputs := UsageInputs{
Id: id,
SshKeys: sshPublicKeys,
RemoteShells: make(map[string]bool),
LocalShells: make(map[string]bool),
Id: id,
SshKeys: sshPublicKeys,
RemoteShells: make(map[string]bool),
LocalShells: make(map[string]bool),
DownloadCommand: downloadCommand,
}
for _, remoteShell := range remoteShells {
inputs.RemoteShells[remoteShell] = true