before I mess it up by changing the way timeouts work

main
Johannes Bülow 2024-02-02 18:47:45 +01:00
parent e5c78de08f
commit 7ff1ec233d
Signed by untrusted user who does not match committer: jmb
GPG Key ID: B56971CF7B8F83A6
7 changed files with 105 additions and 12 deletions

View File

@ -11,7 +11,7 @@ envvars:
# Container Image you want to use # Container Image you want to use
image: git.jmbit.de/jmb/webtop-plus:latest image: git.jmbit.de/jmb/webtop-plus:latest
# Maximum age of Session # Maximum age of Session
maxage: 10800 maxage: 0
# Port Podterminal should listen to # Port Podterminal should listen to
port: 80 port: 80
# Files that will be copied into container on startup # Files that will be copied into container on startup

View File

@ -2,6 +2,7 @@ package pods
import ( import (
"log" "log"
"time"
"github.com/containers/podman/v4/pkg/bindings/containers" "github.com/containers/podman/v4/pkg/bindings/containers"
"github.com/containers/podman/v4/pkg/specgen" "github.com/containers/podman/v4/pkg/specgen"
@ -50,15 +51,30 @@ func DestroyContainer(id string) error {
return nil return nil
} }
// GetContainerIP returns a
func GetContainerIP(id string) (string, error) { func GetContainerIP(id string) (string, error) {
conn := Socket conn := Socket
var ip string
var err error
i := 0
container, err := containers.Inspect(conn, id, nil) for i < 50 {
if err != nil { time.Sleep(100 * time.Millisecond)
log.Println("Could not get IP of container", err) container, err := containers.Inspect(conn, id, nil)
return "", err if err != nil {
log.Println("Could not get IP of container", err)
return "", err
}
ip = container.NetworkSettings.IPAddress
log.Println(ip)
if len(ip) > 5 {
break
}
i++
}
if i > 50 {
log.Println("timed out waiting for IP")
} }
ip := container.NetworkSettings.IPAddress
return ip, err return ip, err
} }

View File

@ -16,6 +16,8 @@ import (
var Socket context.Context var Socket context.Context
var rawSocket net.Conn var rawSocket net.Conn
var OldContainers []string
func socketConnection() context.Context { func socketConnection() context.Context {
uri := "unix:///run/podman/podman.sock" uri := "unix:///run/podman/podman.sock"
conn, err := bindings.NewConnection(context.Background(), uri) conn, err := bindings.NewConnection(context.Background(), uri)

View File

@ -5,9 +5,12 @@ import (
"os" "os"
"github.com/spf13/viper" "github.com/spf13/viper"
"git.jmbit.de/jmb/podterminal/utils"
) )
func readConfigFile() { func readConfigFile() {
sessionKey, _ := utils.RandomString(64)
log.Println("Reading Config") log.Println("Reading Config")
viper.SetConfigFile("/etc/podterminal/config.yaml") viper.SetConfigFile("/etc/podterminal/config.yaml")
viper.SetDefault("port", 80) viper.SetDefault("port", 80)
@ -21,6 +24,8 @@ func readConfigFile() {
viper.SetDefault("skel_target", "/") viper.SetDefault("skel_target", "/")
viper.SetDefault("skel_user", "") viper.SetDefault("skel_user", "")
viper.SetDefault("block_filebrowser", false) viper.SetDefault("block_filebrowser", false)
viper.SetDefault("session_key", sessionKey)
viper.SetDefault("container_port", 3000)
viper.SetDefault("envvars", viper.SetDefault("envvars",
map[string]string{ map[string]string{
"CUSTOM_USER": "user", "CUSTOM_USER": "user",

View File

@ -35,25 +35,32 @@ func createReverseProxy(backendService string) (*httputil.ReverseProxy, error) {
func containerProxy(c *gin.Context) { func containerProxy(c *gin.Context) {
session := sessions.Default(c) session := sessions.Default(c)
session.Save() session.Save()
sessionID := session.ID() sessionID := sessions.Session.ID(session)
if session.Get("ct") == nil { if session.Get("ct") == nil {
log.Println("Creating Container for Session ", sessionID) log.Println("Creating Container for Session ", sessionID)
ct, err := pods.CreateContainer() ct, err := pods.CreateContainer()
session.Set("ct", ct)
session.Save()
if err != nil { if err != nil {
c.HTML(500, "Error", fmt.Sprintf("[%s] Could not create Container: %v", sessionID, err)) c.HTML(500, "Error", fmt.Sprintf("[%s] Could not create Container: %v", sessionID, err))
session.Delete("ct")
session.Save()
c.Abort() c.Abort()
} }
err = pods.StartContainer(ct) err = pods.StartContainer(ct)
if err != nil { if err != nil {
c.HTML(500, "Error", fmt.Sprintf("[%s] Could not start Container: %v", sessionID, err)) c.HTML(500, "Error", fmt.Sprintf("[%s] Could not start Container: %v", sessionID, err))
session.Delete("ct")
session.Save()
c.Abort() c.Abort()
} }
// Hack to wait for Container to start up and get assigned an IP
time.Sleep(3 * time.Second)
ctip, err := pods.GetContainerIP(ct) ctip, err := pods.GetContainerIP(ct)
if err != nil { if err != nil {
c.HTML(500, "Error", fmt.Sprintf("[%s] Could not get Container ip: %v", sessionID, err)) c.HTML(500, "Error", fmt.Sprintf("[%s] Could not get Container ip: %v", sessionID, err))
session.Delete("ct")
session.Save()
c.Abort() c.Abort()
} }
@ -68,9 +75,11 @@ func containerProxy(c *gin.Context) {
"Error", "Error",
fmt.Sprintf("[%s] Could not create Container Proxy: %v", sessionID, err), fmt.Sprintf("[%s] Could not create Container Proxy: %v", sessionID, err),
) )
session.Delete("ct")
session.Save()
c.Abort() c.Abort()
} }
session.Set("ct", ct) session.Set("ready", true)
session.Save() session.Save()
c.Redirect(301, "/") c.Redirect(301, "/")
} else { } else {
@ -80,8 +89,14 @@ func containerProxy(c *gin.Context) {
default: default:
c.HTML(500, "Error", "Session Container ID is not a string") c.HTML(500, "Error", "Session Container ID is not a string")
session.Delete("ct")
session.Save()
c.Abort() c.Abort()
} }
if session.Get("ready") == nil {
time.Sleep(100 * time.Millisecond)
c.Redirect(301, "/")
}
id := session.Get("ct").(string) id := session.Get("ct").(string)
proxy := proxies[id] proxy := proxies[id]
proxy.ServeHTTP(c.Writer, c.Request) proxy.ServeHTTP(c.Writer, c.Request)

View File

@ -8,7 +8,6 @@ import (
"github.com/gin-contrib/sessions" "github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie" "github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/google/uuid"
"github.com/spf13/viper" "github.com/spf13/viper"
) )
@ -42,7 +41,7 @@ func setupRouter() *gin.Engine {
gin.ForceConsoleColor() gin.ForceConsoleColor()
gin.SetMode("release") gin.SetMode("release")
router := gin.New() router := gin.New()
store := cookie.NewStore([]byte(uuid.NewString())) store := cookie.NewStore([]byte(viper.GetString("session_key")))
store.Options(sessions.Options{ store.Options(sessions.Options{
MaxAge: viper.GetInt("maxAge"), MaxAge: viper.GetInt("maxAge"),
}) })

56
web/sessionAging.go Normal file
View File

@ -0,0 +1,56 @@
package web
import (
"time"
"github.com/spf13/viper"
"git.jmbit.de/jmb/podterminal/pods"
)
type sessionData struct {
lastAccess *time.Time
sessionID string
}
var invalidSessions []string
var sessionLastAccess map[string]*sessionData
func initSessionAging() error {
sessionLastAccess = make(map[string]*sessionData)
return nil
}
func updateSession(id string, sessionID string) {
nowTime := time.Now()
sessionLastAccess[id].lastAccess = &nowTime
sessionLastAccess[id].sessionID = sessionID
}
func deleteIdleSessions() {
idleTimout := viper.GetInt("session_timeout")
tenMinutesAgo := -time.Duration(idleTimout) * time.Second
oldAge := time.Now().Add(tenMinutesAgo)
for session, sessionData := range sessionLastAccess {
if oldAge.After(*sessionData.lastAccess) {
pods.DestroyContainer(session)
invalidSessions = append(invalidSessions, sessionData.sessionID)
}
}
}
func IdleSessionCleanup() error {
err := initSessionAging()
if err != nil {
println("Could not initialize Session aging")
return err
}
for {
deleteIdleSessions()
time.Sleep(time.Duration(viper.GetInt("session_timeout")) * time.Second)
}
}