2018-02-26 14:34:04 +00:00
package rules
2016-03-27 00:05:17 +00:00
import (
2016-08-05 18:42:45 +00:00
"fmt"
2016-04-06 11:06:31 +00:00
"net/http"
2016-03-27 00:05:17 +00:00
"strings"
2017-04-17 20:47:53 +00:00
2019-08-03 01:58:23 +00:00
"github.com/containous/traefik/v2/pkg/log"
"github.com/containous/traefik/v2/pkg/middlewares/requestdecorator"
"github.com/gorilla/mux"
2019-01-30 15:24:07 +00:00
"github.com/vulcand/predicate"
2016-03-27 00:05:17 +00:00
)
2019-01-30 15:24:07 +00:00
var funcs = map [ string ] func ( * mux . Route , ... string ) error {
2020-07-08 10:18:03 +00:00
"Host" : hostSecure ,
"HostHeader" : host ,
"HostSNI" : hostSNI ,
2019-01-30 15:24:07 +00:00
"HostRegexp" : hostRegexp ,
"Path" : path ,
"PathPrefix" : pathPrefix ,
"Method" : methods ,
"Headers" : headers ,
"HeadersRegexp" : headersRegexp ,
"Query" : query ,
2016-03-27 00:05:17 +00:00
}
2020-07-08 10:18:03 +00:00
// EnableDomainFronting initialize the matcher functions to used on routers.
// InsecureSNI defines if the domain fronting is allowed.
func EnableDomainFronting ( ok bool ) {
if ok {
log . WithoutContext ( ) . Warn ( "With insecureSNI enabled, router rules do not prevent domain fronting techniques. Please use `HostHeader` and `HostSNI` rules if domain fronting is not desired." )
funcs [ "Host" ] = host
return
}
funcs [ "Host" ] = hostSecure
}
2020-05-11 10:06:07 +00:00
// Router handle routing with rules.
2019-01-30 15:24:07 +00:00
type Router struct {
* mux . Router
parser predicate . Parser
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
// NewRouter returns a new router instance.
func NewRouter ( ) ( * Router , error ) {
parser , err := newParser ( )
if err != nil {
return nil , err
2016-04-06 11:06:31 +00:00
}
2019-01-30 15:24:07 +00:00
return & Router {
Router : mux . NewRouter ( ) . SkipClean ( true ) ,
parser : parser ,
} , nil
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
// AddRoute add a new route to the router.
func ( r * Router ) AddRoute ( rule string , priority int , handler http . Handler ) error {
parse , err := r . parser . Parse ( rule )
if err != nil {
2020-05-11 10:06:07 +00:00
return fmt . Errorf ( "error while parsing rule %s: %w" , rule , err )
2016-04-06 11:06:31 +00:00
}
2019-01-30 15:24:07 +00:00
buildTree , ok := parse . ( treeBuilder )
if ! ok {
return fmt . Errorf ( "error while parsing rule %s" , rule )
2016-04-06 11:06:31 +00:00
}
2016-03-27 00:05:17 +00:00
2019-01-30 15:24:07 +00:00
if priority == 0 {
priority = len ( rule )
2017-12-19 16:00:12 +00:00
}
2019-01-30 15:24:07 +00:00
route := r . NewRoute ( ) . Handler ( handler ) . Priority ( priority )
return addRuleOnRoute ( route , buildTree ( ) )
2017-12-19 16:00:12 +00:00
}
2019-01-30 15:24:07 +00:00
type tree struct {
matcher string
value [ ] string
ruleLeft * tree
ruleRight * tree
2017-12-19 16:00:12 +00:00
}
2019-01-30 15:24:07 +00:00
func path ( route * mux . Route , paths ... string ) error {
rt := route . Subrouter ( )
2016-04-06 11:06:31 +00:00
for _ , path := range paths {
2019-01-30 15:24:07 +00:00
tmpRt := rt . Path ( path )
if tmpRt . GetError ( ) != nil {
return tmpRt . GetError ( )
}
2016-04-06 11:06:31 +00:00
}
2019-01-30 15:24:07 +00:00
return nil
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
func pathPrefix ( route * mux . Route , paths ... string ) error {
rt := route . Subrouter ( )
2017-03-24 11:07:59 +00:00
2017-04-25 18:13:39 +00:00
for _ , path := range paths {
2019-01-30 15:24:07 +00:00
tmpRt := rt . PathPrefix ( path )
if tmpRt . GetError ( ) != nil {
return tmpRt . GetError ( )
}
2017-04-25 18:13:39 +00:00
}
2019-01-30 15:24:07 +00:00
return nil
2017-04-25 18:13:39 +00:00
}
2019-01-30 15:24:07 +00:00
func host ( route * mux . Route , hosts ... string ) error {
for i , host := range hosts {
hosts [ i ] = strings . ToLower ( host )
2017-10-30 11:54:03 +00:00
}
2019-01-30 15:24:07 +00:00
route . MatcherFunc ( func ( req * http . Request , _ * mux . RouteMatch ) bool {
2020-07-08 10:18:03 +00:00
return matchHost ( req , true , hosts ... )
} )
return nil
}
func matchHost ( req * http . Request , insecureSNI bool , hosts ... string ) bool {
logger := log . FromContext ( req . Context ( ) )
reqHost := requestdecorator . GetCanonizedHost ( req . Context ( ) )
if len ( reqHost ) == 0 {
logger . Warnf ( "Could not retrieve CanonizedHost, rejecting %s" , req . Host )
return false
}
flatH := requestdecorator . GetCNAMEFlatten ( req . Context ( ) )
if len ( flatH ) > 0 {
for _ , host := range hosts {
if strings . EqualFold ( reqHost , host ) || strings . EqualFold ( flatH , host ) {
return true
}
logger . Debugf ( "CNAMEFlattening: request %s which resolved to %s, is not matched to route %s" , reqHost , flatH , host )
}
return false
}
for _ , host := range hosts {
if reqHost == host {
logHostSNI ( insecureSNI , req , reqHost )
return true
2019-01-30 15:24:07 +00:00
}
2016-12-02 12:40:18 +00:00
2020-07-08 10:18:03 +00:00
// Check for match on trailing period on host
if last := len ( host ) - 1 ; last >= 0 && host [ last ] == '.' {
h := host [ : last ]
if reqHost == h {
logHostSNI ( insecureSNI , req , reqHost )
return true
2019-01-30 15:24:07 +00:00
}
}
2020-07-08 10:18:03 +00:00
// Check for match on trailing period on request
if last := len ( reqHost ) - 1 ; last >= 0 && reqHost [ last ] == '.' {
h := reqHost [ : last ]
if h == host {
logHostSNI ( insecureSNI , req , reqHost )
return true
}
}
}
return false
}
func logHostSNI ( insecureSNI bool , req * http . Request , reqHost string ) {
if insecureSNI && req . TLS != nil && ! strings . EqualFold ( reqHost , req . TLS . ServerName ) {
log . FromContext ( req . Context ( ) ) . Debugf ( "Router reached with Host(%q) different from SNI(%q)" , reqHost , req . TLS . ServerName )
}
}
func hostSNI ( route * mux . Route , hosts ... string ) error {
for i , host := range hosts {
hosts [ i ] = strings . ToLower ( host )
}
route . MatcherFunc ( func ( req * http . Request , _ * mux . RouteMatch ) bool {
return matchSNI ( req , hosts ... )
} )
return nil
}
func matchSNI ( req * http . Request , hosts ... string ) bool {
if req . TLS == nil {
return true
}
if req . TLS . ServerName == "" {
return false
}
for _ , host := range hosts {
if strings . EqualFold ( req . TLS . ServerName , host ) {
return true
}
// Check for match on trailing period on host
if last := len ( host ) - 1 ; last >= 0 && host [ last ] == '.' {
h := host [ : last ]
if strings . EqualFold ( req . TLS . ServerName , h ) {
2019-01-30 15:24:07 +00:00
return true
}
2020-07-08 10:18:03 +00:00
}
2019-05-06 15:16:03 +00:00
2020-07-08 10:18:03 +00:00
// Check for match on trailing period on request
if last := len ( req . TLS . ServerName ) - 1 ; last >= 0 && req . TLS . ServerName [ last ] == '.' {
h := req . TLS . ServerName [ : last ]
if strings . EqualFold ( h , host ) {
return true
2019-05-06 15:16:03 +00:00
}
2020-07-08 10:18:03 +00:00
}
}
return false
}
func hostSecure ( route * mux . Route , hosts ... string ) error {
for i , host := range hosts {
hosts [ i ] = strings . ToLower ( host )
}
2019-05-06 15:16:03 +00:00
2020-07-08 10:18:03 +00:00
route . MatcherFunc ( func ( req * http . Request , _ * mux . RouteMatch ) bool {
for _ , host := range hosts {
if matchSNI ( req , host ) && matchHost ( req , false , host ) {
return true
2019-05-06 15:16:03 +00:00
}
2019-01-30 15:24:07 +00:00
}
2020-07-08 10:18:03 +00:00
2019-01-30 15:24:07 +00:00
return false
} )
2020-07-08 10:18:03 +00:00
2019-01-30 15:24:07 +00:00
return nil
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
func hostRegexp ( route * mux . Route , hosts ... string ) error {
router := route . Subrouter ( )
for _ , host := range hosts {
tmpRt := router . Host ( host )
if tmpRt . GetError ( ) != nil {
return tmpRt . GetError ( )
}
2017-03-24 11:07:59 +00:00
}
2019-01-30 15:24:07 +00:00
return nil
2017-03-24 11:07:59 +00:00
}
2019-01-30 15:24:07 +00:00
func methods ( route * mux . Route , methods ... string ) error {
return route . Methods ( methods ... ) . GetError ( )
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
func headers ( route * mux . Route , headers ... string ) error {
return route . Headers ( headers ... ) . GetError ( )
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
func headersRegexp ( route * mux . Route , headers ... string ) error {
return route . HeadersRegexp ( headers ... ) . GetError ( )
2016-03-27 00:05:17 +00:00
}
2019-01-30 15:24:07 +00:00
func query ( route * mux . Route , query ... string ) error {
2017-08-24 18:28:03 +00:00
var queries [ ] string
for _ , elem := range query {
queries = append ( queries , strings . Split ( elem , "=" ) ... )
}
2019-01-30 15:24:07 +00:00
route . Queries ( queries ... )
// Queries can return nil so we can't chain the GetError()
return route . GetError ( )
2017-08-24 18:28:03 +00:00
}
2019-01-30 15:24:07 +00:00
func addRuleOnRouter ( router * mux . Router , rule * tree ) error {
switch rule . matcher {
case "and" :
route := router . NewRoute ( )
err := addRuleOnRoute ( route , rule . ruleLeft )
if err != nil {
return err
2016-06-06 07:21:00 +00:00
}
2018-03-23 12:30:03 +00:00
2019-01-30 15:24:07 +00:00
return addRuleOnRoute ( route , rule . ruleRight )
case "or" :
err := addRuleOnRouter ( router , rule . ruleLeft )
if err != nil {
return err
2016-04-07 13:01:04 +00:00
}
2018-03-23 12:30:03 +00:00
2019-01-30 15:24:07 +00:00
return addRuleOnRouter ( router , rule . ruleRight )
default :
err := checkRule ( rule )
if err != nil {
return err
2016-06-06 07:21:00 +00:00
}
2019-01-30 15:24:07 +00:00
return funcs [ rule . matcher ] ( router . NewRoute ( ) , rule . value ... )
}
}
2016-08-05 18:42:45 +00:00
2019-01-30 15:24:07 +00:00
func addRuleOnRoute ( route * mux . Route , rule * tree ) error {
switch rule . matcher {
case "and" :
err := addRuleOnRoute ( route , rule . ruleLeft )
2016-08-05 18:42:45 +00:00
if err != nil {
2019-01-30 15:24:07 +00:00
return err
2016-08-05 18:42:45 +00:00
}
2019-01-30 15:24:07 +00:00
return addRuleOnRoute ( route , rule . ruleRight )
case "or" :
subRouter := route . Subrouter ( )
2018-03-23 12:30:03 +00:00
2019-01-30 15:24:07 +00:00
err := addRuleOnRouter ( subRouter , rule . ruleLeft )
if err != nil {
return err
2016-08-05 18:42:45 +00:00
}
2019-01-30 15:24:07 +00:00
return addRuleOnRouter ( subRouter , rule . ruleRight )
default :
err := checkRule ( rule )
if err != nil {
return err
2016-04-06 11:06:31 +00:00
}
2018-03-23 12:30:03 +00:00
2019-01-30 15:24:07 +00:00
return funcs [ rule . matcher ] ( route , rule . value ... )
}
2016-03-27 00:05:17 +00:00
}
2016-08-05 18:42:45 +00:00
2019-01-30 15:24:07 +00:00
func checkRule ( rule * tree ) error {
if len ( rule . value ) == 0 {
return fmt . Errorf ( "no args for matcher %s" , rule . matcher )
2016-08-05 18:42:45 +00:00
}
2018-03-23 12:30:03 +00:00
2019-01-30 15:24:07 +00:00
for _ , v := range rule . value {
if len ( v ) == 0 {
return fmt . Errorf ( "empty args for matcher %s, %v" , rule . matcher , rule . value )
2018-09-04 15:14:04 +00:00
}
}
2019-01-30 15:24:07 +00:00
return nil
2016-08-05 18:42:45 +00:00
}