2019-04-24 21:52:08 +00:00
package server
import (
2021-02-21 23:07:06 +00:00
"embed"
"io/fs"
2020-11-05 21:57:03 +00:00
"io/ioutil"
2020-07-16 06:36:37 +00:00
"net"
2021-02-21 23:07:06 +00:00
"net/http"
2019-04-24 21:52:08 +00:00
"os"
2020-11-05 21:57:03 +00:00
"runtime"
2020-06-21 13:40:37 +00:00
"strconv"
2020-11-05 21:57:03 +00:00
"strings"
2019-04-24 21:52:08 +00:00
2020-04-05 12:37:21 +00:00
duoapi "github.com/duosecurity/duo_api_golang"
"github.com/fasthttp/router"
"github.com/valyala/fasthttp"
2020-04-11 04:59:58 +00:00
"github.com/valyala/fasthttp/expvarhandler"
2020-04-28 14:07:20 +00:00
"github.com/valyala/fasthttp/fasthttpadaptor"
2020-04-11 04:59:58 +00:00
"github.com/valyala/fasthttp/pprofhandler"
2020-04-05 12:37:21 +00:00
2021-08-11 01:04:35 +00:00
"github.com/authelia/authelia/v4/internal/configuration/schema"
"github.com/authelia/authelia/v4/internal/duo"
"github.com/authelia/authelia/v4/internal/handlers"
"github.com/authelia/authelia/v4/internal/logging"
"github.com/authelia/authelia/v4/internal/middlewares"
2019-04-24 21:52:08 +00:00
)
2021-02-21 23:07:06 +00:00
//go:embed public_html
var assets embed . FS
2021-05-04 22:06:05 +00:00
func registerRoutes ( configuration schema . Configuration , providers middlewares . Providers ) fasthttp . RequestHandler {
2019-04-24 21:52:08 +00:00
autheliaMiddleware := middlewares . AutheliaMiddleware ( configuration , providers )
2020-06-21 13:40:37 +00:00
rememberMe := strconv . FormatBool ( configuration . Session . RememberMeDuration != "0" )
resetPassword := strconv . FormatBool ( ! configuration . AuthenticationBackend . DisableResetPassword )
2021-02-21 23:07:06 +00:00
embeddedPath , _ := fs . Sub ( assets , "public_html" )
embeddedFS := fasthttpadaptor . NewFastHTTPHandler ( http . FileServer ( http . FS ( embeddedPath ) ) )
2020-05-07 11:29:12 +00:00
rootFiles := [ ] string { "favicon.ico" , "manifest.json" , "robots.txt" }
2020-05-21 02:20:55 +00:00
2021-08-10 00:31:08 +00:00
serveIndexHandler := ServeTemplatedFile ( embeddedAssets , indexFile , rememberMe , resetPassword , configuration . Session . Name , configuration . Theme )
serveSwaggerHandler := ServeTemplatedFile ( swaggerAssets , indexFile , rememberMe , resetPassword , configuration . Session . Name , configuration . Theme )
serveSwaggerAPIHandler := ServeTemplatedFile ( swaggerAssets , apiFile , rememberMe , resetPassword , configuration . Session . Name , configuration . Theme )
2020-06-21 13:40:37 +00:00
2020-05-21 02:20:55 +00:00
r := router . New ( )
2020-06-21 13:40:37 +00:00
r . GET ( "/" , serveIndexHandler )
2021-07-22 03:52:37 +00:00
r . OPTIONS ( "/" , autheliaMiddleware ( handleOPTIONS ) )
2021-01-03 04:28:46 +00:00
r . GET ( "/api/" , serveSwaggerHandler )
r . GET ( "/api/" + apiFile , serveSwaggerAPIHandler )
2020-05-07 11:29:12 +00:00
for _ , f := range rootFiles {
2021-02-21 23:07:06 +00:00
r . GET ( "/" + f , embeddedFS )
2020-05-07 11:29:12 +00:00
}
2021-02-21 23:07:06 +00:00
r . GET ( "/static/{filepath:*}" , embeddedFS )
2021-03-11 07:36:58 +00:00
r . ANY ( "/api/{filepath:*}" , embeddedFS )
2019-04-24 21:52:08 +00:00
2020-11-24 23:20:52 +00:00
r . GET ( "/api/health" , autheliaMiddleware ( handlers . HealthGet ) )
2020-05-21 02:20:55 +00:00
r . GET ( "/api/state" , autheliaMiddleware ( handlers . StateGet ) )
2019-04-24 21:52:08 +00:00
2020-06-21 13:40:37 +00:00
r . GET ( "/api/configuration" , autheliaMiddleware (
middlewares . RequireFirstFactor ( handlers . ConfigurationGet ) ) )
2019-12-07 16:40:42 +00:00
2020-05-21 02:20:55 +00:00
r . GET ( "/api/verify" , autheliaMiddleware ( handlers . VerifyGet ( configuration . AuthenticationBackend ) ) )
r . HEAD ( "/api/verify" , autheliaMiddleware ( handlers . VerifyGet ( configuration . AuthenticationBackend ) ) )
2019-04-24 21:52:08 +00:00
2021-08-02 06:15:38 +00:00
r . POST ( "/api/checks/safe-redirection" , autheliaMiddleware ( handlers . CheckSafeRedirection ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/firstfactor" , autheliaMiddleware ( handlers . FirstFactorPost ( 1000 , true ) ) )
r . POST ( "/api/logout" , autheliaMiddleware ( handlers . LogoutPost ) )
2019-04-24 21:52:08 +00:00
2020-04-28 14:07:20 +00:00
// Only register endpoints if forgot password is not disabled.
2020-04-04 23:28:09 +00:00
if ! configuration . AuthenticationBackend . DisableResetPassword {
// Password reset related endpoints.
2020-05-21 02:20:55 +00:00
r . POST ( "/api/reset-password/identity/start" , autheliaMiddleware (
2020-04-04 23:28:09 +00:00
handlers . ResetPasswordIdentityStart ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/reset-password/identity/finish" , autheliaMiddleware (
2020-04-04 23:28:09 +00:00
handlers . ResetPasswordIdentityFinish ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/reset-password" , autheliaMiddleware (
2020-04-04 23:28:09 +00:00
handlers . ResetPasswordPost ) )
}
2019-04-24 21:52:08 +00:00
2020-04-28 14:07:20 +00:00
// Information about the user.
2020-05-21 02:20:55 +00:00
r . GET ( "/api/user/info" , autheliaMiddleware (
2019-12-07 11:18:22 +00:00
middlewares . RequireFirstFactor ( handlers . UserInfoGet ) ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/user/info/2fa_method" , autheliaMiddleware (
2019-12-07 11:18:22 +00:00
middlewares . RequireFirstFactor ( handlers . MethodPreferencePost ) ) )
2019-04-24 21:52:08 +00:00
2020-04-28 14:07:20 +00:00
// TOTP related endpoints.
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/totp/identity/start" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorTOTPIdentityStart ) ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/totp/identity/finish" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorTOTPIdentityFinish ) ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/totp" , autheliaMiddleware (
2020-03-25 01:48:20 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorTOTPPost ( & handlers . TOTPVerifierImpl {
Period : uint ( configuration . TOTP . Period ) ,
Skew : uint ( * configuration . TOTP . Skew ) ,
} ) ) ) )
2019-04-24 21:52:08 +00:00
2020-04-28 14:07:20 +00:00
// U2F related endpoints.
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/u2f/identity/start" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorU2FIdentityStart ) ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/u2f/identity/finish" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorU2FIdentityFinish ) ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/u2f/register" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorU2FRegister ) ) )
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/u2f/sign_request" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorU2FSignGet ) ) )
2020-02-01 12:54:50 +00:00
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/u2f/sign" , autheliaMiddleware (
2020-02-01 12:54:50 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorU2FSignPost ( & handlers . U2FVerifierImpl { } ) ) ) )
2019-04-24 21:52:08 +00:00
2020-04-28 14:07:20 +00:00
// Configure DUO api endpoint only if configuration exists.
2019-04-24 21:52:08 +00:00
if configuration . DuoAPI != nil {
var duoAPI duo . API
2020-11-19 01:50:34 +00:00
if os . Getenv ( "ENVIRONMENT" ) == dev {
2019-04-24 21:52:08 +00:00
duoAPI = duo . NewDuoAPI ( duoapi . NewDuoApi (
configuration . DuoAPI . IntegrationKey ,
configuration . DuoAPI . SecretKey ,
configuration . DuoAPI . Hostname , "" , duoapi . SetInsecure ( ) ) )
} else {
duoAPI = duo . NewDuoAPI ( duoapi . NewDuoApi (
configuration . DuoAPI . IntegrationKey ,
configuration . DuoAPI . SecretKey ,
configuration . DuoAPI . Hostname , "" ) )
}
2020-05-21 02:20:55 +00:00
r . POST ( "/api/secondfactor/duo" , autheliaMiddleware (
2019-04-24 21:52:08 +00:00
middlewares . RequireFirstFactor ( handlers . SecondFactorDuoPost ( duoAPI ) ) ) )
}
2021-06-01 04:09:50 +00:00
if configuration . Server . EnablePprof {
2020-05-21 02:20:55 +00:00
r . GET ( "/debug/pprof/{name?}" , pprofhandler . PprofHandler )
2021-06-01 04:09:50 +00:00
}
if configuration . Server . EnableExpvars {
2020-05-21 02:20:55 +00:00
r . GET ( "/debug/vars" , expvarhandler . ExpvarHandler )
2020-04-11 04:59:58 +00:00
}
2020-06-21 13:40:37 +00:00
r . NotFound = serveIndexHandler
2020-05-21 02:20:55 +00:00
handler := middlewares . LogRequestMiddleware ( r . Handler )
if configuration . Server . Path != "" {
2021-08-10 00:31:08 +00:00
handler = middlewares . StripPathMiddleware ( configuration . Server . Path , handler )
2020-05-21 02:20:55 +00:00
}
2019-11-30 16:49:52 +00:00
2021-05-04 22:06:05 +00:00
if providers . OpenIDConnect . Fosite != nil {
handlers . RegisterOIDC ( r , autheliaMiddleware )
}
return handler
}
2021-08-03 09:55:21 +00:00
// Start Authelia's internal webserver with the given configuration and providers.
func Start ( configuration schema . Configuration , providers middlewares . Providers ) {
2021-05-04 22:06:05 +00:00
logger := logging . Logger ( )
handler := registerRoutes ( configuration , providers )
2020-04-11 04:59:58 +00:00
server := & fasthttp . Server {
2020-04-30 03:16:41 +00:00
ErrorHandler : autheliaErrorHandler ,
2020-05-21 02:20:55 +00:00
Handler : handler ,
2020-04-30 03:16:41 +00:00
NoDefaultServerHeader : true ,
ReadBufferSize : configuration . Server . ReadBufferSize ,
WriteBufferSize : configuration . Server . WriteBufferSize ,
2020-04-11 04:59:58 +00:00
}
2020-04-30 02:03:05 +00:00
2021-08-02 11:55:30 +00:00
addrPattern := net . JoinHostPort ( configuration . Server . Host , strconv . Itoa ( configuration . Server . Port ) )
2020-07-16 06:36:37 +00:00
listener , err := net . Listen ( "tcp" , addrPattern )
if err != nil {
2021-01-16 23:23:35 +00:00
logger . Fatalf ( "Error initializing listener: %s" , err )
2020-07-16 06:36:37 +00:00
}
2019-04-24 21:52:08 +00:00
2021-05-04 22:06:05 +00:00
// TODO(clems4ever): move that piece to a more related location, probably in the configuration package.
2020-11-05 21:57:03 +00:00
if configuration . AuthenticationBackend . File != nil && configuration . AuthenticationBackend . File . Password . Algorithm == "argon2id" && runtime . GOOS == "linux" {
f , err := ioutil . ReadFile ( "/sys/fs/cgroup/memory/memory.limit_in_bytes" )
if err != nil {
2021-01-16 23:23:35 +00:00
logger . Warnf ( "Error reading hosts memory limit: %s" , err )
2020-11-05 21:57:03 +00:00
} else {
m , _ := strconv . Atoi ( strings . TrimSuffix ( string ( f ) , "\n" ) )
hostMem := float64 ( m ) / 1024 / 1024 / 1024
argonMem := float64 ( configuration . AuthenticationBackend . File . Password . Memory ) / 1024
if hostMem / argonMem <= 2 {
2021-01-16 23:23:35 +00:00
logger . Warnf ( "Authelia's password hashing memory parameter is set to: %gGB this is %g%% of the available memory: %gGB" , argonMem , argonMem / hostMem * 100 , hostMem )
logger . Warn ( "Please read https://www.authelia.com/docs/configuration/authentication/file.html#memory and tune your deployment" )
2020-11-05 21:57:03 +00:00
}
}
}
2021-08-02 11:55:30 +00:00
if configuration . Server . TLS . Certificate != "" && configuration . Server . TLS . Key != "" {
2021-08-05 04:02:07 +00:00
if err = writeHealthCheckEnv ( configuration . Server . DisableHealthcheck , "https" , configuration . Server . Host , configuration . Server . Path , configuration . Server . Port ) ; err != nil {
logger . Fatalf ( "Could not configure healthcheck: %v" , err )
}
2021-08-10 00:31:08 +00:00
if configuration . Server . Path == "" {
logger . Infof ( "Listening for TLS connections on '%s' path '/'" , addrPattern )
} else {
logger . Infof ( "Listening for TLS connections on '%s' paths '/' and '%s'" , addrPattern , configuration . Server . Path )
}
2021-08-02 11:55:30 +00:00
logger . Fatal ( server . ServeTLS ( listener , configuration . Server . TLS . Certificate , configuration . Server . TLS . Key ) )
2020-03-03 07:18:25 +00:00
} else {
2021-08-05 04:02:07 +00:00
if err = writeHealthCheckEnv ( configuration . Server . DisableHealthcheck , "http" , configuration . Server . Host , configuration . Server . Path , configuration . Server . Port ) ; err != nil {
logger . Fatalf ( "Could not configure healthcheck: %v" , err )
}
2021-08-10 00:31:08 +00:00
if configuration . Server . Path == "" {
logger . Infof ( "Listening for non-TLS connections on '%s' path '/'" , addrPattern )
} else {
logger . Infof ( "Listening for non-TLS connections on '%s' paths '/' and '%s'" , addrPattern , configuration . Server . Path )
}
2021-01-16 23:23:35 +00:00
logger . Fatal ( server . Serve ( listener ) )
2020-03-03 07:18:25 +00:00
}
2019-04-24 21:52:08 +00:00
}