work in progress:
* usage page now has more dynamic part where user can enter id and publis ssh keys and the server will generate the appropriate commmands to execute depending on the local and remote shell.
This commit is contained in:
parent
4f6d9c209d
commit
62b51a6d09
@ -161,6 +161,9 @@ func main() {
|
||||
http.FileServer(http.Dir(downloadDir))))
|
||||
http.HandleFunc("/", catchAllHandler)
|
||||
|
||||
// create usage generator
|
||||
http.HandleFunc("/usage", generateCLIExammple)
|
||||
|
||||
// Start HTTP server
|
||||
fmt.Println("Rendez-vous server listening on :8000")
|
||||
log.Fatal(http.ListenAndServe(":8000", nil))
|
||||
|
36
cmd/converge/convergeaccess.go
Normal file
36
cmd/converge/convergeaccess.go
Normal file
@ -0,0 +1,36 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"converge/pkg/models"
|
||||
"converge/pkg/server/converge"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func getConvergeAccess(r *http.Request, sshRemoteUser string) models.ConvergeAccess {
|
||||
secure := ""
|
||||
if r.TLS == nil {
|
||||
secure = ""
|
||||
} else {
|
||||
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" {
|
||||
secure = "s"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
location, err := converge.GetUserLocation(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return models.ConvergeAccess{
|
||||
Secure: secure,
|
||||
HostPort: r.Host,
|
||||
Location: location,
|
||||
Username: sshRemoteUser,
|
||||
}
|
||||
}
|
@ -1,34 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"converge/pkg/server/converge"
|
||||
templates2 "converge/pkg/server/templates"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func pageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
secure := ""
|
||||
if r.TLS == nil {
|
||||
secure = ""
|
||||
} else {
|
||||
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" {
|
||||
secure = "s"
|
||||
}
|
||||
}
|
||||
}
|
||||
username, _ := os.LookupEnv("CONVERGE_USERNAME")
|
||||
|
||||
location, err := converge.GetUserLocation(r)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
access := getConvergeAccess(r, username)
|
||||
|
||||
switch r.URL.Path {
|
||||
case "":
|
||||
@ -37,12 +17,13 @@ func pageHandler(w http.ResponseWriter, r *http.Request) {
|
||||
fallthrough
|
||||
case "index.html":
|
||||
templates2.AboutTab().Render(r.Context(), w)
|
||||
// TODO use contexts later.
|
||||
case "usage.html":
|
||||
templates2.UsageTab(secure, r.Host, username).Render(r.Context(), w)
|
||||
templates2.UsageTab(access).Render(r.Context(), w)
|
||||
case "downloads.html":
|
||||
templates2.DownloadsTab().Render(r.Context(), w)
|
||||
case "sessions.html":
|
||||
templates2.SessionsTab(nil, location).Render(r.Context(), w)
|
||||
templates2.SessionsTab(nil, access.Location).Render(r.Context(), w)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
|
24
cmd/converge/usage.go
Normal file
24
cmd/converge/usage.go
Normal file
@ -0,0 +1,24 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
func generateCLIExammple(w http.ResponseWriter, r *http.Request) {
|
||||
log.Println("usage: got ", r.URL.Path)
|
||||
|
||||
err := r.ParseForm()
|
||||
if err != nil {
|
||||
http.Error(w, "Error parsing form", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
remote_shells := r.Form["remote-shell"]
|
||||
local_shells := r.Form["local-shhell"]
|
||||
keys := r.FormValue("ssh-keys")
|
||||
log.Printf("remote_shells %v", remote_shells)
|
||||
log.Printf("local_shells %v", local_shells)
|
||||
log.Printf("ssh-keys %v", keys)
|
||||
w.Write([]byte(time.Now().Format(time.DateTime)))
|
||||
}
|
@ -37,11 +37,18 @@ func main() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
access := models.ConvergeAccess{
|
||||
Secure: "s",
|
||||
HostPort: "example.com",
|
||||
Location: netherlands,
|
||||
Username: "converge",
|
||||
}
|
||||
|
||||
fullindex := func() templ.Component {
|
||||
return templates2.Index("s", "example.com", "converge")
|
||||
return templates2.Index(access)
|
||||
}
|
||||
usage := func() templ.Component {
|
||||
return templates2.UsageTab("s", "example.com", "converge")
|
||||
return templates2.UsageTab(access)
|
||||
}
|
||||
|
||||
render(dir, "fullindex.html", fullindex)
|
||||
|
11
pkg/models/convergeaccess.go
Normal file
11
pkg/models/convergeaccess.go
Normal file
@ -0,0 +1,11 @@
|
||||
package models
|
||||
|
||||
import "time"
|
||||
|
||||
type ConvergeAccess struct {
|
||||
// 's" when secure, "" otherwise
|
||||
Secure string
|
||||
HostPort string
|
||||
Location *time.Location
|
||||
Username string
|
||||
}
|
@ -1,9 +1,12 @@
|
||||
package templates
|
||||
|
||||
templ Index(secure string, host string, username string) {
|
||||
import "converge/pkg/models"
|
||||
|
||||
|
||||
templ Index(access models.ConvergeAccess) {
|
||||
@BasePage(0) {
|
||||
@About()
|
||||
@Usage(secure, host, username)
|
||||
@Usage(access)
|
||||
@Downloads()
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +1,62 @@
|
||||
package templates
|
||||
|
||||
templ Usage(secure string, host string, username string) {
|
||||
import "converge/pkg/models"
|
||||
|
||||
templ Usage(access models.ConvergeAccess) {
|
||||
<div>
|
||||
<h1>usage</h1>
|
||||
|
||||
|
||||
<h1>Usage</h1>
|
||||
|
||||
<style>
|
||||
.minimal-width {
|
||||
width: 1%;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
|
||||
<form id="inputs" novalidate
|
||||
hx-post="/usage"
|
||||
method="post"
|
||||
hx-trigger="load,input delay:500ms,change"
|
||||
hx-target="#example-cli"
|
||||
hx-on::after-request="saveFormToCookie()">
|
||||
<table class="table table-responsive">
|
||||
<tr>
|
||||
<td class="minimal-width"><label for="rendez-vous-id">rendez-vous id</label></td>
|
||||
<td>
|
||||
<input id="rendez-vous-id" class="form-control" name="rendez-vous-id" type="text" maxlength="40"></input>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="minimal-width"><label for="ssh-keys">public ssh keys</label></td>
|
||||
<td>
|
||||
<textarea id="ssh-keys" class="form-control autogrow" name="ssh-keys" rows="5"></textarea>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="minimal-width"><label for="remote-shell">remote shells</label></td>
|
||||
<td>
|
||||
<input checked id="remote-shell-0" name="remote-shell" type="checkbox" value="*sh"> <label for="remote-shell-0">*.sh</label>
|
||||
<input id="remote-shell-1" name="remote-shell" type="checkbox" value="cmd"> <label for="remote-shell-1">cmd.exe</label>
|
||||
<input id="remote-shell-2" name="remote-shell" type="checkbox" value="cmd"> <label for="remote-shell-2">powerpoint.exe</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="minimal-width"><label for="local-shell">local shell</label></td>
|
||||
<td>
|
||||
<input id="checked local-shell-0" name="local-shell" type="checkbox" value="*sh"> <label for="local-shell-0">*.sh</label>
|
||||
<input id="local-shell-1" name="local-shell" type="checkbox" value="cmd"> <label for="local-shell-1">cmd.exe</label>
|
||||
<input id="local-shell-2" name="local-shell" type="checkbox" value="cmd"> <label for="local-shell-2">powerpoint.exe</label>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</form>
|
||||
|
||||
<div id="example-cli">
|
||||
</div>
|
||||
|
||||
<h1>usage old</h1>
|
||||
|
||||
<h2>Continuous integration jobs</h2>
|
||||
|
||||
@ -12,14 +66,14 @@ templ Usage(secure string, host string, username string) {
|
||||
</p>
|
||||
<pre>{`
|
||||
# linux
|
||||
`}curl http{secure}://{host}/static/agent > agent{`
|
||||
`}curl http{access.Secure}://{access.HostPort}/static/agent > agent{`
|
||||
chmod 755 agent
|
||||
`}./agent --id ID ws{secure}://{host}{`
|
||||
`}./agent --id ID ws{access.Secure}://{access.HostPort}{`
|
||||
rm -f agent
|
||||
|
||||
# windows
|
||||
`}curl http{secure}://{host}/static/agent.exe > agent.exe{`
|
||||
`}agent --id ID ws{secure}://{host}{`
|
||||
`}curl http{access.Secure}://{access.HostPort}/static/agent.exe > agent.exe{`
|
||||
`}agent --id ID ws{access.Secure}://{access.HostPort}{`
|
||||
del agent.exe
|
||||
`}</pre>
|
||||
<p>
|
||||
@ -66,8 +120,8 @@ templ Usage(secure string, host string, username string) {
|
||||
|
||||
<pre>
|
||||
{`
|
||||
`}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{access.Secure}://{access.HostPort}/client/ID" { access.Username }{"@localhost"} {`
|
||||
`}sftp -oServerAliveInterval=10 -oProxyCommand="wsproxy ws{access.Secure}://{access.HostPort}/client/ID" { access.Username }{"@localhost"} {`
|
||||
`}</pre>
|
||||
|
||||
<h2>Local clients: using SSH with a local TCP forwarding proxy</h2>
|
||||
@ -91,8 +145,8 @@ templ Usage(secure string, host string, username string) {
|
||||
</p>
|
||||
|
||||
<pre> {`
|
||||
`}ssh -oServerAliveInterval=10 -p 10000 { username }{"@localhost"} {`
|
||||
`}sftp -oServerAliveInterval=10 -oPort=10000 { username }{"@localhost"} {`
|
||||
`}ssh -oServerAliveInterval=10 -p 10000 { access.Username }{"@localhost"} {`
|
||||
`}sftp -oServerAliveInterval=10 -oPort=10000 { access.Username }{"@localhost"} {`
|
||||
`}</pre>
|
||||
|
||||
<h2>Remote shell usage</h2>
|
||||
@ -145,7 +199,7 @@ templ Usage(secure string, host string, username string) {
|
||||
<h2>Authentication</h2>
|
||||
|
||||
<p>
|
||||
The <code>{ username }</code> user above is configured in the
|
||||
The <code>{ access.Username }</code> user above is configured in the
|
||||
Converge server and is communicated to the agent when the agent is
|
||||
started as well as the password.
|
||||
</p>
|
||||
@ -168,13 +222,91 @@ templ Usage(secure string, host string, username string) {
|
||||
Note that on windows you should not used quotes.
|
||||
</p>
|
||||
|
||||
|
||||
<script>
|
||||
function setCookie(name, value, days) {
|
||||
let expires = "";
|
||||
if (days) {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
|
||||
expires = "; expires=" + date.toUTCString();
|
||||
}
|
||||
document.cookie = name + "=" + encodeURIComponent(value) + expires + "; path=/";
|
||||
}
|
||||
|
||||
function getCookie(name) {
|
||||
const nameEQ = name + "=";
|
||||
const ca = document.cookie.split(';');
|
||||
for(let i=0; i < ca.length; i++) {
|
||||
let c = ca[i];
|
||||
while (c.charAt(0)==' ') c = c.substring(1,c.length);
|
||||
if (c.indexOf(nameEQ) == 0) return decodeURIComponent(c.substring(nameEQ.length,c.length));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function saveFormToCookie() {
|
||||
const form = document.getElementById('inputs');
|
||||
const formData = new FormData(form);
|
||||
for (let element of form.elements) {
|
||||
if (element.id) {
|
||||
console.log("Saving " + element.id)
|
||||
if (element.type === 'checkbox') {
|
||||
console.log("Checkbox " + element.checked)
|
||||
setCookie(element.id, element.checked ? 'true' : 'false', 7);
|
||||
} else if (element.type === 'radio') {
|
||||
if (element.checked) {
|
||||
setCookie(element.id, element.value, 7);
|
||||
}
|
||||
} else {
|
||||
setCookie(element.id, element.value, 7);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadFormFromCookie() {
|
||||
const form = document.getElementById('inputs');
|
||||
for (let element of form.elements) {
|
||||
if (element.id) {
|
||||
const value = getCookie(element.id);
|
||||
console.log("Loading " + element.id + " value: " + value)
|
||||
if (value !== null) {
|
||||
if (element.type === 'checkbox') {
|
||||
element.checked = value === 'true';
|
||||
} else if (element.type === 'radio') {
|
||||
element.checked = (element.value === value);
|
||||
} else {
|
||||
element.value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Save form data to cookie on change
|
||||
document.getElementById('inputs').addEventListener('change', saveFormToCookie);
|
||||
|
||||
document.body.addEventListener('htmx:load', function(event) {
|
||||
loadFormFromCookie();
|
||||
});
|
||||
|
||||
document.body.addEventListener('htmx:afterSettle', function(event) {
|
||||
loadFormFromCookie();
|
||||
});
|
||||
|
||||
// when hx-boost=false
|
||||
// Load form data from cookie on page load
|
||||
//document.addEventListener('DOMContentLoaded', loadFormFromCookie);
|
||||
</script>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
|
||||
|
||||
templ UsageTab(secure string, host string, username string) {
|
||||
templ UsageTab(access models.ConvergeAccess) {
|
||||
@BasePage(2) {
|
||||
@Usage(secure, host, username)
|
||||
@Usage(access)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user