2018-11-14 10:18:03 +01:00
package auth
import (
"context"
"fmt"
"net/http"
"net/url"
"strings"
goauth "github.com/abbot/go-http-auth"
2023-02-03 15:24:05 +01:00
"github.com/traefik/traefik/v3/pkg/config/dynamic"
"github.com/traefik/traefik/v3/pkg/middlewares"
"github.com/traefik/traefik/v3/pkg/middlewares/accesslog"
2024-03-12 09:48:04 +01:00
"github.com/traefik/traefik/v3/pkg/middlewares/observability"
2024-01-08 10:10:06 +02:00
"go.opentelemetry.io/otel/trace"
2018-11-14 10:18:03 +01:00
)
const (
2024-01-08 10:10:06 +02:00
typeNameDigest = "digestAuth"
2018-11-14 10:18:03 +01:00
)
type digestAuth struct {
next http . Handler
auth * goauth . DigestAuth
users map [ string ] string
headerField string
removeHeader bool
name string
}
// NewDigest creates a digest auth middleware.
2019-07-10 09:26:04 +02:00
func NewDigest ( ctx context . Context , next http . Handler , authConfig dynamic . DigestAuth , name string ) ( http . Handler , error ) {
2024-01-08 10:10:06 +02:00
middlewares . GetLogger ( ctx , name , typeNameDigest ) . Debug ( ) . Msg ( "Creating middleware" )
2022-11-21 18:36:05 +01:00
2018-11-14 10:18:03 +01:00
users , err := getUsers ( authConfig . UsersFile , authConfig . Users , digestUserParser )
if err != nil {
return nil , err
}
da := & digestAuth {
next : next ,
users : users ,
headerField : authConfig . HeaderField ,
removeHeader : authConfig . RemoveHeader ,
name : name ,
}
realm := defaultRealm
if len ( authConfig . Realm ) > 0 {
realm = authConfig . Realm
}
da . auth = goauth . NewDigestAuthenticator ( realm , da . secretDigest )
return da , nil
}
2024-01-08 10:10:06 +02:00
func ( d * digestAuth ) GetTracingInformation ( ) ( string , string , trace . SpanKind ) {
return d . name , typeNameDigest , trace . SpanKindInternal
2018-11-14 10:18:03 +01:00
}
func ( d * digestAuth ) ServeHTTP ( rw http . ResponseWriter , req * http . Request ) {
2024-01-08 10:10:06 +02:00
logger := middlewares . GetLogger ( req . Context ( ) , d . name , typeNameDigest )
2018-11-14 10:18:03 +01:00
2020-03-25 14:28:04 +01:00
username , authinfo := d . auth . CheckAuth ( req )
if username == "" {
2020-06-18 16:02:04 +02:00
headerField := d . headerField
if d . headerField == "" {
headerField = "Authorization"
}
auth := goauth . DigestAuthParams ( req . Header . Get ( headerField ) )
if auth [ "username" ] != "" {
logData := accesslog . GetLogData ( req )
if logData != nil {
logData . Core [ accesslog . ClientUsername ] = auth [ "username" ]
}
}
2020-03-25 14:28:04 +01:00
if authinfo != nil && * authinfo == "stale" {
2022-11-21 18:36:05 +01:00
logger . Debug ( ) . Msg ( "Digest authentication failed, possibly because out of order requests" )
2024-03-12 09:48:04 +01:00
observability . SetStatusErrorf ( req . Context ( ) , "Digest authentication failed, possibly because out of order requests" )
2020-03-25 14:28:04 +01:00
d . auth . RequireAuthStale ( rw , req )
return
}
2022-11-21 18:36:05 +01:00
logger . Debug ( ) . Msg ( "Digest authentication failed" )
2024-03-12 09:48:04 +01:00
observability . SetStatusErrorf ( req . Context ( ) , "Digest authentication failed" )
2018-11-14 10:18:03 +01:00
d . auth . RequireAuth ( rw , req )
2020-03-25 14:28:04 +01:00
return
}
2018-11-14 10:18:03 +01:00
2022-11-21 18:36:05 +01:00
logger . Debug ( ) . Msg ( "Digest authentication succeeded" )
2020-03-25 14:28:04 +01:00
req . URL . User = url . User ( username )
2018-11-14 10:18:03 +01:00
2020-03-25 14:28:04 +01:00
logData := accesslog . GetLogData ( req )
if logData != nil {
logData . Core [ accesslog . ClientUsername ] = username
}
2018-11-14 10:18:03 +01:00
2020-03-25 14:28:04 +01:00
if d . headerField != "" {
req . Header [ d . headerField ] = [ ] string { username }
}
if d . removeHeader {
2022-11-21 18:36:05 +01:00
logger . Debug ( ) . Msg ( "Removing the Authorization header" )
2020-03-25 14:28:04 +01:00
req . Header . Del ( authorizationHeader )
2018-11-14 10:18:03 +01:00
}
2020-03-25 14:28:04 +01:00
d . next . ServeHTTP ( rw , req )
2018-11-14 10:18:03 +01:00
}
func ( d * digestAuth ) secretDigest ( user , realm string ) string {
if secret , ok := d . users [ user + ":" + realm ] ; ok {
return secret
}
return ""
}
func digestUserParser ( user string ) ( string , string , error ) {
split := strings . Split ( user , ":" )
if len ( split ) != 3 {
return "" , "" , fmt . Errorf ( "error parsing DigestUser: %v" , user )
}
return split [ 0 ] + ":" + split [ 1 ] , split [ 2 ] , nil
}