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
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 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
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
Groups are defined by files in the `./groups` directory (this may be
......
......@@ -21,7 +21,7 @@ import (
)
func main() {
var cpuprofile, memprofile, mutexprofile, httpAddr, dataDir string
var cpuprofile, memprofile, mutexprofile, httpAddr string
var udpRange string
flag.StringVar(&httpAddr, "http", ":8443", "web server `address`")
......@@ -31,7 +31,7 @@ func main() {
"redirect to canonical `host`")
flag.BoolVar(&webserver.Insecure, "insecure", false,
"act as an HTTP server rather than HTTPS")
flag.StringVar(&dataDir, "data", "./data/",
flag.StringVar(&group.DataDirectory, "data", "./data/",
"data `directory`")
flag.StringVar(&group.Directory, "groups", "./groups/",
"group description `directory`")
......@@ -112,7 +112,7 @@ func main() {
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
go group.Update()
......@@ -123,7 +123,7 @@ func main() {
serverDone := make(chan struct{})
go func() {
err := webserver.Serve(httpAddr, dataDir)
err := webserver.Serve(httpAddr, group.DataDirectory)
if err != nil {
log.Printf("Server: %v", err)
}
......
......@@ -17,7 +17,7 @@ import (
"github.com/pion/webrtc/v3"
)
var Directory string
var Directory, DataDirectory string
var UseMDNS bool
var UDPMin, UDPMax uint16
......@@ -802,8 +802,67 @@ func matchClient(group string, creds ClientCredentials, users []ClientPattern) (
return false, false
}
// Type Description represents a group description together with some
// metadata about the JSON file it was deserialised from.
// Configuration represents the contents of the data/config.json file.
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 {
// The file this was deserialised from. This is not necessarily
// the name of the group, for example in case of a subgroup.
......
package webserver
import (
"bufio"
"context"
"crypto/tls"
"encoding/json"
......@@ -327,26 +326,20 @@ func publicHandler(w http.ResponseWriter, r *http.Request) {
return
}
func getPassword(dataDir string) (string, string, error) {
f, err := os.Open(filepath.Join(dataDir, "passwd"))
func adminMatch(username, password string) (bool, error) {
conf, err := group.GetConfiguration()
if err != nil {
return "", "", err
return false, err
}
defer f.Close()
r := bufio.NewReader(f)
s, err := r.ReadString('\n')
if err != nil {
return "", "", err
}
l := strings.SplitN(strings.TrimSpace(s), ":", 2)
if len(l) != 2 {
return "", "", errors.New("couldn't parse passwords")
for _, cred := range conf.Admin {
if cred.Username == "" || cred.Username == username {
if ok, _ := cred.Password.Match(password); ok {
return true, nil
}
}
}
return l[0], l[1], nil
return false, nil
}
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) {
u, p, err := getPassword(dataDir)
if err != nil {
log.Printf("Passwd: %v", err)
username, password, ok := r.BasicAuth()
if !ok {
failAuthentication(w, "stats")
return
}
username, password, ok := r.BasicAuth()
if !ok || username != u || password != p {
if ok, err := adminMatch(username, password); !ok {
if err != nil {
log.Printf("Administrator password: %v", err)
}
failAuthentication(w, "stats")
return
}
......@@ -377,7 +371,7 @@ func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) {
ss := stats.GetGroups()
e := json.NewEncoder(w)
err = e.Encode(ss)
err := e.Encode(ss)
if err != nil {
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