Now displaying agent number instead of id.

Passing timezone to server side for rendering of time stamps
Configuration of preferred shells.
This commit is contained in:
Erik Brakkee 2024-08-01 19:16:00 +02:00 committed by Erik Brakkee
parent d6fc2e4118
commit e141007f0a
8 changed files with 63 additions and 31 deletions

View File

@ -174,7 +174,11 @@ func printHelp(msg string) {
"--warning-time: advance warning time before sessio ends\n" + "--warning-time: advance warning time before sessio ends\n" +
"--expiry-time: expiry time of the session\n" + "--expiry-time: expiry time of the session\n" +
"--check-interval: interval at which expiry is checked\n" + "--check-interval: interval at which expiry is checked\n" +
"-insecure: allow invalid certificates\n" "--insecure: allow invalid certificates\n" +
"--shells: comma-separated list of shells to add to the front of theshell search path\n" +
" (e.g. 'zsh,sh'). If the shell name contains a slash,then the path must exist:\n" +
" either relative to the agent's current directory or absolute. Otherwise it is looekd\n" +
" up in the system search path. "
fmt.Fprintln(os.Stderr, helpText) fmt.Fprintln(os.Stderr, helpText)
os.Exit(1) os.Exit(1)
@ -211,8 +215,14 @@ func main() {
agentExpriryTime := 10 * time.Minute agentExpriryTime := 10 * time.Minute
tickerInterval := 60 * time.Second tickerInterval := 60 * time.Second
insecure := false insecure := false
shells := []string{"bash", "sh", "ash", "ksh", "zsh", "fish", "tcsh", "csh"}
if runtime.GOOS == "windows" {
shells = []string{"powershell", "bash"}
}
args := os.Args[1:] args := os.Args[1:]
additionalShells := []string{}
commaSeparated := ""
for len(args) > 0 && strings.HasPrefix(args[0], "-") { for len(args) > 0 && strings.HasPrefix(args[0], "-") {
val := "" val := ""
switch args[0] { switch args[0] {
@ -228,12 +238,17 @@ func main() {
tickerInterval, args = parseDuration(args, val) tickerInterval, args = parseDuration(args, val)
case "--insecure": case "--insecure":
insecure = true insecure = true
case "--shells":
commaSeparated, args = getArg(args)
additionalShells = append(additionalShells, strings.Split(commaSeparated, ",")...)
default: default:
printHelp("Unknown option " + args[0]) printHelp("Unknown option " + args[0])
} }
args = args[1:] args = args[1:]
} }
shells = append(additionalShells, shells...)
id = getId(id) id = getId(id)
if len(args) != 1 { if len(args) != 1 {
@ -266,7 +281,7 @@ func main() {
wsConn := websocketutil.NewWebSocketConn(conn, false) wsConn := websocketutil.NewWebSocketConn(conn, false)
defer wsConn.Close() defer wsConn.Close()
shell := chooseShell() shell := chooseShell(shells)
serverInfo, err := comms.AgentInitialization(wsConn, comms.NewAgentInfo(shell)) serverInfo, err := comms.AgentInitialization(wsConn, comms.NewAgentInfo(shell))
if err != nil { if err != nil {
log.Printf("ERROR: %v", err) log.Printf("ERROR: %v", err)
@ -355,14 +370,9 @@ func setupAuthentication(commChannel comms.CommChannel,
return passwordHandler, authorizedKeys return passwordHandler, authorizedKeys
} }
func chooseShell() string { func chooseShell(shells []string) string {
log.Printf("Shell search path is %v", shells)
var err error var err error
shells := []string{"bash", "sh", "ash", "ksh", "zsh", "fish", "tcsh", "csh"}
if runtime.GOOS == "windows" {
shells = []string{"powershell", "bash"}
}
shell := "" shell := ""
for _, candidate := range shells { for _, candidate := range shells {
shell, err = exec.LookPath(candidate) shell, err = exec.LookPath(candidate)

View File

@ -61,8 +61,9 @@ func main() {
state := models.State{} state := models.State{}
agent := models.Agent{ agent := models.Agent{
PublicId: "id", PublicId: "id",
StartTime: time.Now().In(japan), GeneratedId: "100",
StartTime: time.Now().In(japan),
AgentInfo: comms.AgentInfo{ AgentInfo: comms.AgentInfo{
Username: "ci", Username: "ci",
Hostname: "container123", Hostname: "container123",
@ -75,6 +76,7 @@ func main() {
state.Agents = append(state.Agents, agent) state.Agents = append(state.Agents, agent)
client := models.Client{ client := models.Client{
PublicId: "c1", PublicId: "c1",
AgentId: "100",
ClientId: 3, ClientId: 3,
StartTime: time.Now().In(japan), StartTime: time.Now().In(japan),
SessionType: "sftp", SessionType: "sftp",

View File

@ -6,8 +6,9 @@ import (
) )
type Agent struct { type Agent struct {
PublicId string PublicId string
StartTime time.Time GeneratedId string
StartTime time.Time
AgentInfo comms.AgentInfo AgentInfo comms.AgentInfo
ExpiryTime time.Time ExpiryTime time.Time

View File

@ -6,6 +6,7 @@ import (
type Client struct { type Client struct {
PublicId string PublicId string
AgentId string
ClientId int ClientId int
StartTime time.Time StartTime time.Time
SessionType string SessionType string

View File

@ -21,6 +21,7 @@ type AgentConnection struct {
commChannel comms.CommChannel commChannel comms.CommChannel
} }
var agentIdGenerator = concurrency.NewAtomicCounter()
var clientIdGenerator = concurrency.NewAtomicCounter() var clientIdGenerator = concurrency.NewAtomicCounter()
type ClientConnection struct { type ClientConnection struct {
@ -32,19 +33,21 @@ type ClientConnection struct {
func NewAgent(commChannel comms.CommChannel, publicId string, agentInfo comms.AgentInfo) *AgentConnection { func NewAgent(commChannel comms.CommChannel, publicId string, agentInfo comms.AgentInfo) *AgentConnection {
return &AgentConnection{ return &AgentConnection{
Agent: models.Agent{ Agent: models.Agent{
PublicId: publicId, PublicId: publicId,
StartTime: time.Now(), GeneratedId: strconv.Itoa(agentIdGenerator.IncrementAndGet()),
AgentInfo: agentInfo, StartTime: time.Now(),
AgentInfo: agentInfo,
}, },
commChannel: commChannel, commChannel: commChannel,
} }
} }
func NewClient(publicId string, clientConn iowrappers2.ReadWriteAddrCloser, func NewClient(publicId string, agentId string, clientConn iowrappers2.ReadWriteAddrCloser,
agentConn net.Conn) *ClientConnection { agentConn net.Conn) *ClientConnection {
return &ClientConnection{ return &ClientConnection{
Client: models.Client{ Client: models.Client{
PublicId: publicId, PublicId: publicId,
AgentId: agentId,
ClientId: clientIdGenerator.IncrementAndGet(), ClientId: clientIdGenerator.IncrementAndGet(),
StartTime: time.Now(), StartTime: time.Now(),
}, },
@ -197,7 +200,7 @@ func (admin *Admin) addClient(publicId string, clientConn iowrappers2.ReadWriteA
log.Println("Sending connection information to agent") log.Println("Sending connection information to agent")
client := NewClient(publicId, clientConn, agentConn) client := NewClient(publicId, agent.GeneratedId, clientConn, agentConn)
// Before using this connection for SSH we use it to send client metadata to the // Before using this connection for SSH we use it to send client metadata to the
// agent // agent

View File

@ -59,20 +59,14 @@ func (sessions *WebSessions) NewSession(wsConnection net.Conn) *WebSession {
} }
func GetUserLocation(r *http.Request) (*time.Location, error) { func GetUserLocation(r *http.Request) (*time.Location, error) {
// Try to get timezone from a custom header tzName := r.URL.Query().Get("timezone")
tzName := r.Header.Get("X-Timezone")
// If not found in header, try to get from query parameter
if tzName == "" { if tzName == "" {
tzName = r.URL.Query().Get("tz") tzName = r.Header.Get("X-Timezone")
} }
//log.Println("GetUserLocation: timezone ", tzName)
// If still not found, default to UTC
if tzName == "" { if tzName == "" {
tzName = "UTC" tzName = "UTC"
} }
// Load the location
return time.LoadLocation(tzName) return time.LoadLocation(tzName)
} }

View File

@ -24,15 +24,36 @@ templ BasePage(tab int) {
<title>Converge</title> <title>Converge</title>
</head> </head>
<body> <body>
<script> <!-- script>
htmx.logAll(); htmx.logAll();
</script -->
<script>
function getTimezone() {
return Intl.DateTimeFormat().resolvedOptions().timeZone;
}
if (!window.originalWebSocket) {
console.log("timezone override for websockets")
window.originalWebSocket = htmx.createWebSocket
htmx.createWebSocket = function(url) {
let modifiedUrl = url + "?timezone=" + getTimezone()
return window.originalWebSocket(modifiedUrl)
}
}
document.body.addEventListener(
"htmx:configRequest",
function(evt) {
console.log("Adding timezone to htmx request headers");
evt.detail.headers["X-Timezone"] = getTimezone();
}
);
</script> </script>
<script src="../static/js/bootstrap.bundle.min.js" <script src="../static/js/bootstrap.bundle.min.js"
crossorigin="anonymous"></script> crossorigin="anonymous"></script>
<div class="container-fluid"> <div class="container-fluid" hx-boost="true">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
<nav class="navbar navbar-expand-sm navbar-light bg-light"> <nav class="navbar navbar-expand-sm navbar-light bg-light">

View File

@ -45,7 +45,7 @@ templ State(state *models.State, location *time.Location) {
</thead> </thead>
for _, agent := range state.Agents { for _, agent := range state.Agents {
<tr> <tr>
<td>{agent.PublicId}</td> <td>{agent.GeneratedId}</td>
<td>{agent.StartTime.In(location).Format(time.DateTime)}</td> <td>{agent.StartTime.In(location).Format(time.DateTime)}</td>
<td>{agent.ExpiryTime.In(location).Format(time.DateTime)}</td> <td>{agent.ExpiryTime.In(location).Format(time.DateTime)}</td>
<td>{agent.AgentInfo.Username}</td> <td>{agent.AgentInfo.Username}</td>
@ -75,7 +75,7 @@ templ State(state *models.State, location *time.Location) {
</thead> </thead>
for _, client := range state.Clients { for _, client := range state.Clients {
<tr> <tr>
<td>{client.PublicId}</td> <td>{client.AgentId}</td>
<td>{strconv.Itoa(client.ClientId)}</td> <td>{strconv.Itoa(client.ClientId)}</td>
<td>{client.StartTime.In(location).Format(time.DateTime)}</td> <td>{client.StartTime.In(location).Format(time.DateTime)}</td>
<td>{client.SessionType}</td> <td>{client.SessionType}</td>