From 4b05d7e8d8553a044df21867284a7628c56000c8 Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Sun, 28 Jul 2024 21:31:17 +0200 Subject: [PATCH] Now rendering the index.html using the Templ library. This is in preparation for: 1. creating a base page 2. using tabs: Home, Using, Downloads, Status 3. htmx --- Makefile | 6 +- README.md | 11 ++ cmd/converge/fileserver.go | 25 ++--- go.mod | 1 + go.sum | 2 + pkg/templates/index.templ | 217 +++++++++++++++++++++++++++++++++++++ static/index.html | 8 +- 7 files changed, 248 insertions(+), 22 deletions(-) create mode 100644 README.md create mode 100644 pkg/templates/index.templ diff --git a/Makefile b/Makefile index b71a8a8..856283e 100644 --- a/Makefile +++ b/Makefile @@ -6,10 +6,14 @@ fmt: go fmt ./... + +generate: + templ generate + vet: fmt go vet ./... -build: vet +build: generate vet mkdir -p bin go build -o bin ./cmd/... diff --git a/README.md b/README.md new file mode 100644 index 0000000..adc163b --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ + + +* Environment + +Go version 1.21 + +``` +go install github.com/a-h/templ/cmd/templ@latest + + +``` \ No newline at end of file diff --git a/cmd/converge/fileserver.go b/cmd/converge/fileserver.go index d011542..9854559 100644 --- a/cmd/converge/fileserver.go +++ b/cmd/converge/fileserver.go @@ -1,7 +1,7 @@ package main import ( - "html/template" + "converge/pkg/templates" "net/http" "os" "path/filepath" @@ -38,31 +38,22 @@ func (handler FileHandlerFilter) ServeHTTP(w http.ResponseWriter, r *http.Reques w.Header().Set("Pragma", "no-cache") w.Header().Set("Expires", "0") - filters := make(map[string]string) + secure := "" if r.TLS == nil { - filters["secure"] = "" + secure = "" } else { - filters["secure"] = "s" + secure = "s" } for _, header := range []string{"X-Forwarded-Proto", "X-Scheme", "X-Forwarded-Scheme"} { values := r.Header.Values(header) for _, value := range values { if strings.ToLower(value) == "https" { - filters["secure"] = "s" + secure = "s" } } } - filters["host"] = r.Host + username, _ := os.LookupEnv("CONVERGE_USERNAME") - tmpl, err := template.ParseFiles(filepath.Join(handler.dir, contextPath)) - if err != nil { - // let the filehandler generate the rror - handler.fileHandler.ServeHTTP(w, r) - } - filters["username"], _ = os.LookupEnv("CONVERGE_USERNAME") - err = tmpl.Execute(w, filters) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - // ok, tmpl has written the response + templates.Index(secure, r.Host, username).Render( + r.Context(), w) } diff --git a/go.mod b/go.mod index d114c5b..fe53485 100755 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect + github.com/a-h/templ v0.2.747 // indirect github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect github.com/kr/fs v0.1.0 // indirect golang.org/x/sys v0.22.0 // indirect diff --git a/go.sum b/go.sum index c99068c..aceb5a6 100755 --- a/go.sum +++ b/go.sum @@ -2,6 +2,8 @@ github.com/ActiveState/termtest/conpty v0.5.0 h1:JLUe6YDs4Jw4xNPCU+8VwTpniYOGeKz github.com/ActiveState/termtest/conpty v0.5.0/go.mod h1:LO4208FLsxw6DcNZ1UtuGUMW+ga9PFtX4ntv8Ymg9og= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +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/creack/pty v1.1.21 h1:1/QdRyBaHHJP61QkWMXlOIBfsgdDeeKfK8SYVUWJKf0= diff --git a/pkg/templates/index.templ b/pkg/templates/index.templ new file mode 100644 index 0000000..b0fce8a --- /dev/null +++ b/pkg/templates/index.templ @@ -0,0 +1,217 @@ +package templates + +templ Index(secure string, host string, username string) { + + + + + + + Converge + + + + + +
+
+
+ +
+ +

About

+ +

+ Converge is a utility for troubleshooting builds on continuous integration servers. + It solves a common problem where the cause of job failure is difficult to determine. + This is complicated further by the fact that build jobs are usually run on a build + farm where there is no access to the build agents or in more modern envrionments when + jobs are run in ephemeral containers. +

+ +

+ With Converge it is possible to get remote shell access to such jobs. This works + 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 setup is such that the connection from client (end-user) to server (agent on CI job) + is end-to-end encrypted. The Converge server itself is no more than a bitpipe which pumps + data between client and agent. +

+ +

+ Both ssh and sftp are supported. Multiple shells are also allowed. +

+ +

+ 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. This behavior as well as general expiry can be + controlled from within a shell session by touching a .hold file. After logging in, the + user can control expiry of the session as instructed by messages in the ssh session. + When the timeout of a session is near the user is informed about this with messages + in the shell. +

+ +

Usage

+ +

Continous integration jobs

+ +

+ In a continous integration job, download the agent, chmod it and run it. +

+
{`
+         # linux
+         `}curl http{secure}://{host}/docs/agent > agent{`
+         chmod 755 agent
+         `}./agent --id ID ws{secure}://{host}{`
+
+         # windows
+         `}curl http{secure}://{host}/docs/agent.exe > agent.exe{`
+         `}agent --id ID ws{secure}://{host}{`
+        `}
+

+ Above, ID is a unique id for the job, the so-called rendez-cous ID. This should not conflict with IDs + used by other agents. The ID is used for a rendez-vous between the end-user on a local system and + the continuous integration job running on a build agent. If you don't specify an id, a random + id will be generated. + + The agent to the converge server and tells it the ID. Clients can now connect to the Converge + server to establish a connection to the CI job through converge by also specifying the same + ID. + + Communication between + end-user and agent is encrypted using SSH and the rendez-vous server is unable to + read the contents. The rendez-vous server is nothing more then a glorified bit pipe, + simply transferring data between end-user SSH client and the agent which runs an + embedded SSH server. +

+ +

+ NOTE: When running the agent on windows, an exit of the remote session using + exit in powershell or command prompt does not terminate the shell completely. + To terminate the client ssh session must be killed by closing the terminal. + Cleanup of remote processes on the agent appears to work properly despite this + problem. It is just that exit of the windows shell (powershell or command prompt) + is not detected properly. +

+ +

+ The agent has more options, download the agent and run it without arguments to + see all options. +

+ +

Local clients: using ssh with a proxy command

+ +

wsproxy is a command that can be used as a proxy command for SSH which performs the connection to the + remote server. This command needs to be downloaded only once (see downloads below). It does not depend on + the converge implementation but only on the websocket standards. Other tools that + provide a mapping of stdio to a websocket can also be used instead of wsproxy. +

+

+ Next step is to run a local SSH or SFTP client: +

+ +
+                                                                                                                            {`
+        `}ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{secure}://{host}/client/ID"  { username }{"@localhost"}   {`
+        `}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{secure}://{host}/client/ID" { username }{"@localhost"}   {`
+        `}
+ +

Local clients: using SSH with a local TCP forwarding proxy

+ +

+ This option is less convenient than the proxy command because it requires two separate + commands to execute. +

+ +

+ Local clients can connect using regular ssh and sftp commands through a tunnel that + translates a local TCP port to a websocket connection in converge. See + the downloads section. + This runs a local client that allows SSH to port 10000 and connects to converge using + a websocket connection. +

+ + +

+ Next step is to run a local SSH of SFTP client: +

+ +
                                                           {`
+        `}ssh -oServerAliveInterval=10 -p 10000 { username }{"@localhost"}          {`
+        `}sftp -oServerAliveInterval=10 -oPort=10000 { username }{"@localhost"}     {`
+        `}
+ +

Authentication

+ +

+ The { username } user above the Converge server and + communicated to the agent when the agent is started. This is the + username that must be used when setting up an ssh connection. + Another way to authenticate is through an .authorized_keys file in the + same directory as where the agent is started. + + This can be setup as follows before starting the agent: +

+
                                         {`
+        `}# linux                                                 {`
+        `}echo "ssh-rsa dkddkdkkk a@b.c" > .authorized_keys       {`
+        `}echo "ssh-rsa adfadjfdf d@e.f" >> .authorized_keys      {`
+        `}                                                        {`
+        `}# windows                                               {`
+        `}echo ssh-rsa dkddkdkkk a@b.c > .authorized_keys         {`
+        `}echo ssh-rsa adfadjfdf d@e.f >> .authorized_keys
+                    
+

+ Note that on windows you should not used quotes. +

+ +

Downloads

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ComponentPurposeLinuxWindows
agentThe agent to run inside aa CI jobagentagent.exe
wsproxySSH proxy command that can be directly used by sshwsproxywsproxy.exe
tcptowsTCP to WS tunnel for allowing regular + SSH and SFTP clients to connect to convergetcptowstcptows.exe
+
+
+
+ +
+ + + +} + diff --git a/static/index.html b/static/index.html index 5053d0a..b4b59d7 100644 --- a/static/index.html +++ b/static/index.html @@ -117,8 +117,8 @@

-    ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{{.secure}}://{{.host}}/client/ID"  {{ .username }}@localhost
-    sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{{.secure}}://{{.host}}/client/ID" {{ .username }}@localhost
+    ssh -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{{.secure}}://{{.host}}/client/ID"  {{ .username }}@@localhost
+    sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{{.secure}}://{{.host}}/client/ID" {{ .username }}@@localhost
     

Local clients: using SSH with a local TCP forwarding proxy

@@ -142,8 +142,8 @@

-    ssh -oServerAliveInterval=10 -p 10000 {{ .username }}@localhost
-    sftp -oServerAliveInterval=10 -oPort=10000 {{ .username }}@localhost
+    ssh -oServerAliveInterval=10 -p 10000 {{ .username }}@@localhost
+    sftp -oServerAliveInterval=10 -oPort=10000 {{ .username }}@@localhost
     

Authentication