Commit c0b30c85 authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Move administrator password to data/config.json.

parent c64ec4cc
...@@ -12,15 +12,6 @@ On Windows, do ...@@ -12,15 +12,6 @@ On Windows, do
go build -ldflags="-s -w" go build -ldflags="-s -w"
## Set the server administrator credentials
This step is optional, it is currently only relevant for access to the
`/stats.html` and `/stats.json` locations.
mkdir data
echo 'god:topsecret' > data/passwd
## Set up a group ## Set up a group
Set up a group called *test* by creating a file `groups/test.json`: Set up a group called *test* by creating a file `groups/test.json`:
......
...@@ -37,6 +37,21 @@ available commands; the output depends on whether you are an operator or ...@@ -37,6 +37,21 @@ available commands; the output depends on whether you are an operator or
not. not.
# The global configuration file
The server may be configured in the JSON file `data/config.json`. This
file may look as follows:
{
"admin":[{"username":"root","password":"secret"}]
}
The fields are as follows:
- `admin` defines the users allowed to look at the `/stats.html` file; it
has the same syntax as user definitions in groups (see below).
# Group definitions # Group definitions
Groups are defined by files in the `./groups` directory (this may be Groups are defined by files in the `./groups` directory (this may be
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
) )
func main() { func main() {
var cpuprofile, memprofile, mutexprofile, httpAddr, dataDir string var cpuprofile, memprofile, mutexprofile, httpAddr string
var udpRange string var udpRange string
flag.StringVar(&httpAddr, "http", ":8443", "web server `address`") flag.StringVar(&httpAddr, "http", ":8443", "web server `address`")
...@@ -31,7 +31,7 @@ func main() { ...@@ -31,7 +31,7 @@ func main() {
"redirect to canonical `host`") "redirect to canonical `host`")
flag.BoolVar(&webserver.Insecure, "insecure", false, flag.BoolVar(&webserver.Insecure, "insecure", false,
"act as an HTTP server rather than HTTPS") "act as an HTTP server rather than HTTPS")
flag.StringVar(&dataDir, "data", "./data/", flag.StringVar(&group.DataDirectory, "data", "./data/",
"data `directory`") "data `directory`")
flag.StringVar(&group.Directory, "groups", "./groups/", flag.StringVar(&group.Directory, "groups", "./groups/",
"group description `directory`") "group description `directory`")
...@@ -112,7 +112,7 @@ func main() { ...@@ -112,7 +112,7 @@ func main() {
log.Printf("File descriptor limit is %v, please increase it!", n) log.Printf("File descriptor limit is %v, please increase it!", n)
} }
ice.ICEFilename = filepath.Join(dataDir, "ice-servers.json") ice.ICEFilename = filepath.Join(group.DataDirectory, "ice-servers.json")
// make sure the list of public groups is updated early // make sure the list of public groups is updated early
go group.Update() go group.Update()
...@@ -123,7 +123,7 @@ func main() { ...@@ -123,7 +123,7 @@ func main() {
serverDone := make(chan struct{}) serverDone := make(chan struct{})
go func() { go func() {
err := webserver.Serve(httpAddr, dataDir) err := webserver.Serve(httpAddr, group.DataDirectory)
if err != nil { if err != nil {
log.Printf("Server: %v", err) log.Printf("Server: %v", err)
} }
......
...@@ -17,7 +17,7 @@ import ( ...@@ -17,7 +17,7 @@ import (
"github.com/pion/webrtc/v3" "github.com/pion/webrtc/v3"
) )
var Directory string var Directory, DataDirectory string
var UseMDNS bool var UseMDNS bool
var UDPMin, UDPMax uint16 var UDPMin, UDPMax uint16
...@@ -802,8 +802,67 @@ func matchClient(group string, creds ClientCredentials, users []ClientPattern) ( ...@@ -802,8 +802,67 @@ func matchClient(group string, creds ClientCredentials, users []ClientPattern) (
return false, false return false, false
} }
// Type Description represents a group description together with some // Configuration represents the contents of the data/config.json file.
// metadata about the JSON file it was deserialised from. type Configuration struct {
// The modtime and size of the file. These are used to detect
// when a file has changed on disk.
modTime time.Time `json:"-"`
fileSize int64 `json:"-"`
Admin []ClientPattern `json:"admin"`
}
var configuration struct {
mu sync.Mutex
configuration *Configuration
}
func GetConfiguration() (*Configuration, error) {
configuration.mu.Lock()
defer configuration.mu.Unlock()
if configuration.configuration == nil {
configuration.configuration = &Configuration{}
}
filename := filepath.Join(DataDirectory, "config.json")
fi, err := os.Stat(filename)
if err != nil {
if os.IsNotExist(err) {
if !configuration.configuration.modTime.Equal(
time.Time{},
) {
configuration.configuration = &Configuration{}
return configuration.configuration, nil
}
}
return nil, err
}
if configuration.configuration.modTime.Equal(fi.ModTime()) &&
configuration.configuration.fileSize == fi.Size() {
return configuration.configuration, nil
}
f, err := os.Open(filename)
if err != nil {
return nil, err
}
defer f.Close()
d := json.NewDecoder(f)
d.DisallowUnknownFields()
var conf Configuration
err = d.Decode(&conf)
if err != nil {
return nil, err
}
configuration.configuration = &conf
return configuration.configuration, nil
}
// Description represents a group description together with some metadata
// about the JSON file it was deserialised from.
type Description struct { type Description struct {
// The file this was deserialised from. This is not necessarily // The file this was deserialised from. This is not necessarily
// the name of the group, for example in case of a subgroup. // the name of the group, for example in case of a subgroup.
......
package webserver package webserver
import ( import (
"bufio"
"context" "context"
"crypto/tls" "crypto/tls"
"encoding/json" "encoding/json"
...@@ -327,26 +326,20 @@ func publicHandler(w http.ResponseWriter, r *http.Request) { ...@@ -327,26 +326,20 @@ func publicHandler(w http.ResponseWriter, r *http.Request) {
return return
} }
func getPassword(dataDir string) (string, string, error) { func adminMatch(username, password string) (bool, error) {
f, err := os.Open(filepath.Join(dataDir, "passwd")) conf, err := group.GetConfiguration()
if err != nil { if err != nil {
return "", "", err return false, err
} }
defer f.Close()
r := bufio.NewReader(f)
s, err := r.ReadString('\n') for _, cred := range conf.Admin {
if err != nil { if cred.Username == "" || cred.Username == username {
return "", "", err if ok, _ := cred.Password.Match(password); ok {
} return true, nil
}
l := strings.SplitN(strings.TrimSpace(s), ":", 2) }
if len(l) != 2 {
return "", "", errors.New("couldn't parse passwords")
} }
return false, nil
return l[0], l[1], nil
} }
func failAuthentication(w http.ResponseWriter, realm string) { func failAuthentication(w http.ResponseWriter, realm string) {
...@@ -356,15 +349,16 @@ func failAuthentication(w http.ResponseWriter, realm string) { ...@@ -356,15 +349,16 @@ func failAuthentication(w http.ResponseWriter, realm string) {
} }
func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) { func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) {
u, p, err := getPassword(dataDir) username, password, ok := r.BasicAuth()
if err != nil { if !ok {
log.Printf("Passwd: %v", err)
failAuthentication(w, "stats") failAuthentication(w, "stats")
return return
} }
username, password, ok := r.BasicAuth() if ok, err := adminMatch(username, password); !ok {
if !ok || username != u || password != p { if err != nil {
log.Printf("Administrator password: %v", err)
}
failAuthentication(w, "stats") failAuthentication(w, "stats")
return return
} }
...@@ -377,7 +371,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) { ...@@ -377,7 +371,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) {
ss := stats.GetGroups() ss := stats.GetGroups()
e := json.NewEncoder(w) e := json.NewEncoder(w)
err = e.Encode(ss) err := e.Encode(ss)
if err != nil { if err != nil {
log.Printf("stats.json: %v", err) log.Printf("stats.json: %v", err)
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment