diff --git a/dashboard/app.go b/dashboard/app.go index fa55b098..af41a65c 100644 --- a/dashboard/app.go +++ b/dashboard/app.go @@ -2,12 +2,8 @@ package main import ( "context" - "encoding/json" - "fmt" "log" "net/http" - "os" - "strings" "time" "github.com/gin-gonic/gin" @@ -45,11 +41,7 @@ func NewApp(htpasswd_file *string, restrict_to_ips *string, baseURL string, bind } if restrict_to_ips != nil && len(*restrict_to_ips) > 0 { - var err error - app.ips, err = loadIPs(*restrict_to_ips) - if err != nil { - log.Fatal("Unable to parse ip restricting file: ", err) - } + app.loadAndWatchIPs(*restrict_to_ips) baserouter.Use(app.Restrict2IPs) } @@ -58,39 +50,6 @@ func NewApp(htpasswd_file *string, restrict_to_ips *string, baseURL string, bind return app } -func loadIPs(file string) ([]string, error) { - fd, err := os.Open(file) - if err != nil { - return nil, err - } - defer fd.Close() - - var ret []string - jdec := json.NewDecoder(fd) - - if err := jdec.Decode(&ret); err != nil { - return ret, err - } - - return ret, nil -} - -func (app *App) Restrict2IPs(c *gin.Context) { - found := false - for _, ip := range app.ips { - if strings.HasPrefix(c.Request.RemoteAddr, ip) { - found = true - break - } - } - - if found { - c.Next() - } else { - c.AbortWithError(http.StatusForbidden, fmt.Errorf("IP not authorized: %s", c.Request.RemoteAddr)) - } -} - func (app *App) Start() { app.srv = &http.Server{ Addr: app.bind, diff --git a/dashboard/restrict_ip.go b/dashboard/restrict_ip.go new file mode 100644 index 00000000..e2ec172c --- /dev/null +++ b/dashboard/restrict_ip.go @@ -0,0 +1,99 @@ +package main + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "path" + "strings" + "syscall" + + "github.com/gin-gonic/gin" + "gopkg.in/fsnotify.v1" +) + +func (app *App) loadAndWatchIPs(ipfile string) { + // First load of file if it exists + app.tryReloadIPList(ipfile) + + // Register SIGHUP + c := make(chan os.Signal, 1) + signal.Notify(c, syscall.SIGHUP) + go func() { + for range c { + log.Println("SIGHUP received, reloading ip list...") + go app.tryReloadIPList(ipfile) + } + }() + + // Watch the configuration file + if watcher, err := fsnotify.NewWatcher(); err != nil { + log.Fatal(err) + } else { + if err := watcher.Add(path.Dir(ipfile)); err != nil { + log.Fatal("Unable to watch: ", path.Dir(ipfile), ": ", err) + } + + go func() { + defer watcher.Close() + for { + select { + case ev := <-watcher.Events: + if path.Base(ev.Name) == path.Base(ipfile) && ev.Op&(fsnotify.Write|fsnotify.Create) != 0 { + log.Println("IPs file changes, reloading them!") + go app.tryReloadIPList(ipfile) + } + case err := <-watcher.Errors: + log.Println("watcher error:", err) + } + } + }() + } +} + +func (app *App) tryReloadIPList(ipfile string) { + if _, err := os.Stat(ipfile); !os.IsNotExist(err) { + if iplist, err := loadIPs(ipfile); err != nil { + log.Println("ERROR: Unable to read ip file:", err) + } else { + log.Printf("Loading %d IPs to authorized list", len(iplist)) + app.ips = iplist + } + } +} + +func loadIPs(file string) ([]string, error) { + fd, err := os.Open(file) + if err != nil { + return nil, err + } + defer fd.Close() + + var ret []string + jdec := json.NewDecoder(fd) + + if err := jdec.Decode(&ret); err != nil { + return ret, err + } + + return ret, nil +} + +func (app *App) Restrict2IPs(c *gin.Context) { + found := false + for _, ip := range app.ips { + if strings.HasPrefix(c.Request.RemoteAddr, ip) { + found = true + break + } + } + + if found { + c.Next() + } else { + c.AbortWithError(http.StatusForbidden, fmt.Errorf("IP not authorized: %s", c.Request.RemoteAddr)) + } +} diff --git a/fickit-backend.yml b/fickit-backend.yml index 5aa07b7b..e74b875f 100644 --- a/fickit-backend.yml +++ b/fickit-backend.yml @@ -208,9 +208,10 @@ services: - /var/lib/fic/teams - name: fic-dashboard image: nemunaire/fic-dashboard:latest + command: ["/srv/dashboard", "-bind=:8082", "-restrict-to-ips=/srv/DASHBOARD/restricted-ips.json"] binds: - /etc/hosts:/etc/hosts:ro - - /var/lib/fic/dashboard:/srv/DASHBOARD + - /var/lib/fic/dashboard:/srv/DASHBOARD:ro - /var/lib/fic/files:/srv/FILES:ro - /var/lib/fic/teams:/srv/TEAMS:ro - /var/lib/fic/settings:/srv/SETTINGS:ro