outposts: set cookies for a domain to authenticate an entire domain (#971)

* outposts: initial cookie domain implementation

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: add cookie domain setting

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/proxy: replace forward_auth_mode with general mode

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: rebuild proxy provider form

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/proxy: re-add forward_auth_mode for backwards compat

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: fix data.mode not being set

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* root: always set log level to debug when testing

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/proxy: use new mode attribute

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/proxy: only ingress /akprox on forward_domain

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* providers/proxy: fix lint error

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: fix error on ProxyProviderForm when not using proxy mode

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: fix default for outpost form's type missing

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: add additional desc for proxy modes

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts: fix service account permissions not always being updated

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outpost/proxy: fix redirecting to incorrect host for domain mode

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: improve error handling for network errors

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outpost: fix image naming not matching main imaeg

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/proxy: fix redirects for domain mode and traefik

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: fix colour for paragraphs

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/flows: fix consent stage not showing permissions correctly

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* website/docs: add domain-level docs

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* website/docs: fix broken links

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* outposts/proxy: remove dead code

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/flows: fix missing id for #header-text

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
This commit is contained in:
Jens L
2021-06-08 23:10:17 +02:00
committed by GitHub
parent fb8d67a9d9
commit dad24c03ff
27 changed files with 473 additions and 232 deletions

View File

@ -65,7 +65,7 @@ type OAuthProxy struct {
AuthOnlyPath string
UserInfoPath string
forwardAuthMode bool
mode api.ProxyMode
redirectURL *url.URL // the url to receive requests at
whitelistDomains []string
provider providers.Provider
@ -77,6 +77,7 @@ type OAuthProxy struct {
PassUserHeaders bool
BasicAuthUserAttribute string
BasicAuthPasswordAttribute string
ExternalHost string
PassAccessToken bool
SetAuthorization bool
PassAuthorization bool
@ -136,7 +137,7 @@ func NewOAuthProxy(opts *options.Options, provider api.ProxyOutpostConfig, c *ht
CookieRefresh: opts.Cookie.Refresh,
CookieSameSite: opts.Cookie.SameSite,
forwardAuthMode: *provider.ForwardAuthMode,
mode: *provider.Mode,
RobotsPath: "/robots.txt",
SignInPath: fmt.Sprintf("%s/sign_in", opts.ProxyPrefix),
SignOutPath: fmt.Sprintf("%s/sign_out", opts.ProxyPrefix),
@ -216,43 +217,6 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, code int, title string, m
}
}
// 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
}
// See https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching?hl=en
var noCacheHeaders = map[string]string{
"Expires": time.Unix(0, 0).Format(time.RFC1123),
@ -340,18 +304,41 @@ func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request) {
session, err := p.getAuthenticatedSession(rw, req)
if err != nil {
if p.forwardAuthMode {
if p.mode == api.PROXYMODE_FORWARD_SINGLE || p.mode == api.PROXYMODE_FORWARD_DOMAIN {
if _, ok := req.URL.Query()["nginx"]; ok {
rw.WriteHeader(401)
return
}
if _, ok := req.URL.Query()["traefik"]; ok {
host := getHost(req)
host := ""
// Optional suffix, which is appended to the URL
suffix := ""
if p.mode == api.PROXYMODE_FORWARD_SINGLE {
host = getHost(req)
} else if p.mode == api.PROXYMODE_FORWARD_DOMAIN {
host = p.ExternalHost
// set the ?rd flag to the current URL we have, since we redirect
// to a (possibly) different domain, but we want to be redirected back
// to the application
v := url.Values{
// see https://doc.traefik.io/traefik/middlewares/forwardauth/
// X-Forwarded-Uri is only the path, so we need to build the entire URL
"rd": []string{fmt.Sprintf(
"%s://%s%s",
req.Header.Get("X-Forwarded-Proto"),
req.Header.Get("X-Forwarded-Host"),
req.Header.Get("X-Forwarded-Uri"),
)},
}
suffix = fmt.Sprintf("?%s", v.Encode())
}
proto := req.Header.Get("X-Forwarded-Proto")
if proto != "" {
proto = proto + ":"
}
http.Redirect(rw, req, fmt.Sprintf("%s//%s%s", proto, host, p.OAuthStartPath), http.StatusTemporaryRedirect)
rdFinal := fmt.Sprintf("%s//%s%s%s", proto, host, p.OAuthStartPath, suffix)
p.logger.WithField("url", rdFinal).Debug("Redirecting to login")
http.Redirect(rw, req, rdFinal, http.StatusTemporaryRedirect)
return
}
}
@ -360,7 +347,7 @@ func (p *OAuthProxy) AuthenticateOnly(rw http.ResponseWriter, req *http.Request)
}
// we are authenticated
p.addHeadersForProxying(rw, req, session)
if p.forwardAuthMode {
if p.mode == api.PROXYMODE_FORWARD_SINGLE || p.mode == api.PROXYMODE_FORWARD_DOMAIN {
for headerKey, headers := range req.Header {
for _, value := range headers {
rw.Header().Set(headerKey, value)