mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2026-10-08 15:41:20 +02:00
Merge pull request #1286 from instadeepai/allowed_email_domains-on-auth_request-endpoint
Add allowed_email_domains on auth_request endpoint
This commit is contained in:
@@ -124,7 +124,7 @@ func NewFlagSet() *pflag.FlagSet {
|
||||
flagSet.StringSlice("extra-jwt-issuers", []string{}, "if skip-jwt-bearer-tokens is set, a list of extra JWT issuer=audience pairs (where the issuer URL has a .well-known/openid-configuration or a .well-known/jwks.json)")
|
||||
|
||||
flagSet.StringSlice("email-domain", []string{}, "authenticate emails with the specified domain (may be given multiple times). Use * to authenticate any email")
|
||||
flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . to allow subdomains (eg .example.com)")
|
||||
flagSet.StringSlice("whitelist-domain", []string{}, "allowed domains for redirection after authentication. Prefix domain with a . or a *. to allow subdomains (eg .example.com, *.example.com)")
|
||||
flagSet.String("authenticated-emails-file", "", "authenticate against emails via file (one per line)")
|
||||
flagSet.String("htpasswd-file", "", "additionally authenticate against a htpasswd file. Entries must be created with \"htpasswd -B\" for bcrypt encryption")
|
||||
flagSet.StringSlice("htpasswd-user-group", []string{}, "the groups to be set on sessions for htpasswd users (may be given multiple times)")
|
||||
|
||||
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||
|
||||
util "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -50,28 +52,9 @@ func (v *validator) IsValidRedirect(redirect string) bool {
|
||||
logger.Printf("Rejecting invalid redirect %q: scheme unsupported or missing", redirect)
|
||||
return false
|
||||
}
|
||||
redirectHostname := redirectURL.Hostname()
|
||||
|
||||
for _, allowedDomain := range v.allowedDomains {
|
||||
allowedHost, allowedPort := splitHostPort(allowedDomain)
|
||||
if allowedHost == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if redirectHostname == strings.TrimPrefix(allowedHost, ".") ||
|
||||
(strings.HasPrefix(allowedHost, ".") &&
|
||||
strings.HasSuffix(redirectHostname, allowedHost)) {
|
||||
// the domain names match, now validate the ports
|
||||
// if the whitelisted domain's port is '*', allow all ports
|
||||
// if the whitelisted domain contains a specific port, only allow that port
|
||||
// if the whitelisted domain doesn't contain a port at all, only allow empty redirect ports ie http and https
|
||||
redirectPort := redirectURL.Port()
|
||||
if allowedPort == "*" ||
|
||||
allowedPort == redirectPort ||
|
||||
(allowedPort == "" && redirectPort == "") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
if util.IsEndpointAllowed(redirectURL, v.allowedDomains) {
|
||||
return true
|
||||
}
|
||||
|
||||
logger.Printf("Rejecting invalid redirect %q: domain / port not in whitelist", redirect)
|
||||
@@ -81,40 +64,3 @@ func (v *validator) IsValidRedirect(redirect string) bool {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// splitHostPort separates host and port. If the port is not valid, it returns
|
||||
// the entire input as host, and it doesn't check the validity of the host.
|
||||
// Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.
|
||||
// *** taken from net/url, modified validOptionalPort() to accept ":*"
|
||||
func splitHostPort(hostport string) (host, port string) {
|
||||
host = hostport
|
||||
|
||||
colon := strings.LastIndexByte(host, ':')
|
||||
if colon != -1 && validOptionalPort(host[colon:]) {
|
||||
host, port = host[:colon], host[colon+1:]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = host[1 : len(host)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validOptionalPort reports whether port is either an empty string
|
||||
// or matches /^:\d*$/
|
||||
// *** taken from net/url, modified to accept ":*"
|
||||
func validOptionalPort(port string) bool {
|
||||
if port == "" || port == ":*" {
|
||||
return true
|
||||
}
|
||||
if port[0] != ':' {
|
||||
return false
|
||||
}
|
||||
for _, b := range port[1:] {
|
||||
if b < '0' || b > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/extensions/table"
|
||||
. "github.com/onsi/gomega"
|
||||
@@ -22,6 +23,10 @@ var _ = Describe("Validator suite", func() {
|
||||
"anyport.bar:*",
|
||||
".sub.anyport.bar:*",
|
||||
"www.whitelisteddomain.tld",
|
||||
"*.wildcard.sub.port.bar:8080",
|
||||
"*.wildcard.sub.anyport.bar:*",
|
||||
"*.wildcard.bar",
|
||||
"*.wildcard.proxy.foo.bar",
|
||||
}
|
||||
})
|
||||
|
||||
@@ -96,7 +101,20 @@ var _ = Describe("Validator suite", func() {
|
||||
Entry("Quad Tab 2", "/\t\t\\\t\t/evil.com", false),
|
||||
Entry("Relative Path", "/./\\evil.com", false),
|
||||
Entry("Relative Subpath", "/./../../\\evil.com", false),
|
||||
Entry("Partial Subdomain", "evilbar.foo", false),
|
||||
Entry("Valid HTTP Wildcard Subdomain", "http://foo.wildcard.bar/redirect", true),
|
||||
Entry("Valid HTTPS Wildcard Subdomain", "https://foo.wildcard.bar/redirect", true),
|
||||
Entry("Valid HTTP Wildcard Subdomain Root", "http://wildcard.bar/redirect", true),
|
||||
Entry("Valid HTTPS Wildcard Subdomain Root", "https://wildcard.bar/redirect", true),
|
||||
Entry("Valid HTTP Wildcard Subdomain anyport", "http://foo.wildcard.sub.anyport.bar:4242/redirect", true),
|
||||
Entry("Valid HTTPS Wildcard Subdomain anyport", "https://foo.wildcard.sub.anyport.bar:4242/redirect", true),
|
||||
Entry("Valid HTTP Wildcard Subdomain Anyport Root", "http://wildcard.sub.anyport.bar:4242/redirect", true),
|
||||
Entry("Valid HTTPS Wildcard Subdomain Anyport Root", "https://wildcard.sub.anyport.bar:4242/redirect", true),
|
||||
Entry("Valid HTTP Wildcard Subdomain Defined Port", "http://foo.wildcard.sub.port.bar:8080/redirect", true),
|
||||
Entry("Valid HTTPS Wildcard Subdomain Defined Port", "https://foo.wildcard.sub.port.bar:8080/redirect", true),
|
||||
Entry("Valid HTTP Wildcard Subdomain Defined Port Root", "http://wildcard.sub.port.bar:8080/redirect", true),
|
||||
Entry("Valid HTTPS Wildcard Subdomain Defined Port Root", "https://wildcard.sub.port.bar:8080/redirect", true),
|
||||
Entry("Missing Protocol Root Domain", "foo.bar/redirect", false),
|
||||
Entry("Missing Protocol Wildcard Subdomain", "proxy.wildcard.bar/redirect", false),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -109,7 +127,7 @@ var _ = Describe("Validator suite", func() {
|
||||
|
||||
DescribeTable("Should split the host and port",
|
||||
func(in splitHostPortTableInput) {
|
||||
host, port := splitHostPort(in.hostport)
|
||||
host, port := util.SplitHostPort(in.hostport)
|
||||
Expect(host).To(Equal(in.expectedHost))
|
||||
Expect(port).To(Equal(in.expectedPort))
|
||||
},
|
||||
|
||||
@@ -9,6 +9,8 @@ import (
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"net"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -66,3 +68,84 @@ func GenerateCert() ([]byte, []byte, error) {
|
||||
certBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
|
||||
return certBytes, keyBytes, err
|
||||
}
|
||||
|
||||
// SplitHostPort separates host and port. If the port is not valid, it returns
|
||||
// the entire input as host, and it doesn't check the validity of the host.
|
||||
// Unlike net.SplitHostPort, but per RFC 3986, it requires ports to be numeric.
|
||||
// *** taken from net/url, modified validOptionalPort() to accept ":*"
|
||||
func SplitHostPort(hostport string) (host, port string) {
|
||||
host = hostport
|
||||
|
||||
colon := strings.LastIndexByte(host, ':')
|
||||
if colon != -1 && validOptionalPort(host[colon:]) {
|
||||
host, port = host[:colon], host[colon+1:]
|
||||
}
|
||||
|
||||
if strings.HasPrefix(host, "[") && strings.HasSuffix(host, "]") {
|
||||
host = host[1 : len(host)-1]
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// validOptionalPort reports whether port is either an empty string
|
||||
// or matches /^:\d*$/
|
||||
// *** taken from net/url, modified to accept ":*"
|
||||
func validOptionalPort(port string) bool {
|
||||
if port == "" || port == ":*" {
|
||||
return true
|
||||
}
|
||||
if port[0] != ':' {
|
||||
return false
|
||||
}
|
||||
for _, b := range port[1:] {
|
||||
if b < '0' || b > '9' {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// IsEndpointAllowed checks whether the endpoint URL is allowed based
|
||||
// on an allowed domains list.
|
||||
func IsEndpointAllowed(endpoint *url.URL, allowedDomains []string) bool {
|
||||
hostname := endpoint.Hostname()
|
||||
|
||||
for _, allowedDomain := range allowedDomains {
|
||||
allowedHost, allowedPort := SplitHostPort(allowedDomain)
|
||||
if allowedHost == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
if isHostnameAllowed(hostname, allowedHost) {
|
||||
// the domain names match, now validate the ports
|
||||
// if the allowed domain's port is '*', allow all ports
|
||||
// if the allowed domain contains a specific port, only allow that port
|
||||
// if the allowed domain doesn't contain a port at all, only allow empty redirect ports ie http and https
|
||||
redirectPort := endpoint.Port()
|
||||
if allowedPort == "*" ||
|
||||
allowedPort == redirectPort ||
|
||||
(allowedPort == "" && redirectPort == "") {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func isHostnameAllowed(hostname, allowedHost string) bool {
|
||||
// check if we have a perfect match between hostname and allowedHost
|
||||
if hostname == strings.TrimPrefix(allowedHost, ".") ||
|
||||
hostname == strings.TrimPrefix(allowedHost, "*.") {
|
||||
return true
|
||||
}
|
||||
|
||||
// check if hostname is a sub domain of the allowedHost
|
||||
if (strings.HasPrefix(allowedHost, ".") && strings.HasSuffix(hostname, allowedHost)) ||
|
||||
(strings.HasPrefix(allowedHost, "*.") && strings.HasSuffix(hostname, allowedHost[1:])) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user