func (users *Users) createHandler(mode string, runtime phoenix.Runtime) (handler UsersHandler, err error) { switch mode { case "sharedsecret": secret, _ := runtime.GetString("users", "sharedsecret_secret") if secret != "" { handler = &UsersSharedsecretHandler{secret: []byte(secret)} } else { err = errors.New("Cannot enable sharedsecret users handler: No secret.") } case "httpheader": headerName, _ := runtime.GetString("users", "httpheader_header") if headerName == "" { headerName = "x-users" } handler = &UsersHTTPHeaderHandler{headerName: headerName} case "certificate": var err2 error verifiedHeader, _ := runtime.GetString("users", "certificate_verifiedHeader") verifiedHeaderValue, _ := runtime.GetString("users", "certificate_verifiedHeaderValue") certificateHeader, _ := runtime.GetString("users", "certificate_certificateHeader") validForDays, _ := runtime.GetInt("users", "certificate_validForDays") if validForDays == 0 { validForDays = 365 } 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") certificateFn, _ := runtime.GetString("users", "certificate_certificate") if keyFn != "" && certificateFn != "" { // Load private key from file and use it for signing, if uh.privateKey, err2 = loadX509PrivateKey(keyFn); err2 == nil { log.Printf("Users certificate private key loaded from %s\n", keyFn) } else { log.Printf("Failed to load certificate private key: %s\n", err2) } } if certificateFn != "" { // Load Certificate from file. var certificate tls.Certificate if certificate, err = loadX509Certificate(certificateFn); err == nil { // Parse first certificate in file. var certificates []*x509.Certificate if certificates, err = x509.ParseCertificates(certificate.Certificate[0]); err == nil { // Use first parsed certificate as CA. uh.certificate = certificates[0] log.Printf("Users certificate loaded from %s\n", certificateFn) handler = uh // Get TLS config if the server has one. if tlsConfig, err2 := runtime.TLSConfig(); err2 == nil { // Enable TLS client certificate authentication. tlsConfig.ClientAuth = tls.VerifyClientCertIfGiven // Create cert pool. pool := x509.NewCertPool() // Add CA certificate to pool for TLS client authentication. for _, derCert := range certificate.Certificate { cert, err2 := x509.ParseCertificate(derCert) if err2 != nil { continue } pool.AddCert(cert) } // Add pool to config. tlsConfig.ClientCAs = pool log.Printf("Initialized TLS auth pool with %d certificates.", len(pool.Subjects())) } } } } else { err = errors.New("Cannot enable certificate users handler: No certificate.") } } return }
func runner(runtime phoenix.Runtime) error { log.SetFlags(log.LstdFlags | log.Lmicroseconds) rootFolder, err := runtime.GetString("http", "root") if err != nil { cwd, err2 := os.Getwd() if err2 != nil { return fmt.Errorf("Error while getting current directory: %s", err) } rootFolder = cwd } if !httputils.HasDirPath(rootFolder) { return fmt.Errorf("Configured root '%s' is not a directory.", rootFolder) } if !httputils.HasFilePath(path.Join(rootFolder, "static", "css", "main.min.css")) { return fmt.Errorf("Unable to find client. Path correct and compiled css?") } statsEnabled, err := runtime.GetBool("http", "stats") if err != nil { statsEnabled = false } pprofListen, err := runtime.GetString("http", "pprofListen") if err == nil && pprofListen != "" { log.Printf("Starting pprof HTTP server on %s", pprofListen) go func() { log.Println(http.ListenAndServe(pprofListen, nil)) }() } var sessionSecret []byte sessionSecretString, err := runtime.GetString("app", "sessionSecret") if err != nil { return fmt.Errorf("No sessionSecret in config file.") } sessionSecret, err = hex.DecodeString(sessionSecretString) if err != nil { log.Println("Warning: sessionSecret value is not a hex encoded", err) sessionSecret = []byte(sessionSecretString) } if len(sessionSecret) < 32 { return fmt.Errorf("Length of sessionSecret must be at least 32 bytes.") } if len(sessionSecret) < 32 { log.Printf("Weak sessionSecret (only %d bytes). It is recommended to use a key with 32 or 64 bytes.\n", len(sessionSecret)) } var encryptionSecret []byte encryptionSecretString, err := runtime.GetString("app", "encryptionSecret") if err != nil { return fmt.Errorf("No encryptionSecret in config file.") } encryptionSecret, err = hex.DecodeString(encryptionSecretString) if err != nil { log.Println("Warning: encryptionSecret value is not a hex encoded", err) encryptionSecret = []byte(encryptionSecretString) } switch l := len(encryptionSecret); { case l == 16: case l == 24: case l == 32: default: return fmt.Errorf("Length of encryptionSecret must be exactly 16, 24 or 32 bytes to select AES-128, AES-192 or AES-256.") } var turnSecret []byte turnSecretString, err := runtime.GetString("app", "turnSecret") if err == nil { turnSecret = []byte(turnSecretString) } serverRealm, err := runtime.GetString("app", "serverRealm") if err != nil { serverRealm = "local" } // Create token provider. tokenFile, err := runtime.GetString("app", "tokenFile") if err == nil { if !httputils.HasFilePath(path.Clean(tokenFile)) { return fmt.Errorf("Unable to find token file at %s", tokenFile) } } var tokenProvider TokenProvider if tokenFile != "" { log.Printf("Using token authorization from %s\n", tokenFile) tokenProvider = TokenFileProvider(tokenFile) } // Load remaining configuration items. config = NewConfig(runtime, tokenProvider != nil) // Load templates. templates = template.New("") templates.Delims("<%", "%>") // Load html templates folder err = filepath.Walk(path.Join(rootFolder, "html"), func(path string, info os.FileInfo, err error) error { if err == nil { if strings.HasSuffix(path, ".html") { _, err = templates.ParseFiles(path) if err != nil { return err } } } return nil }) if err != nil { return fmt.Errorf("Failed to load templates: %s", err) } // Load extra templates folder extraFolder, err := runtime.GetString("app", "extra") if err == nil { if !httputils.HasDirPath(extraFolder) { return fmt.Errorf("Configured extra '%s' is not a directory.", extraFolder) } templates, err = templates.ParseGlob(path.Join(extraFolder, "*.html")) if err != nil { return fmt.Errorf("Failed to load extra templates: %s", err) } log.Printf("Loaded extra templates from: %s", extraFolder) } // Define incoming channeling API limit it byte. Larger messages will be discarded. incomingCodecLimit := 1024 * 1024 // 1MB // Create realm string from config. computedRealm := fmt.Sprintf("%s.%s", serverRealm, config.Token) // Set number of go routines if it is 1 if goruntime.GOMAXPROCS(0) == 1 { nCPU := goruntime.NumCPU() goruntime.GOMAXPROCS(nCPU) log.Printf("Using the number of CPU's (%d) as GOMAXPROCS\n", nCPU) } // Get current number of max open files. var rLimit syscall.Rlimit err = syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit) if err != nil { log.Println("Error getting max numer of open files", err) } else { log.Printf("Max open files are %d\n", rLimit.Max) } // Try to increase number of file open files. This only works as root. maxfd, err := runtime.GetInt("http", "maxfd") if err == nil { rLimit.Max = uint64(maxfd) rLimit.Cur = uint64(maxfd) err = syscall.Setrlimit(syscall.RLIMIT_NOFILE, &rLimit) if err != nil { log.Println("Error setting max open files", err) } else { log.Printf("Set max open files successfully to %d\n", uint64(maxfd)) } } // Create router. router := mux.NewRouter() r := router.PathPrefix(config.B).Subrouter().StrictSlash(true) // HTTP listener support. if _, err = runtime.GetString("http", "listen"); err == nil { runtime.DefaultHTTPHandler(r) } // Native HTTPS listener support. if _, err = runtime.GetString("https", "listen"); err == nil { // Setup TLS. tlsConfig, err := runtime.TLSConfig() if err != nil { return fmt.Errorf("TLS configuration error: %s", err) } // Explicitly set random to use. tlsConfig.Rand = rand.Reader log.Println("Native TLS configuration intialized") runtime.DefaultHTTPSHandler(r) } // Prepare services. buddyImages := NewImageCache() codec := NewCodec(incomingCodecLimit) roomManager := NewRoomManager(config, codec) hub := NewHub(config, sessionSecret, encryptionSecret, turnSecret, codec) tickets := NewTickets(sessionSecret, encryptionSecret, computedRealm) sessionManager := NewSessionManager(config, tickets, hub, roomManager, roomManager, buddyImages, sessionSecret) statsManager := NewStatsManager(hub, roomManager, sessionManager) channellingAPI := NewChannellingAPI(config, roomManager, tickets, sessionManager, statsManager, hub, hub, hub) // Add handlers. r.HandleFunc("/", httputils.MakeGzipHandler(mainHandler)) r.Handle("/static/img/buddy/{flags}/{imageid}/{idx:.*}", http.StripPrefix(config.B, makeImageHandler(buddyImages, time.Duration(24)*time.Hour))) r.Handle("/static/{path:.*}", http.StripPrefix(config.B, httputils.FileStaticServer(http.Dir(rootFolder)))) r.Handle("/robots.txt", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static"))))) r.Handle("/favicon.ico", http.StripPrefix(config.B, http.FileServer(http.Dir(path.Join(rootFolder, "static", "img"))))) r.Handle("/ws", makeWSHandler(statsManager, sessionManager, codec, channellingAPI)) // Simple room handler. r.HandleFunc("/{room}", httputils.MakeGzipHandler(roomHandler)) // Sandbox handler. r.HandleFunc("/sandbox/{origin_scheme}/{origin_host}/{sandbox}.html", httputils.MakeGzipHandler(sandboxHandler)) // Add API end points. api := sloth.NewAPI() api.SetMux(r.PathPrefix("/api/v1/").Subrouter()) api.AddResource(&Rooms{}, "/rooms") api.AddResource(config, "/config") api.AddResourceWithWrapper(&Tokens{tokenProvider}, httputils.MakeGzipHandler, "/tokens") if config.UsersEnabled { // Create Users handler. users := NewUsers(hub, tickets, sessionManager, config.UsersMode, serverRealm, runtime) api.AddResource(&Sessions{tickets, hub, users}, "/sessions/{id}/") if config.UsersAllowRegistration { api.AddResource(users, "/users") } } if statsEnabled { api.AddResourceWithWrapper(&Stats{statsManager}, httputils.MakeGzipHandler, "/stats") log.Println("Stats are enabled!") } // Add extra/static support if configured and exists. if extraFolder != "" { extraFolderStatic := path.Join(extraFolder, "static") if _, err = os.Stat(extraFolderStatic); err == nil { r.Handle("/extra/static/{path:.*}", http.StripPrefix(fmt.Sprintf("%sextra", config.B), httputils.FileStaticServer(http.Dir(extraFolder)))) log.Printf("Added URL handler /extra/static/... for static files in %s/...\n", extraFolderStatic) } } // Map everything else to a room when it is a GET. rooms := r.PathPrefix("/").Methods("GET").Subrouter() rooms.HandleFunc("/{room:.*}", httputils.MakeGzipHandler(roomHandler)) return runtime.Start() }