Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mumble-voip/grumble.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDerrick <actown@users.noreply.github.com>2020-04-12 05:14:51 +0300
committerDerrick <actown@users.noreply.github.com>2020-04-12 05:14:51 +0300
commit911438042323cb18f2df8467207b20452ac13e76 (patch)
tree620e05e3ea1fa2edf0c3fcfefb99dc39feb34269
parent5038fe0c5714ecca84f86e33f1f04ea793cb6404 (diff)
parentdf983754639dbe9b689f47584bffabae9f438137 (diff)
Merge branch 'master' into go_mod
-rw-r--r--Dockerfile10
-rw-r--r--Dockerfile.arm32v610
-rw-r--r--README.md64
-rw-r--r--cmd/grumble/grumble.go4
-rw-r--r--cmd/grumble/server.go151
-rw-r--r--cmd/grumble/signal_unix.go2
-rw-r--r--pkg/logtarget/logtarget.go85
7 files changed, 206 insertions, 120 deletions
diff --git a/Dockerfile b/Dockerfile
index f30ac8e..d976431 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,11 +1,12 @@
-FROM golang:1.9-alpine as builder
+FROM golang:1.14-alpine as builder
COPY . /go/src/mumble.info/grumble
WORKDIR /go/src/mumble.info/grumble
-RUN apk add --no-cache git \
- && go get -v -t ./... \
+RUN apk add --no-cache git build-base
+
+RUN go get -v -t ./... \
&& go build mumble.info/grumble/cmd/grumble \
&& go test -v ./...
@@ -21,4 +22,7 @@ WORKDIR /data
VOLUME /data
+EXPOSE 64738/tcp
+EXPOSE 64738/udp
+
ENTRYPOINT [ "/usr/bin/grumble", "--datadir", "/data", "--log", "/data/grumble.log" ]
diff --git a/Dockerfile.arm32v6 b/Dockerfile.arm32v6
index 4638f99..a32951a 100644
--- a/Dockerfile.arm32v6
+++ b/Dockerfile.arm32v6
@@ -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
WORKDIR /go/src/mumble.info/grumble
-RUN apk add --no-cache git \
- && go get -v -t ./... \
+RUN apk add --no-cache git build-base
+
+RUN go get -v -t ./... \
&& go build mumble.info/grumble/cmd/grumble \
&& go test -v ./...
@@ -21,4 +22,7 @@ WORKDIR /data
VOLUME /data
+EXPOSE 64738/tcp
+EXPOSE 64738/udp
+
ENTRYPOINT [ "/usr/bin/grumble", "--datadir", "/data", "--log", "/data/grumble.log" ]
diff --git a/README.md b/README.md
index cd93c7a..d9b904f 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
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):
@@ -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.
Set up a GOPATH. On Unix, do something like this
-
- $ export GOPATH=$HOME/gocode
- $ mkdir -p $GOPATH
+```shell script
+$ export GOPATH=$HOME/gocode
+$ mkdir -p $GOPATH
+```
and on Windows, do something like this (for cmd.exe):
-
- c:\> set GOPATH=%USERPROFILE%\gocode
- c:\> mkdir %GOPATH%
+```shell script
+c:\> set GOPATH=%USERPROFILE%\gocode
+c:\> mkdir %GOPATH%
+```
Then, it's time to install Grumble. The following line should do the trick:
-
- $ go get mumble.info/grumble/cmd/grumble
+```shell script
+$ go get mumble.info/grumble/cmd/grumble
+```
And that should be it. Grumble has been built, and is available in $GOPATH/bin as 'grumble'.
@@ -61,29 +64,32 @@ Docker
## Getting the image
### Building
-
- $ git clone https://github.com/mumble-voip/grumble.git
- $ cd grumble/
- $ docker build -t mumble-voip/grumble .
+```shell script
+$ git clone https://github.com/mumble-voip/grumble.git
+$ cd grumble/
+$ docker build -t mumble-voip/grumble .
+```
## Running
### Command line
-
- $ docker run \
- -v $HOME/.grumble:/data \
- -p 64738:64738 \
- -p 64738:64738/udp \
- mumble-voip/grumble
+```shell script
+$ docker run \
+ -v $HOME/.grumble:/data \
+ -p 64738:64738 \
+ -p 64738:64738/udp \
+ mumble-voip/grumble
+```
### Compose
-
- version: '3'
- services:
- grumble:
- image: mumble-voip/grumble
- ports:
- - 64738:64738
- - 64738:64738/udp
- volumes:
- - $HOME/.grumble:/data
+```yaml
+version: '3'
+services:
+ grumble:
+ image: mumble-voip/grumble
+ ports:
+ - 64738:64738
+ - 64738:64738/udp
+ volumes:
+ - $HOME/.grumble:/data
+```
diff --git a/cmd/grumble/grumble.go b/cmd/grumble/grumble.go
index 56eaa9a..139eadc 100644
--- a/cmd/grumble/grumble.go
+++ b/cmd/grumble/grumble.go
@@ -37,14 +37,14 @@ func main() {
dataDir.Close()
// Set up logging
- err = logtarget.Target.OpenFile(Args.LogPath)
+ logtarget.Default, err = logtarget.OpenFile(Args.LogPath, os.Stderr)
if err != nil {
fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", Args.LogPath, err)
return
}
log.SetPrefix("[G] ")
log.SetFlags(log.LstdFlags | log.Lmicroseconds)
- log.SetOutput(&logtarget.Target)
+ log.SetOutput(logtarget.Default)
log.Printf("Grumble")
log.Printf("Using data directory: %s", Args.DataDir)
diff --git a/cmd/grumble/server.go b/cmd/grumble/server.go
index 64b4dd8..d46552d 100644
--- a/cmd/grumble/server.go
+++ b/cmd/grumble/server.go
@@ -156,7 +156,7 @@ func NewServer(id int64) (s *Server, err error) {
s.Channels[0] = NewChannel(0, "Root")
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
}
@@ -175,8 +175,7 @@ func (server *Server) RootChannel() *Channel {
return root
}
-// Set password as the new SuperUser password
-func (server *Server) SetSuperUserPassword(password string) {
+func (server *Server) setConfigPassword(key, password string) {
saltBytes := make([]byte, 24)
_, err := rand.Read(saltBytes)
if err != nil {
@@ -190,7 +189,6 @@ func (server *Server) SetSuperUserPassword(password string) {
digest := hex.EncodeToString(hasher.Sum(nil))
// Could be racy, but shouldn't really matter...
- key := "SuperUserPassword"
val := "sha1$" + salt + "$" + digest
server.cfg.Set(key, val)
@@ -199,9 +197,18 @@ func (server *Server) SetSuperUserPassword(password string) {
}
}
-// CheckSuperUserPassword checks whether password matches the set SuperUser password.
-func (server *Server) CheckSuperUserPassword(password string) bool {
- parts := strings.Split(server.cfg.StringValue("SuperUserPassword"), "$")
+// SetSuperUserPassword sets password as the new SuperUser password
+func (server *Server) SetSuperUserPassword(password string) {
+ 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 {
return false
}
@@ -239,6 +246,20 @@ func (server *Server) CheckSuperUserPassword(password string) bool {
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.
func (server *Server) handleIncomingClient(conn net.Conn) (err error) {
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.
err = client.crypt.GenerateKey(client.CryptoMode)
if err != nil {
@@ -1358,6 +1386,12 @@ func (server *Server) Port() int {
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
// started.
func (server *Server) WebPort() int {
@@ -1399,6 +1433,7 @@ func (server *Server) Start() (err error) {
host := server.HostAddress()
port := server.Port()
webport := server.WebPort()
+ shouldListenWeb := server.ListenWebPort()
// Setup our UDP listener
server.udpconn, err = net.ListenUDP("udp", &net.UDPAddr{IP: net.ParseIP(host), Port: port})
@@ -1437,37 +1472,42 @@ func (server *Server) Start() (err error) {
}
server.tlsl = tls.NewListener(server.tcpl, server.tlscfg)
- // Create HTTP server and WebSocket "listener"
- webaddr := &net.TCPAddr{IP: net.ParseIP(host), Port: webport}
- server.webtlscfg = &tls.Config{
- Certificates: []tls.Certificate{cert},
- ClientAuth: tls.NoClientCert,
- NextProtos: []string{"http/1.1"},
- }
- server.webwsl = web.NewListener(webaddr, server.Logger)
- mux := http.NewServeMux()
- mux.Handle("/", server.webwsl)
- server.webhttp = &http.Server{
- Addr: webaddr.String(),
- Handler: mux,
- TLSConfig: server.webtlscfg,
- ErrorLog: server.Logger,
-
- // Set sensible timeouts, in case no reverse proxy is in front of Grumble.
- // Non-conforming (or malicious) clients may otherwise block indefinitely and cause
- // file descriptors (or handles, depending on your OS) to leak and/or be exhausted
- ReadTimeout: 5 * time.Second,
- WriteTimeout: 10 * time.Second,
- IdleTimeout: 2 * time.Minute,
+ if shouldListenWeb {
+ // Create HTTP server and WebSocket "listener"
+ webaddr := &net.TCPAddr{IP: net.ParseIP(host), Port: webport}
+ server.webtlscfg = &tls.Config{
+ Certificates: []tls.Certificate{cert},
+ ClientAuth: tls.NoClientCert,
+ NextProtos: []string{"http/1.1"},
+ }
+ server.webwsl = web.NewListener(webaddr, server.Logger)
+ mux := http.NewServeMux()
+ mux.Handle("/", server.webwsl)
+ server.webhttp = &http.Server{
+ Addr: webaddr.String(),
+ Handler: mux,
+ TLSConfig: server.webtlscfg,
+ ErrorLog: server.Logger,
+
+ // Set sensible timeouts, in case no reverse proxy is in front of Grumble.
+ // Non-conforming (or malicious) clients may otherwise block indefinitely and cause
+ // file descriptors (or handles, depending on your OS) to leak and/or be exhausted
+ ReadTimeout: 5 * time.Second,
+ WriteTimeout: 10 * time.Second,
+ IdleTimeout: 2 * time.Minute,
+ }
+ go func() {
+ err := server.webhttp.ListenAndServeTLS("", "")
+ if err != http.ErrServerClosed {
+ server.Fatalf("Fatal HTTP server error: %v", err)
+ }
+ }()
+
+ server.Printf("Started: listening on %v and %v", server.tcpl.Addr(), server.webwsl.Addr())
+ } else {
+ server.Printf("Started: listening on %v", server.tcpl.Addr())
}
- go func() {
- err := server.webhttp.ListenAndServeTLS("", "")
- if err != http.ErrServerClosed {
- server.Fatalf("Fatal HTTP server error: %v", err)
- }
- }()
- server.Printf("Started: listening on %v and %v", server.tcpl.Addr(), server.webwsl.Addr())
server.running = true
// 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
// netwg.Done(). In the Stop() we close all the connections
// 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.acceptLoop(server.tlsl)
- go server.acceptLoop(server.webwsl)
+ if shouldListenWeb {
+ go server.acceptLoop(server.webwsl)
+ }
// Schedule a server registration update (if needed)
go func() {
@@ -1523,12 +1570,19 @@ func (server *Server) Stop() (err error) {
// This does not apply to opened WebSockets, which were forcibly closed when
// all clients were disconnected.
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(15*time.Second))
- err = server.webhttp.Shutdown(ctx)
- cancel()
- if err == context.DeadlineExceeded {
- server.Println("Forcibly shutdown HTTP server while stopping")
- } else if err != nil {
- return err
+ if server.ListenWebPort() {
+ err = server.webhttp.Shutdown(ctx)
+ cancel()
+ if err == context.DeadlineExceeded {
+ server.Println("Forcibly shutdown HTTP server while stopping")
+ } else if err != nil {
+ return err
+ }
+
+ err = server.webwsl.Close()
+ if err != nil {
+ return err
+ }
}
// Close the listeners
@@ -1536,10 +1590,6 @@ func (server *Server) Stop() (err error) {
if err != nil {
return err
}
- err = server.webwsl.Close()
- if err != nil {
- return err
- }
// Close the UDP connection
err = server.udpconn.Close()
@@ -1565,3 +1615,8 @@ func (server *Server) Stop() (err error) {
return nil
}
+
+// Set will set a configuration value
+func (server *Server) Set(key string, value string) {
+ server.cfg.Set(key, value)
+}
diff --git a/cmd/grumble/signal_unix.go b/cmd/grumble/signal_unix.go
index 7127f24..9066b49 100644
--- a/cmd/grumble/signal_unix.go
+++ b/cmd/grumble/signal_unix.go
@@ -20,7 +20,7 @@ func SignalHandler() {
signal.Notify(sigchan, syscall.SIGUSR2, syscall.SIGTERM, syscall.SIGINT)
for sig := range sigchan {
if sig == syscall.SIGUSR2 {
- err := logtarget.Target.Rotate()
+ err := logtarget.Default.Rotate()
if err != nil {
fmt.Fprintf(os.Stderr, "unable to rotate log file: %v", err)
}
diff --git a/pkg/logtarget/logtarget.go b/pkg/logtarget/logtarget.go
index 46a2eb2..1b69a3b 100644
--- a/pkg/logtarget/logtarget.go
+++ b/pkg/logtarget/logtarget.go
@@ -6,7 +6,7 @@
package logtarget
import (
- "bytes"
+ "io"
"os"
"sync"
)
@@ -15,56 +15,72 @@ import (
// LogTarget to be registered with the regular Go log package.
// LogTarget multiplexes its incoming writes to multiple optional
// output writers, and one main output writer (the log file).
-type LogTarget struct {
- mu sync.Mutex
- logfn string
- file *os.File
- memLog *bytes.Buffer
-}
-
-var Target LogTarget
+type LogTarget interface {
+ io.Writer
-// Write writes a log message to all registered io.Writers
-func (target *LogTarget) Write(in []byte) (int, error) {
- target.mu.Lock()
- defer target.mu.Unlock()
-
- if target.file == nil {
- panic("no log file opened")
- }
+ Rotate() error
+}
- n, err := os.Stderr.Write(in)
- if err != nil {
- return n, err
- }
+// 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
+ logfn string
+ file *os.File
+ w io.Writer
+ ws []io.Writer
+}
- n, err = target.file.Write(in)
- if err != nil {
- return n, err
- }
+// Default is the default log target for the application
+// It has to be initialized before used
+var Default LogTarget
- return len(in), nil
+// 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 opens the main log file for writing.
+// OpenFile creates a LogTarget pointing to a log file
+// and returns it.
// 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)
+// 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 err
+ return nil, err
}
- return nil
+ target.ws = ws
+ target.w = io.MultiWriter(append(ws, target.file)...)
+ return target, nil
}
-// Rotate rotates the current log file.
+// Write writes a log message to all registered io.Writers
+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 {
+func (target *logTarget) Rotate() error {
target.mu.Lock()
defer target.mu.Unlock()
+ if target.file == nil {
+ return nil
+ }
+
// Close the existing log file
err := target.file.Close()
if err != nil {
@@ -75,6 +91,7 @@ func (target *LogTarget) Rotate() error {
if err != nil {
return err
}
+ target.w = io.MultiWriter(append(target.ws, target.file)...)
return nil
}