Browse Source

TLS client side auth now even works.

pull/28/head
Simon Eisenmann 11 years ago
parent
commit
eb13945be8
  1. 36
      server.conf.in
  2. 11
      src/app/spreed-speakfreely-server/connection.go
  3. 24
      src/app/spreed-speakfreely-server/hub.go
  4. 62
      src/app/spreed-speakfreely-server/users.go
  5. 8
      src/app/spreed-speakfreely-server/ws.go

36
server.conf.in

@ -108,14 +108,44 @@ enabled = false @@ -108,14 +108,44 @@ enabled = false
; validation. This usually only makes sense with a front end HTTPS proxy which
; does the authentication and injects the user id as HTTP header for sessions
; REST requests. In mode httpheader, allowRegistration should be false.
; certificate:
; The userid is provided as CommonName with a certificate provided with TLS
; client authentication. When you use this with a front end proxy for TLS
; termination, that proxy has to validate the certificate and inject certain
; headers into the proxy connection. Furthermode in certificate mode the
; server can act as a CA to sign incoming user requests with a private key
; when allowRegistration is true.
;mode = sharedsecret
; The shared secred for HMAC validation in "sharedsecret" mode. Best use 32 or
; 64 bytes of random data.
;sharedsecret_secret = some-secret-do-not-keep
; The HTTP header name where to find the userid in "httpheader" mode.
;httpheader_header = x-userid
; The server can create new userids if enabled. Set allowRegistration to true to
; enable userid creation/registration. Users are created to match the settings
; Full path to PEM encoded private key to use for user creation in "certificate"
; mode. Keep this commented if you do not want the server to sign certificate
; requests.
;certificate_key = userskey.key
; Full path to PEM encoded certificate to use for user validation in
; "certificate" mode. When allowRegistration is true and certificate_key is also
; set then the server will act as a CA and sign incoming user registrations and
; return certificates to users as registration.
;certificate_certificate = usersca.crt
; The HTTP header name where to find if the TLS lient authentication was
; successfull. The value of this header is matched to
; certificate_verifiedHeaderValue and only if there is a match, the proxy
; handled TLS client authentication is accepted as success.
;certificate_verifiedHeader = x-verified
;certificate_verifiedHeaderValue = SUCCESS
; The HTTP header name where to find the userid extracted by a proxy after
; TLS client authentication. If not set certificate_certificateHeader will be
; used to retrieve the userid from the CommonName field of the certificate.
;certificate_useridHeader = x-userid
; The HTTP header name where to find the PEM encoded certificate authenticated
; by a front end proxy. This configuration this is optional if you have set a
; certificate_useridHeader value and the front end TLS proxy does inject the
; userid into that header.
;certificate_certificateHeader = x-certificate
; If enabled the server can create new userids. Set allowRegistration to true to
; enable userid creation/registration. Users are created according the settings
; of the currently configured mode (see above).
;allowRegistration = false

11
src/app/spreed-speakfreely-server/connection.go

@ -27,6 +27,7 @@ import ( @@ -27,6 +27,7 @@ import (
"github.com/gorilla/websocket"
"io"
"log"
"net/http"
"sync"
"time"
)
@ -57,6 +58,7 @@ type Connection struct { @@ -57,6 +58,7 @@ type Connection struct {
// References.
h *Hub
ws *websocket.Conn
request *http.Request
// Data handling.
condition *sync.Cond
@ -72,15 +74,14 @@ type Connection struct { @@ -72,15 +74,14 @@ type Connection struct {
IsRegistered bool
Hello bool
Version string
RemoteAddr string
}
func NewConnection(h *Hub, ws *websocket.Conn, remoteAddr string) *Connection {
func NewConnection(h *Hub, ws *websocket.Conn, request *http.Request) *Connection {
c := &Connection{
h: h,
ws: ws,
RemoteAddr: remoteAddr,
request: request,
}
c.condition = sync.NewCond(&c.mutex)
@ -112,7 +113,7 @@ func (c *Connection) close() { @@ -112,7 +113,7 @@ func (c *Connection) close() {
func (c *Connection) register() error {
s := c.h.CreateSession(nil)
s := c.h.CreateSession(c.request, nil)
c.h.registerHandler(c, s)
return nil
}
@ -120,7 +121,7 @@ func (c *Connection) register() error { @@ -120,7 +121,7 @@ func (c *Connection) register() error {
func (c *Connection) reregister(token string) error {
if st, err := c.h.DecodeSessionToken(token); err == nil {
s := c.h.CreateSession(st)
s := c.h.CreateSession(c.request, st)
c.h.registerHandler(c, s)
} else {
log.Println("Error while decoding session token", err)

24
src/app/spreed-speakfreely-server/hub.go

@ -32,6 +32,7 @@ import ( @@ -32,6 +32,7 @@ import (
"fmt"
"github.com/gorilla/securecookie"
"log"
"net/http"
"strings"
"sync"
"sync/atomic"
@ -81,6 +82,7 @@ type Hub struct { @@ -81,6 +82,7 @@ type Hub struct {
buddyImages ImageCache
realm string
tokenName string
useridRetriever func(*http.Request) (string, error)
}
func NewHub(version string, config *Config, sessionSecret, turnSecret, realm string) *Hub {
@ -164,22 +166,30 @@ func (h *Hub) CreateTurnData(id string) *DataTurn { @@ -164,22 +166,30 @@ func (h *Hub) CreateTurnData(id string) *DataTurn {
}
func (h *Hub) CreateSession(st *SessionToken) *Session {
func (h *Hub) CreateSession(request *http.Request, st *SessionToken) *Session {
// NOTE(longsleep): Is it required to make this a secure cookie,
// random data in itself should be sufficent if we do not validate
// session ids somewhere?
var session *Session
var userid string
usersEnabled := h.config.UsersEnabled
if usersEnabled && h.useridRetriever != nil {
userid, _ = h.useridRetriever(request)
}
if st == nil {
sid := NewRandomString(32)
id, _ := h.tickets.Encode("id", sid)
session = NewSession(id, sid, "")
log.Println("Created new session id", len(id), id, sid)
session = NewSession(id, sid, userid)
log.Println("Created new session id", len(id), id, sid, userid)
} else {
userid := st.Userid
if !h.config.UsersEnabled {
if userid == "" {
userid = st.Userid
}
if !usersEnabled {
userid = ""
}
session = NewSession(st.Id, st.Sid, userid)
@ -307,14 +317,14 @@ func (h *Hub) registerHandler(c *Connection, s *Session) { @@ -307,14 +317,14 @@ func (h *Hub) registerHandler(c *Connection, s *Session) {
if ec, ok := h.connectionTable[c.Id]; ok {
ec.IsRegistered = false
ec.close()
//log.Printf("Register (%d) from %s: %s (existing)\n", c.Idx, c.RemoteAddr, c.Id)
//log.Printf("Register (%d) from %s: %s (existing)\n", c.Idx, c.Id)
}
h.connectionTable[c.Id] = c
h.sessionTable[c.Id] = s
//fmt.Println("registered", c.Id)
h.mutex.Unlock()
//log.Printf("Register (%d) from %s: %s\n", c.Idx, c.RemoteAddr, c.Id)
//log.Printf("Register (%d) from %s: %s\n", c.Idx, c.Id)
h.server.OnRegister(c)
}

62
src/app/spreed-speakfreely-server/users.go

@ -48,6 +48,7 @@ var ( @@ -48,6 +48,7 @@ var (
)
type UsersHandler interface {
Get(request *http.Request) (string, error)
Validate(snr *SessionNonceRequest, request *http.Request) (string, error)
Create(snr *UserNonce, request *http.Request) (*UserNonce, error)
}
@ -64,6 +65,10 @@ func (uh *UsersSharedsecretHandler) createHMAC(useridCombo string) string { @@ -64,6 +65,10 @@ func (uh *UsersSharedsecretHandler) createHMAC(useridCombo string) string {
}
func (uh *UsersSharedsecretHandler) Get(request *http.Request) (userid string, err error) {
return
}
func (uh *UsersSharedsecretHandler) Validate(snr *SessionNonceRequest, request *http.Request) (string, error) {
// Parse UseridCombo.
@ -105,20 +110,20 @@ type UsersHTTPHeaderHandler struct { @@ -105,20 +110,20 @@ type UsersHTTPHeaderHandler struct {
headerName string
}
func (uh *UsersHTTPHeaderHandler) Validate(snr *SessionNonceRequest, request *http.Request) (string, error) {
userid := request.Header.Get(uh.headerName)
func (uh *UsersHTTPHeaderHandler) Get(request *http.Request) (userid string, err error) {
userid = request.Header.Get(uh.headerName)
if userid == "" {
return "", errors.New("no userid provided")
err = errors.New("no userid provided")
}
return userid, nil
return
}
func (uh *UsersHTTPHeaderHandler) Validate(snr *SessionNonceRequest, request *http.Request) (string, error) {
return uh.Get(request)
}
func (uh *UsersHTTPHeaderHandler) Create(un *UserNonce, request *http.Request) (*UserNonce, error) {
return nil, errors.New("create is not possible in httpheader mode")
}
type UsersCertificateHandler struct {
@ -151,8 +156,25 @@ func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certif @@ -151,8 +156,25 @@ func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certif
}
func (uh *UsersCertificateHandler) Get(request *http.Request) (userid string, err error) {
if len(request.TLS.VerifiedChains) == 0 {
return
}
chain := request.TLS.VerifiedChains[0]
if len(chain) == 0 {
return
}
cert := chain[0]
userid = cert.Subject.CommonName
log.Printf("Client certificate found for user: %s\n", userid)
return
}
func (uh *UsersCertificateHandler) Validate(snr *SessionNonceRequest, request *http.Request) (string, error) {
return "", errors.New("certificate validation not implemented")
return uh.Get(request)
}
func (uh *UsersCertificateHandler) Create(un *UserNonce, request *http.Request) (*UserNonce, error) {
@ -240,6 +262,8 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users { @@ -240,6 +262,8 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
secret, _ := runtime.GetString("users", "sharedsecret_secret")
if secret != "" {
handler = &UsersSharedsecretHandler{secret: []byte(secret)}
} else {
log.Println("Cannot enable sharedsecret users handler: No secret.")
}
case "httpheader":
headerName, _ := runtime.GetString("users", "httpheader_header")
@ -249,6 +273,7 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users { @@ -249,6 +273,7 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
handler = &UsersHTTPHeaderHandler{headerName: headerName}
case "certificate":
uh := &UsersCertificateHandler{}
// TODO(longsleep): Add validFor to configuration.
uh.validFor = 24 * time.Hour * 365
keyFn, _ := runtime.GetString("users", "certificate_key")
certificateFn, _ := runtime.GetString("users", "certificate_certificate")
@ -264,24 +289,35 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users { @@ -264,24 +289,35 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
if certificates, err := x509.ParseCertificates(certificate.Certificate[0]); err == nil {
uh.certificate = certificates[0]
log.Printf("Users certificate loaded from %s\n", certificateFn)
handler = uh
} else {
log.Printf("Failed to parse users certificate: %s\n", err)
}
} else {
log.Printf("Failed to load users certificate: %s\n", err)
}
} else {
log.Println("Cannot enable certificate users handler: No certificate.")
}
handler = uh
default:
mode = ""
}
if handler == nil {
handler = &UsersSharedsecretHandler{secret: []byte("")}
if handler != nil {
// Register handler Get at the hub.
hub.useridRetriever = func(request *http.Request) (userid string, err error) {
userid, err = handler.Get(request)
if userid != "" {
log.Printf("Users handler get success: %s\n", userid)
}
return
}
log.Printf("Enabled users handler '%s'\n", mode)
}
return &Users{
hub: hub,
realm: realm,
@ -293,6 +329,10 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users { @@ -293,6 +329,10 @@ func NewUsers(hub *Hub, realm string, runtime phoenix.Runtime) *Users {
// Post is used to create new userids for this server.
func (users *Users) Post(request *http.Request) (int, interface{}, http.Header) {
if users.handler == nil {
return 404, "No handler found", http.Header{"Content-Type": {"text/plain"}}
}
var snr *SessionNonceRequest
switch request.Header.Get("Content-Type") {

8
src/app/spreed-speakfreely-server/ws.go

@ -71,15 +71,9 @@ func makeWsHubHandler(h *Hub) http.HandlerFunc { @@ -71,15 +71,9 @@ func makeWsHubHandler(h *Hub) http.HandlerFunc {
// Read request details.
r.ParseForm()
token := r.FormValue("t")
remoteAddr := r.RemoteAddr
if remoteAddr == "@" || remoteAddr == "127.0.0.1" {
if r.Header["X-Forwarded-For"][0] != "" {
remoteAddr = r.Header["X-Forwarded-For"][0]
}
}
// Create a new connection instance.
c := NewConnection(h, ws, remoteAddr)
c := NewConnection(h, ws, r)
if token != "" {
if err := c.reregister(token); err != nil {
log.Println(err)

Loading…
Cancel
Save