mirror of
https://github.com/strukturag/nextcloud-spreed-signaling
synced 2024-05-04 23:03:09 +02:00
proxy: Prepare for different token storages.
This commit is contained in:
parent
2d73b97882
commit
85d6726d59
|
@ -12,6 +12,13 @@
|
|||
# servers to determine the closest proxy for publishers.
|
||||
#country = DE
|
||||
|
||||
# Type of token configuration for signaling servers allowed to connect, see
|
||||
# below for details. Defaults to "static".
|
||||
#
|
||||
# Possible values:
|
||||
# - static: A mapping of token id -> public key is configured below.
|
||||
token_type = static
|
||||
|
||||
[sessions]
|
||||
# Secret value used to generate checksums of sessions. This should be a random
|
||||
# string of 32 or 64 bytes.
|
||||
|
@ -30,7 +37,8 @@ blockkey = -encryption-key-
|
|||
#url = nats://localhost:4222
|
||||
|
||||
[tokens]
|
||||
# Mapping of <tokenid> = <publickey> of signaling servers allowed to connect.
|
||||
# For token_type "static": Mapping of <tokenid> = <publickey> of signaling
|
||||
# servers allowed to connect.
|
||||
#server1 = pubkey1.pem
|
||||
#server2 = pubkey2.pem
|
||||
|
||||
|
|
|
@ -22,10 +22,8 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
|
@ -33,7 +31,6 @@ import (
|
|||
"os"
|
||||
"os/signal"
|
||||
runtimepprof "runtime/pprof"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
@ -98,7 +95,7 @@ type ProxyServer struct {
|
|||
|
||||
upgrader websocket.Upgrader
|
||||
|
||||
tokenKeys atomic.Value
|
||||
tokens ProxyTokens
|
||||
statsAllowedIps map[string]bool
|
||||
|
||||
sid uint64
|
||||
|
@ -132,32 +129,22 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile, na
|
|||
return nil, fmt.Errorf("The sessions block key must be 16, 24 or 32 bytes but is %d bytes", len(blockKey))
|
||||
}
|
||||
|
||||
tokenKeys := make(map[string]*rsa.PublicKey)
|
||||
options, _ := config.GetOptions("tokens")
|
||||
for _, id := range options {
|
||||
filename, _ := config.GetString("tokens", id)
|
||||
if filename == "" {
|
||||
return nil, fmt.Errorf("No filename given for token %s", id)
|
||||
}
|
||||
|
||||
keyData, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not read public key from %s: %s", filename, err)
|
||||
}
|
||||
key, err := jwt.ParseRSAPublicKeyFromPEM(keyData)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Could not parse public key from %s: %s", filename, err)
|
||||
}
|
||||
|
||||
tokenKeys[id] = key
|
||||
var tokens ProxyTokens
|
||||
var err error
|
||||
tokenType, _ := config.GetString("app", "token_type")
|
||||
if tokenType == "" {
|
||||
tokenType = TokenTypeDefault
|
||||
}
|
||||
|
||||
var keyIds []string
|
||||
for k, _ := range tokenKeys {
|
||||
keyIds = append(keyIds, k)
|
||||
switch tokenType {
|
||||
case TokenTypeStatic:
|
||||
tokens, err = NewProxyTokensStatic(config)
|
||||
default:
|
||||
return nil, fmt.Errorf("Unsupported token type configured: %s", tokenType)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sort.Strings(keyIds)
|
||||
log.Printf("Enabled token keys: %v", keyIds)
|
||||
|
||||
statsAllowed, _ := config.GetString("stats", "allowed_ips")
|
||||
var statsAllowedIps map[string]bool
|
||||
|
@ -200,6 +187,7 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile, na
|
|||
WriteBufferSize: websocketWriteBufferSize,
|
||||
},
|
||||
|
||||
tokens: tokens,
|
||||
statsAllowedIps: statsAllowedIps,
|
||||
|
||||
cookie: securecookie.New([]byte(hashKey), blockBytes).MaxAge(0),
|
||||
|
@ -209,7 +197,6 @@ func NewProxyServer(r *mux.Router, version string, config *goconf.ConfigFile, na
|
|||
clientIds: make(map[string]string),
|
||||
}
|
||||
|
||||
result.setTokenKeys(tokenKeys)
|
||||
result.upgrader.CheckOrigin = result.checkOrigin
|
||||
|
||||
if debug, _ := config.GetBool("app", "debug"); debug {
|
||||
|
@ -235,14 +222,6 @@ func (s *ProxyServer) checkOrigin(r *http.Request) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
func (s *ProxyServer) setTokenKeys(keys map[string]*rsa.PublicKey) {
|
||||
s.tokenKeys.Store(keys)
|
||||
}
|
||||
|
||||
func (s *ProxyServer) getTokenKeys() map[string]*rsa.PublicKey {
|
||||
return s.tokenKeys.Load().(map[string]*rsa.PublicKey)
|
||||
}
|
||||
|
||||
func (s *ProxyServer) Start(config *goconf.ConfigFile) error {
|
||||
s.url, _ = config.GetString("mcu", "url")
|
||||
if s.url == "" {
|
||||
|
@ -413,40 +392,7 @@ func (s *ProxyServer) ScheduleShutdown() {
|
|||
}
|
||||
|
||||
func (s *ProxyServer) Reload(config *goconf.ConfigFile) {
|
||||
tokenKeys := make(map[string]*rsa.PublicKey)
|
||||
options, _ := config.GetOptions("tokens")
|
||||
for _, id := range options {
|
||||
filename, _ := config.GetString("tokens", id)
|
||||
if filename == "" {
|
||||
log.Printf("No filename given for token %s, ignoring", id)
|
||||
continue
|
||||
}
|
||||
|
||||
keyData, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Printf("Could not read public key from %s, ignoring: %s", filename, err)
|
||||
continue
|
||||
}
|
||||
key, err := jwt.ParseRSAPublicKeyFromPEM(keyData)
|
||||
if err != nil {
|
||||
log.Printf("Could not parse public key from %s, ignoring: %s", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
tokenKeys[id] = key
|
||||
}
|
||||
|
||||
if len(tokenKeys) == 0 {
|
||||
log.Printf("No token keys loaded")
|
||||
} else {
|
||||
var keyIds []string
|
||||
for k, _ := range tokenKeys {
|
||||
keyIds = append(keyIds, k)
|
||||
}
|
||||
sort.Strings(keyIds)
|
||||
log.Printf("Enabled token keys: %v", keyIds)
|
||||
}
|
||||
s.setTokenKeys(tokenKeys)
|
||||
s.tokens.Reload(config)
|
||||
}
|
||||
|
||||
func (s *ProxyServer) setCommonHeaders(f func(http.ResponseWriter, *http.Request)) func(http.ResponseWriter, *http.Request) {
|
||||
|
@ -878,13 +824,17 @@ func (s *ProxyServer) NewSession(hello *signaling.HelloProxyClientMessage) (*Pro
|
|||
return nil, fmt.Errorf("Unsupported claims type")
|
||||
}
|
||||
|
||||
tokenKeys := s.getTokenKeys()
|
||||
publicKey := tokenKeys[claims.Issuer]
|
||||
if publicKey == nil {
|
||||
tokenKey, err := s.tokens.Get(claims.Issuer)
|
||||
if err != nil {
|
||||
log.Printf("Could not get token for %s: %s", claims.Issuer, err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if tokenKey == nil || tokenKey.key == nil {
|
||||
log.Printf("Issuer %s is not supported", claims.Issuer)
|
||||
return nil, fmt.Errorf("No key found for issuer")
|
||||
}
|
||||
return publicKey, nil
|
||||
return tokenKey.key, nil
|
||||
})
|
||||
if err != nil {
|
||||
return nil, TokenAuthFailed
|
||||
|
|
45
src/proxy/proxy_tokens.go
Normal file
45
src/proxy/proxy_tokens.go
Normal file
|
@ -0,0 +1,45 @@
|
|||
/**
|
||||
* Standalone signaling server for the Nextcloud Spreed app.
|
||||
* Copyright (C) 2020 struktur AG
|
||||
*
|
||||
* @author Joachim Bauch <bauch@struktur.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/rsa"
|
||||
|
||||
"github.com/dlintw/goconf"
|
||||
)
|
||||
|
||||
const (
|
||||
TokenTypeStatic = "static"
|
||||
|
||||
TokenTypeDefault = TokenTypeStatic
|
||||
)
|
||||
|
||||
type ProxyToken struct {
|
||||
id string
|
||||
key *rsa.PublicKey
|
||||
}
|
||||
|
||||
type ProxyTokens interface {
|
||||
Get(id string) (*ProxyToken, error)
|
||||
|
||||
Reload(config *goconf.ConfigFile)
|
||||
}
|
118
src/proxy/proxy_tokens_static.go
Normal file
118
src/proxy/proxy_tokens_static.go
Normal file
|
@ -0,0 +1,118 @@
|
|||
/**
|
||||
* Standalone signaling server for the Nextcloud Spreed app.
|
||||
* Copyright (C) 2020 struktur AG
|
||||
*
|
||||
* @author Joachim Bauch <bauch@struktur.de>
|
||||
*
|
||||
* @license GNU AGPL version 3 or any later version
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/dlintw/goconf"
|
||||
|
||||
"gopkg.in/dgrijalva/jwt-go.v3"
|
||||
)
|
||||
|
||||
type tokensStatic struct {
|
||||
tokenKeys atomic.Value
|
||||
}
|
||||
|
||||
func NewProxyTokensStatic(config *goconf.ConfigFile) (ProxyTokens, error) {
|
||||
result := &tokensStatic{}
|
||||
if err := result.load(config, false); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func (t *tokensStatic) setTokenKeys(keys map[string]*ProxyToken) {
|
||||
t.tokenKeys.Store(keys)
|
||||
}
|
||||
|
||||
func (t *tokensStatic) getTokenKeys() map[string]*ProxyToken {
|
||||
return t.tokenKeys.Load().(map[string]*ProxyToken)
|
||||
}
|
||||
|
||||
func (t *tokensStatic) Get(id string) (*ProxyToken, error) {
|
||||
tokenKeys := t.getTokenKeys()
|
||||
token := tokenKeys[id]
|
||||
return token, nil
|
||||
}
|
||||
|
||||
func (t *tokensStatic) load(config *goconf.ConfigFile, ignoreErrors bool) error {
|
||||
tokenKeys := make(map[string]*ProxyToken)
|
||||
options, _ := config.GetOptions("tokens")
|
||||
for _, id := range options {
|
||||
filename, _ := config.GetString("tokens", id)
|
||||
if filename == "" {
|
||||
if !ignoreErrors {
|
||||
return fmt.Errorf("No filename given for token %s", id)
|
||||
}
|
||||
|
||||
log.Printf("No filename given for token %s, ignoring", id)
|
||||
continue
|
||||
}
|
||||
|
||||
keyData, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
if !ignoreErrors {
|
||||
return fmt.Errorf("Could not read public key from %s: %s", filename, err)
|
||||
}
|
||||
|
||||
log.Printf("Could not read public key from %s, ignoring: %s", filename, err)
|
||||
continue
|
||||
}
|
||||
key, err := jwt.ParseRSAPublicKeyFromPEM(keyData)
|
||||
if err != nil {
|
||||
if !ignoreErrors {
|
||||
return fmt.Errorf("Could not parse public key from %s: %s", filename, err)
|
||||
}
|
||||
|
||||
log.Printf("Could not parse public key from %s, ignoring: %s", filename, err)
|
||||
continue
|
||||
}
|
||||
|
||||
tokenKeys[id] = &ProxyToken{
|
||||
id: id,
|
||||
key: key,
|
||||
}
|
||||
}
|
||||
|
||||
if len(tokenKeys) == 0 {
|
||||
log.Printf("No token keys loaded")
|
||||
} else {
|
||||
var keyIds []string
|
||||
for k, _ := range tokenKeys {
|
||||
keyIds = append(keyIds, k)
|
||||
}
|
||||
sort.Strings(keyIds)
|
||||
log.Printf("Enabled token keys: %v", keyIds)
|
||||
}
|
||||
t.setTokenKeys(tokenKeys)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tokensStatic) Reload(config *goconf.ConfigFile) {
|
||||
t.load(config, true)
|
||||
}
|
Loading…
Reference in a new issue