Merge branch 'master' into go_mod

This commit is contained in:
Derrick 2020-04-11 19:14:51 -07:00
commit 9114380423
7 changed files with 213 additions and 127 deletions

View file

@ -1,11 +1,12 @@
FROM golang:1.9-alpine as builder FROM golang:1.14-alpine as builder
COPY . /go/src/mumble.info/grumble COPY . /go/src/mumble.info/grumble
WORKDIR /go/src/mumble.info/grumble WORKDIR /go/src/mumble.info/grumble
RUN apk add --no-cache git \ RUN apk add --no-cache git build-base
&& go get -v -t ./... \
RUN go get -v -t ./... \
&& go build mumble.info/grumble/cmd/grumble \ && go build mumble.info/grumble/cmd/grumble \
&& go test -v ./... && go test -v ./...
@ -21,4 +22,7 @@ WORKDIR /data
VOLUME /data VOLUME /data
EXPOSE 64738/tcp
EXPOSE 64738/udp
ENTRYPOINT [ "/usr/bin/grumble", "--datadir", "/data", "--log", "/data/grumble.log" ] ENTRYPOINT [ "/usr/bin/grumble", "--datadir", "/data", "--log", "/data/grumble.log" ]

View file

@ -1,11 +1,12 @@
FROM arm32v6/golang:1.9-alpine as builder FROM arm32v6/golang:1.14-alpine as builder
COPY . /go/src/mumble.info/grumble COPY . /go/src/mumble.info/grumble
WORKDIR /go/src/mumble.info/grumble WORKDIR /go/src/mumble.info/grumble
RUN apk add --no-cache git \ RUN apk add --no-cache git build-base
&& go get -v -t ./... \
RUN go get -v -t ./... \
&& go build mumble.info/grumble/cmd/grumble \ && go build mumble.info/grumble/cmd/grumble \
&& go test -v ./... && go test -v ./...
@ -21,4 +22,7 @@ WORKDIR /data
VOLUME /data VOLUME /data
EXPOSE 64738/tcp
EXPOSE 64738/udp
ENTRYPOINT [ "/usr/bin/grumble", "--datadir", "/data", "--log", "/data/grumble.log" ] ENTRYPOINT [ "/usr/bin/grumble", "--datadir", "/data", "--log", "/data/grumble.log" ]

View file

@ -1,7 +1,7 @@
Linux CI (Travis CI): Linux CI (Travis CI):
[![Build Status](https://travis-ci.com/mumble-voip/grumble.svg?branch=master)](https://travis-ci.org/mumble-voip/grumble) [![Build Status](https://travis-ci.com/mumble-voip/grumble.svg?branch=master)](https://travis-ci.com/mumble-voip/grumble)
Windows CI (AppVeyor): Windows CI (AppVeyor):
@ -23,18 +23,21 @@ https://golang.org/dl/
Once Go is installed, you should set up a GOPATH to avoid clobbering your Go environment's root directory with third party packages. Once Go is installed, you should set up a GOPATH to avoid clobbering your Go environment's root directory with third party packages.
Set up a GOPATH. On Unix, do something like this Set up a GOPATH. On Unix, do something like this
```shell script
$ export GOPATH=$HOME/gocode $ export GOPATH=$HOME/gocode
$ mkdir -p $GOPATH $ mkdir -p $GOPATH
```
and on Windows, do something like this (for cmd.exe): and on Windows, do something like this (for cmd.exe):
```shell script
c:\> set GOPATH=%USERPROFILE%\gocode c:\> set GOPATH=%USERPROFILE%\gocode
c:\> mkdir %GOPATH% c:\> mkdir %GOPATH%
```
Then, it's time to install Grumble. The following line should do the trick: Then, it's time to install Grumble. The following line should do the trick:
```shell script
$ go get mumble.info/grumble/cmd/grumble $ go get mumble.info/grumble/cmd/grumble
```
And that should be it. Grumble has been built, and is available in $GOPATH/bin as 'grumble'. And that should be it. Grumble has been built, and is available in $GOPATH/bin as 'grumble'.
@ -61,25 +64,27 @@ Docker
## Getting the image ## Getting the image
### Building ### Building
```shell script
$ git clone https://github.com/mumble-voip/grumble.git $ git clone https://github.com/mumble-voip/grumble.git
$ cd grumble/ $ cd grumble/
$ docker build -t mumble-voip/grumble . $ docker build -t mumble-voip/grumble .
```
## Running ## Running
### Command line ### Command line
```shell script
$ docker run \ $ docker run \
-v $HOME/.grumble:/data \ -v $HOME/.grumble:/data \
-p 64738:64738 \ -p 64738:64738 \
-p 64738:64738/udp \ -p 64738:64738/udp \
mumble-voip/grumble mumble-voip/grumble
```
### Compose ### Compose
```yaml
version: '3' version: '3'
services: services:
grumble: grumble:
image: mumble-voip/grumble image: mumble-voip/grumble
ports: ports:
@ -87,3 +92,4 @@ Docker
- 64738:64738/udp - 64738:64738/udp
volumes: volumes:
- $HOME/.grumble:/data - $HOME/.grumble:/data
```

View file

@ -37,14 +37,14 @@ func main() {
dataDir.Close() dataDir.Close()
// Set up logging // Set up logging
err = logtarget.Target.OpenFile(Args.LogPath) logtarget.Default, err = logtarget.OpenFile(Args.LogPath, os.Stderr)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", Args.LogPath, err) fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", Args.LogPath, err)
return return
} }
log.SetPrefix("[G] ") log.SetPrefix("[G] ")
log.SetFlags(log.LstdFlags | log.Lmicroseconds) log.SetFlags(log.LstdFlags | log.Lmicroseconds)
log.SetOutput(&logtarget.Target) log.SetOutput(logtarget.Default)
log.Printf("Grumble") log.Printf("Grumble")
log.Printf("Using data directory: %s", Args.DataDir) log.Printf("Using data directory: %s", Args.DataDir)

View file

@ -156,7 +156,7 @@ func NewServer(id int64) (s *Server, err error) {
s.Channels[0] = NewChannel(0, "Root") s.Channels[0] = NewChannel(0, "Root")
s.nextChanId = 1 s.nextChanId = 1
s.Logger = log.New(&logtarget.Target, fmt.Sprintf("[%v] ", s.Id), log.LstdFlags|log.Lmicroseconds) s.Logger = log.New(logtarget.Default, fmt.Sprintf("[%v] ", s.Id), log.LstdFlags|log.Lmicroseconds)
return return
} }
@ -175,8 +175,7 @@ func (server *Server) RootChannel() *Channel {
return root return root
} }
// Set password as the new SuperUser password func (server *Server) setConfigPassword(key, password string) {
func (server *Server) SetSuperUserPassword(password string) {
saltBytes := make([]byte, 24) saltBytes := make([]byte, 24)
_, err := rand.Read(saltBytes) _, err := rand.Read(saltBytes)
if err != nil { if err != nil {
@ -190,7 +189,6 @@ func (server *Server) SetSuperUserPassword(password string) {
digest := hex.EncodeToString(hasher.Sum(nil)) digest := hex.EncodeToString(hasher.Sum(nil))
// Could be racy, but shouldn't really matter... // Could be racy, but shouldn't really matter...
key := "SuperUserPassword"
val := "sha1$" + salt + "$" + digest val := "sha1$" + salt + "$" + digest
server.cfg.Set(key, val) server.cfg.Set(key, val)
@ -199,9 +197,18 @@ func (server *Server) SetSuperUserPassword(password string) {
} }
} }
// CheckSuperUserPassword checks whether password matches the set SuperUser password. // SetSuperUserPassword sets password as the new SuperUser password
func (server *Server) CheckSuperUserPassword(password string) bool { func (server *Server) SetSuperUserPassword(password string) {
parts := strings.Split(server.cfg.StringValue("SuperUserPassword"), "$") server.setConfigPassword("SuperUserPassword", password)
}
// SetServerPassword sets password as the new Server password
func (server *Server) SetServerPassword(password string) {
server.setConfigPassword("ServerPassword", password)
}
func (server *Server) checkConfigPassword(key, password string) bool {
parts := strings.Split(server.cfg.StringValue(key), "$")
if len(parts) != 3 { if len(parts) != 3 {
return false return false
} }
@ -239,6 +246,20 @@ func (server *Server) CheckSuperUserPassword(password string) bool {
return false return false
} }
// CheckSuperUserPassword checks whether password matches the set SuperUser password.
func (server *Server) CheckSuperUserPassword(password string) bool {
return server.checkConfigPassword("SuperUserPassword", password)
}
// CheckServerPassword checks whether password matches the set Server password.
func (server *Server) CheckServerPassword(password string) bool {
return server.checkConfigPassword("ServerPassword", password)
}
func (server *Server) hasServerPassword() bool {
return server.cfg.StringValue("ServerPassword") != ""
}
// Called by the server to initiate a new client connection. // Called by the server to initiate a new client connection.
func (server *Server) handleIncomingClient(conn net.Conn) (err error) { func (server *Server) handleIncomingClient(conn net.Conn) (err error) {
client := new(Client) client := new(Client)
@ -518,6 +539,13 @@ func (server *Server) handleAuthenticate(client *Client, msg *Message) {
} }
} }
if client.user == nil && server.hasServerPassword() {
if auth.Password == nil || !server.CheckServerPassword(*auth.Password) {
client.RejectAuth(mumbleproto.Reject_WrongServerPW, "Invalid server password")
return
}
}
// Setup the cryptstate for the client. // Setup the cryptstate for the client.
err = client.crypt.GenerateKey(client.CryptoMode) err = client.crypt.GenerateKey(client.CryptoMode)
if err != nil { if err != nil {
@ -1358,6 +1386,12 @@ func (server *Server) Port() int {
return port return port
} }
// ListenWebPort returns true if we should listen to the
// web port, otherwise false
func (server *Server) ListenWebPort() bool {
return !server.cfg.BoolValue("NoWebServer")
}
// WebPort returns the port the web server will listen on when it is // WebPort returns the port the web server will listen on when it is
// started. // started.
func (server *Server) WebPort() int { func (server *Server) WebPort() int {
@ -1399,6 +1433,7 @@ func (server *Server) Start() (err error) {
host := server.HostAddress() host := server.HostAddress()
port := server.Port() port := server.Port()
webport := server.WebPort() webport := server.WebPort()
shouldListenWeb := server.ListenWebPort()
// Setup our UDP listener // Setup our UDP listener
server.udpconn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP(host), Port: port}) server.udpconn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP(host), Port: port})
@ -1437,6 +1472,7 @@ func (server *Server) Start() (err error) {
} }
server.tlsl = tls.NewListener(server.tcpl, server.tlscfg) server.tlsl = tls.NewListener(server.tcpl, server.tlscfg)
if shouldListenWeb {
// Create HTTP server and WebSocket "listener" // Create HTTP server and WebSocket "listener"
webaddr := &net.TCPAddr{IP: net.ParseIP(host), Port: webport} webaddr := &net.TCPAddr{IP: net.ParseIP(host), Port: webport}
server.webtlscfg = &tls.Config{ server.webtlscfg = &tls.Config{
@ -1468,6 +1504,10 @@ func (server *Server) Start() (err error) {
}() }()
server.Printf("Started: listening on %v and %v", server.tcpl.Addr(), server.webwsl.Addr()) server.Printf("Started: listening on %v and %v", server.tcpl.Addr(), server.webwsl.Addr())
} else {
server.Printf("Started: listening on %v", server.tcpl.Addr())
}
server.running = true server.running = true
// Open a fresh freezer log // Open a fresh freezer log
@ -1490,10 +1530,17 @@ func (server *Server) Start() (err error) {
// for the servers. Each network goroutine defers a call to // for the servers. Each network goroutine defers a call to
// netwg.Done(). In the Stop() we close all the connections // netwg.Done(). In the Stop() we close all the connections
// and call netwg.Wait() to wait for the goroutines to end. // and call netwg.Wait() to wait for the goroutines to end.
server.netwg.Add(3) numWG := 2
if shouldListenWeb {
numWG++
}
server.netwg.Add(numWG)
go server.udpListenLoop() go server.udpListenLoop()
go server.acceptLoop(server.tlsl) go server.acceptLoop(server.tlsl)
if shouldListenWeb {
go server.acceptLoop(server.webwsl) go server.acceptLoop(server.webwsl)
}
// Schedule a server registration update (if needed) // Schedule a server registration update (if needed)
go func() { go func() {
@ -1523,6 +1570,7 @@ func (server *Server) Stop() (err error) {
// This does not apply to opened WebSockets, which were forcibly closed when // This does not apply to opened WebSockets, which were forcibly closed when
// all clients were disconnected. // all clients were disconnected.
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second)) ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
if server.ListenWebPort() {
err = server.webhttp.Shutdown(ctx) err = server.webhttp.Shutdown(ctx)
cancel() cancel()
if err == context.DeadlineExceeded { if err == context.DeadlineExceeded {
@ -1531,12 +1579,14 @@ func (server *Server) Stop() (err error) {
return err return err
} }
// Close the listeners err = server.webwsl.Close()
err = server.tlsl.Close()
if err != nil { if err != nil {
return err return err
} }
err = server.webwsl.Close() }
// Close the listeners
err = server.tlsl.Close()
if err != nil { if err != nil {
return err return err
} }
@ -1565,3 +1615,8 @@ func (server *Server) Stop() (err error) {
return nil return nil
} }
// Set will set a configuration value
func (server *Server) Set(key string, value string) {
server.cfg.Set(key, value)
}

View file

@ -20,7 +20,7 @@ func SignalHandler() {
signal.Notify(sigchan, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGINT) signal.Notify(sigchan, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGINT)
for sig := range sigchan { for sig := range sigchan {
if sig == syscall.SIGUSR2 { if sig == syscall.SIGUSR2 {
err := logtarget.Target.Rotate() err := logtarget.Default.Rotate()
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "unable to rotate log file: %v", err) fmt.Fprintf(os.Stderr, "unable to rotate log file: %v", err)
} }

View file

@ -6,7 +6,7 @@
package logtarget package logtarget
import ( import (
"bytes" "io"
"os" "os"
"sync" "sync"
) )
@ -15,55 +15,71 @@ import (
// LogTarget to be registered with the regular Go log package. // LogTarget to be registered with the regular Go log package.
// LogTarget multiplexes its incoming writes to multiple optional // LogTarget multiplexes its incoming writes to multiple optional
// output writers, and one main output writer (the log file). // output writers, and one main output writer (the log file).
type LogTarget struct { type LogTarget interface {
io.Writer
Rotate() error
}
// logTarget is the default implementation of a log
// target. It can write to more than one writer at the same time
// but only rotate one file
type logTarget struct {
mu sync.Mutex mu sync.Mutex
logfn string logfn string
file *os.File file *os.File
memLog *bytes.Buffer w io.Writer
ws []io.Writer
} }
var Target LogTarget // Default is the default log target for the application
// It has to be initialized before used
var Default LogTarget
// OpenWriters returns a log target that will
// log to all the given writers at the same time
func OpenWriters(ws ...io.Writer) LogTarget {
target := &logTarget{}
target.w = io.MultiWriter(ws...)
return target
}
// OpenFile creates a LogTarget pointing to a log file
// and returns it.
// This method will open the file in append-only mode.
// It also takes a variable number of writers that are
// other log targets
func OpenFile(fileName string, ws ...io.Writer) (t LogTarget, err error) {
target := &logTarget{}
target.logfn = fileName
target.file, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0650)
if err != nil {
return nil, err
}
target.ws = ws
target.w = io.MultiWriter(append(ws, target.file)...)
return target, nil
}
// Write writes a log message to all registered io.Writers // Write writes a log message to all registered io.Writers
func (target *LogTarget) Write(in []byte) (int, error) { func (target *logTarget) Write(out []byte) (int, error) {
target.mu.Lock()
defer target.mu.Unlock()
return target.Write(out)
}
// Rotate rotates the current log file, if one is opened.
// This method holds a lock while rotating the log file,
// and all log writes will be held back until the rotation
// is complete.
func (target *logTarget) Rotate() error {
target.mu.Lock() target.mu.Lock()
defer target.mu.Unlock() defer target.mu.Unlock()
if target.file == nil { if target.file == nil {
panic("no log file opened")
}
n, err := os.Stderr.Write(in)
if err != nil {
return n, err
}
n, err = target.file.Write(in)
if err != nil {
return n, err
}
return len(in), nil
}
// OpenFile opens the main log file for writing.
// This method will open the file in append-only mode.
func (target *LogTarget) OpenFile(fn string) (err error) {
target.logfn = fn
target.file, err = os.OpenFile(target.logfn, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0650)
if err != nil {
return err
}
return nil return nil
} }
// Rotate rotates the current log file.
// This method holds a lock while rotating the log file,
// and all log writes will be held back until the rotation
// is complete.
func (target *LogTarget) Rotate() error {
target.mu.Lock()
defer target.mu.Unlock()
// Close the existing log file // Close the existing log file
err := target.file.Close() err := target.file.Close()
@ -75,6 +91,7 @@ func (target *LogTarget) Rotate() error {
if err != nil { if err != nil {
return err return err
} }
target.w = io.MultiWriter(append(target.ws, target.file)...)
return nil return nil
} }