before I mess it up by changing the way timeouts work
parent
e5c78de08f
commit
7ff1ec233d
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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"),
|
||||||
})
|
})
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue