Browse Source

Implemented certificate pass through from SSL frontend proxy via HTTP headers.

pull/28/head
Simon Eisenmann 11 years ago
parent
commit
ca7e6d0332
  1. 12
      server.conf.in
  2. 65
      src/app/spreed-speakfreely-server/users.go

12
server.conf.in

@ -138,18 +138,16 @@ enabled = false
; settings when not using a front end proxy. ; settings when not using a front end proxy.
;certificate_verifiedHeader = x-verified ;certificate_verifiedHeader = x-verified
;certificate_verifiedHeaderValue = SUCCESS ;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 ; 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 ; by a front end proxy. With Nginx the required value is in $ssl_client_cert.
; certificate_useridHeader value and the front end TLS proxy does inject the
; userid into that header.
;certificate_certificateHeader = x-certificate ;certificate_certificateHeader = x-certificate
; The valid duration of generated certificates created in certificate mode when ; The valid duration of generated certificates created in certificate mode when
; allowRegistration is enabled. ; allowRegistration is enabled.
;certificate_validForDays = 365 ;certificate_validForDays = 365
; Organization to set into the created user certificates. Use a readable public
; name to make the certificate easily recognizable as certificate for your
; server so users can choose the correct certificate when prompted.
;certificate_organization= = My Spreed Server
; If enabled the server can create new userids. Set allowRegistration to true to ; If enabled the server can create new userids. Set allowRegistration to true to
; enable userid creation/registration. Users are created according the settings ; enable userid creation/registration. Users are created according the settings
; of the currently configured mode (see above). ; of the currently configured mode (see above).

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

@ -31,6 +31,7 @@ import (
"crypto/x509/pkix" "crypto/x509/pkix"
"encoding/base64" "encoding/base64"
"encoding/json" "encoding/json"
"encoding/pem"
"errors" "errors"
"fmt" "fmt"
"github.com/longsleep/pkac" "github.com/longsleep/pkac"
@ -131,6 +132,10 @@ type UsersCertificateHandler struct {
validFor time.Duration validFor time.Duration
privateKey crypto.PrivateKey privateKey crypto.PrivateKey
certificate *x509.Certificate certificate *x509.Certificate
verifiedHeader string
verifiedHeaderValue string
certificateHeader string
organization []string
} }
func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certificate, error) { func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certificate, error) {
@ -147,6 +152,7 @@ func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certif
SerialNumber: serialNumber, SerialNumber: serialNumber,
Subject: pkix.Name{ Subject: pkix.Name{
CommonName: commonName, CommonName: commonName,
Organization: uh.organization,
}, },
NotBefore: notBefore, NotBefore: notBefore,
NotAfter: notAfter, NotAfter: notAfter,
@ -158,7 +164,41 @@ func (uh *UsersCertificateHandler) makeTemplate(commonName string) (*x509.Certif
} }
func (uh *UsersCertificateHandler) Get(request *http.Request) (userid string, err error) { func (uh *UsersCertificateHandler) Get(request *http.Request) (userid string, err error) {
if uh.verifiedHeader != "" && uh.verifiedHeaderValue != "" {
// Use incoming HTTP headers.
if request.Header.Get(uh.verifiedHeader) != uh.verifiedHeaderValue {
// Verify header does not match - ignore incoming userid.
return
}
if uh.certificateHeader != "" {
// Read userid from certificate in header if configured.
var cert tls.Certificate
var certDERBlock *pem.Block
// Whuahah this is an evil fix to get back valid PEM data from Nginx $ssl_client_cert values.
certString := strings.Replace(request.Header.Get(uh.certificateHeader), " ", "\n", -1)
certString = strings.Replace(certString, "BEGIN\n", "BEGIN ", 1)
certString = strings.Replace(certString, "END\n", "END ", 1)
certPEMBlock := []byte(certString)
for {
certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
if certDERBlock == nil {
break
}
if certDERBlock.Type == "CERTIFICATE" {
cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
}
}
if len(cert.Certificate) == 0 {
err = errors.New("failed to parse certificate PEM data")
return
}
var certificates []*x509.Certificate
if certificates, err = x509.ParseCertificates(cert.Certificate[0]); err == nil {
userid = certificates[0].Subject.CommonName
}
}
} else {
// Direct TLS termination and authentication.
if request.TLS == nil || len(request.TLS.VerifiedChains) == 0 { if request.TLS == nil || len(request.TLS.VerifiedChains) == 0 {
return return
} }
@ -166,11 +206,10 @@ func (uh *UsersCertificateHandler) Get(request *http.Request) (userid string, er
if len(chain) == 0 { if len(chain) == 0 {
return return
} }
cert := chain[0] cert := chain[0]
userid = cert.Subject.CommonName userid = cert.Subject.CommonName
}
log.Printf("Client certificate found for user: %s\n", userid) log.Printf("Client certificate found for user: %s\n", userid)
return return
} }
@ -268,9 +307,13 @@ func NewUsers(hub *Hub, mode, realm string, runtime phoenix.Runtime) *Users {
// Register handler Get at the hub. // Register handler Get at the hub.
users.hub.useridRetriever = func(request *http.Request) (userid string, err error) { users.hub.useridRetriever = func(request *http.Request) (userid string, err error) {
userid, err = handler.Get(request) userid, err = handler.Get(request)
if err != nil {
log.Printf("Failed to get userid from handler: %s", err)
} else {
if userid != "" { if userid != "" {
log.Printf("Users handler get success: %s\n", userid) log.Printf("Users handler get success: %s\n", userid)
} }
}
return return
} }
log.Printf("Enabled users handler '%s'\n", mode) log.Printf("Enabled users handler '%s'\n", mode)
@ -299,12 +342,24 @@ func (users *Users) createHandler(mode string, runtime phoenix.Runtime) (handler
handler = &UsersHTTPHeaderHandler{headerName: headerName} handler = &UsersHTTPHeaderHandler{headerName: headerName}
case "certificate": case "certificate":
var err2 error var err2 error
uh := &UsersCertificateHandler{} verifiedHeader, _ := runtime.GetString("users", "certificate_verifiedHeader")
verifiedHeaderValue, _ := runtime.GetString("users", "certificate_verifiedHeaderValue")
certificateHeader, _ := runtime.GetString("users", "certificate_certificateHeader")
validForDays, _ := runtime.GetInt("users", "certificate_validForDays") validForDays, _ := runtime.GetInt("users", "certificate_validForDays")
if validForDays == 0 { if validForDays == 0 {
validForDays = 365 validForDays = 365
} }
uh.validFor = time.Duration(validForDays) * 24 * time.Hour organization, _ := runtime.GetString("users", "certificate_organization")
if organization == "" {
organization = "My Spreed Server"
}
uh := &UsersCertificateHandler{
verifiedHeader: verifiedHeader,
verifiedHeaderValue: verifiedHeaderValue,
certificateHeader: certificateHeader,
validFor: time.Duration(validForDays) * 24 * time.Hour,
organization: []string{organization},
}
keyFn, _ := runtime.GetString("users", "certificate_key") keyFn, _ := runtime.GetString("users", "certificate_key")
certificateFn, _ := runtime.GetString("users", "certificate_certificate") certificateFn, _ := runtime.GetString("users", "certificate_certificate")
if keyFn != "" && certificateFn != "" { if keyFn != "" && certificateFn != "" {

Loading…
Cancel
Save