2021-01-16 20:41:39 +00:00
|
|
|
package proxy
|
2020-09-02 22:04:12 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/tls"
|
|
|
|
"net"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"os"
|
2020-09-19 09:32:21 +00:00
|
|
|
"strings"
|
2020-09-02 22:04:12 +00:00
|
|
|
|
|
|
|
"github.com/jinzhu/copier"
|
|
|
|
"github.com/justinas/alice"
|
|
|
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/apis/options"
|
|
|
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/middleware"
|
|
|
|
"github.com/oauth2-proxy/oauth2-proxy/pkg/validation"
|
|
|
|
log "github.com/sirupsen/logrus"
|
2021-06-29 14:21:00 +00:00
|
|
|
"goauthentik.io/api"
|
2020-09-02 22:04:12 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
type providerBundle struct {
|
|
|
|
http.Handler
|
|
|
|
|
2021-01-16 20:41:39 +00:00
|
|
|
s *Server
|
|
|
|
proxy *OAuthProxy
|
2020-09-02 22:04:12 +00:00
|
|
|
Host string
|
|
|
|
|
2021-07-01 13:48:56 +00:00
|
|
|
endSessionUrl string
|
2021-08-29 19:13:28 +00:00
|
|
|
Mode *api.ProxyMode
|
2021-07-01 13:48:56 +00:00
|
|
|
|
2020-09-02 22:04:12 +00:00
|
|
|
cert *tls.Certificate
|
2021-01-16 20:41:39 +00:00
|
|
|
|
|
|
|
log *log.Entry
|
2020-09-02 22:04:12 +00:00
|
|
|
}
|
|
|
|
|
2021-04-29 16:17:10 +00:00
|
|
|
func intToPointer(i int) *int {
|
|
|
|
return &i
|
|
|
|
}
|
|
|
|
|
2021-08-08 13:39:52 +00:00
|
|
|
func (pb *providerBundle) replaceLocal(url string) string {
|
2021-08-11 10:39:23 +00:00
|
|
|
if strings.HasPrefix(url, "http://localhost:8000") {
|
|
|
|
authentikHost, c := pb.s.ak.Outpost.Config["authentik_host"]
|
|
|
|
if !c {
|
|
|
|
pb.log.Warning("Outpost has localhost API Connection but no authentik_host is configured.")
|
|
|
|
return url
|
|
|
|
}
|
|
|
|
f := strings.ReplaceAll(url, "http://localhost:8000", authentikHost.(string))
|
2021-08-08 20:24:10 +00:00
|
|
|
return f
|
|
|
|
}
|
|
|
|
return url
|
2021-08-08 13:39:52 +00:00
|
|
|
}
|
|
|
|
|
2021-05-16 19:07:01 +00:00
|
|
|
func (pb *providerBundle) prepareOpts(provider api.ProxyOutpostConfig) *options.Options {
|
2021-08-29 19:13:28 +00:00
|
|
|
// We need to save the mode in the bundle
|
|
|
|
// Since for the embedded outpost we only switch for fully proxy providers
|
|
|
|
pb.Mode = provider.Mode
|
|
|
|
|
2021-05-16 19:07:01 +00:00
|
|
|
externalHost, err := url.Parse(provider.ExternalHost)
|
2020-09-02 22:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Warning("Failed to parse URL, skipping provider")
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
providerOpts := &options.Options{}
|
2021-04-19 18:43:13 +00:00
|
|
|
err = copier.Copy(&providerOpts, getCommonOptions())
|
|
|
|
if err != nil {
|
|
|
|
log.WithError(err).Warning("Failed to copy options, skipping provider")
|
|
|
|
return nil
|
|
|
|
}
|
2021-05-16 19:07:01 +00:00
|
|
|
providerOpts.ClientID = *provider.ClientId
|
|
|
|
providerOpts.ClientSecret = *provider.ClientSecret
|
2020-09-02 22:04:12 +00:00
|
|
|
|
2021-05-16 19:07:01 +00:00
|
|
|
providerOpts.Cookie.Secret = *provider.CookieSecret
|
2020-09-02 22:04:12 +00:00
|
|
|
providerOpts.Cookie.Secure = externalHost.Scheme == "https"
|
|
|
|
|
|
|
|
providerOpts.SkipOIDCDiscovery = true
|
2021-08-08 13:39:52 +00:00
|
|
|
providerOpts.OIDCIssuerURL = pb.replaceLocal(provider.OidcConfiguration.Issuer)
|
|
|
|
providerOpts.LoginURL = pb.replaceLocal(provider.OidcConfiguration.AuthorizationEndpoint)
|
|
|
|
providerOpts.RedeemURL = pb.replaceLocal(provider.OidcConfiguration.TokenEndpoint)
|
|
|
|
providerOpts.OIDCJwksURL = pb.replaceLocal(provider.OidcConfiguration.JwksUri)
|
|
|
|
providerOpts.ProfileURL = pb.replaceLocal(provider.OidcConfiguration.UserinfoEndpoint)
|
|
|
|
providerOpts.ValidateURL = pb.replaceLocal(provider.OidcConfiguration.UserinfoEndpoint)
|
2021-07-01 13:42:48 +00:00
|
|
|
providerOpts.AcrValues = "goauthentik.io/providers/oauth2/default"
|
2021-05-16 19:07:01 +00:00
|
|
|
|
|
|
|
if *provider.SkipPathRegex != "" {
|
|
|
|
skipRegexes := strings.Split(*provider.SkipPathRegex, "\n")
|
2020-09-19 19:05:41 +00:00
|
|
|
providerOpts.SkipAuthRegex = skipRegexes
|
|
|
|
}
|
2020-09-19 09:32:21 +00:00
|
|
|
|
2021-06-08 21:10:17 +00:00
|
|
|
if *provider.Mode == api.PROXYMODE_FORWARD_SINGLE || *provider.Mode == api.PROXYMODE_FORWARD_DOMAIN {
|
2021-04-29 16:17:10 +00:00
|
|
|
providerOpts.UpstreamServers = []options.Upstream{
|
|
|
|
{
|
|
|
|
ID: "static",
|
|
|
|
Static: true,
|
|
|
|
StaticCode: intToPointer(202),
|
|
|
|
Path: "/",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
providerOpts.UpstreamServers = []options.Upstream{
|
|
|
|
{
|
|
|
|
ID: "default",
|
2021-05-16 19:07:01 +00:00
|
|
|
URI: *provider.InternalHost,
|
2021-04-29 16:17:10 +00:00
|
|
|
Path: "/",
|
2021-05-16 19:07:01 +00:00
|
|
|
InsecureSkipTLSVerify: !(*provider.InternalHostSslValidation),
|
2021-04-29 16:17:10 +00:00
|
|
|
},
|
|
|
|
}
|
2020-09-02 22:04:12 +00:00
|
|
|
}
|
|
|
|
|
2021-05-16 19:07:01 +00:00
|
|
|
if provider.Certificate.Get() != nil {
|
2021-07-21 21:53:43 +00:00
|
|
|
kp := provider.Certificate.Get()
|
|
|
|
err := pb.s.cs.AddKeypair(*kp)
|
2020-09-02 22:04:12 +00:00
|
|
|
if err != nil {
|
2021-07-21 21:53:43 +00:00
|
|
|
pb.log.WithError(err).Warning("Failed to initially fetch certificate")
|
2020-09-02 22:04:12 +00:00
|
|
|
}
|
2021-07-21 21:53:43 +00:00
|
|
|
pb.cert = pb.s.cs.Get(*kp)
|
2020-09-02 22:04:12 +00:00
|
|
|
}
|
|
|
|
return providerOpts
|
|
|
|
}
|
|
|
|
|
2021-05-16 19:07:01 +00:00
|
|
|
func (pb *providerBundle) Build(provider api.ProxyOutpostConfig) {
|
2020-09-02 22:04:12 +00:00
|
|
|
opts := pb.prepareOpts(provider)
|
|
|
|
|
2021-06-08 21:10:17 +00:00
|
|
|
if *provider.Mode == api.PROXYMODE_FORWARD_DOMAIN {
|
|
|
|
opts.Cookie.Domains = []string{*provider.CookieDomain}
|
|
|
|
}
|
|
|
|
|
2020-09-02 22:04:12 +00:00
|
|
|
chain := alice.New()
|
|
|
|
|
|
|
|
if opts.ForceHTTPS {
|
|
|
|
_, httpsPort, err := net.SplitHostPort(opts.HTTPSAddress)
|
|
|
|
if err != nil {
|
|
|
|
log.Fatalf("FATAL: invalid HTTPS address %q: %v", opts.HTTPAddress, err)
|
|
|
|
}
|
|
|
|
chain = chain.Append(middleware.NewRedirectToHTTPS(httpsPort))
|
|
|
|
}
|
|
|
|
|
|
|
|
healthCheckPaths := []string{opts.PingPath}
|
|
|
|
healthCheckUserAgents := []string{opts.PingUserAgent}
|
|
|
|
|
|
|
|
// To silence logging of health checks, register the health check handler before
|
|
|
|
// the logging handler
|
|
|
|
if opts.Logging.SilencePing {
|
|
|
|
chain = chain.Append(middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents), LoggingHandler)
|
|
|
|
} else {
|
|
|
|
chain = chain.Append(LoggingHandler, middleware.NewHealthCheck(healthCheckPaths, healthCheckUserAgents))
|
|
|
|
}
|
|
|
|
|
|
|
|
err := validation.Validate(opts)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("%s", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
2021-05-23 17:27:08 +00:00
|
|
|
oauthproxy, err := NewOAuthProxy(opts, provider, pb.s.ak.Client.GetConfig().HTTPClient)
|
2020-09-02 22:04:12 +00:00
|
|
|
if err != nil {
|
|
|
|
log.Errorf("ERROR: Failed to initialise OAuth2 Proxy: %v", err)
|
|
|
|
os.Exit(1)
|
|
|
|
}
|
|
|
|
|
2021-05-16 19:07:01 +00:00
|
|
|
if *provider.BasicAuthEnabled {
|
2020-09-30 09:49:06 +00:00
|
|
|
oauthproxy.SetBasicAuth = true
|
2021-05-16 19:07:01 +00:00
|
|
|
oauthproxy.BasicAuthUserAttribute = *provider.BasicAuthUserAttribute
|
|
|
|
oauthproxy.BasicAuthPasswordAttribute = *provider.BasicAuthPasswordAttribute
|
2020-09-30 09:49:06 +00:00
|
|
|
}
|
|
|
|
|
2021-07-01 13:48:56 +00:00
|
|
|
oauthproxy.endSessionEndpoint = pb.endSessionUrl
|
2021-06-08 21:10:17 +00:00
|
|
|
oauthproxy.ExternalHost = pb.Host
|
|
|
|
|
2020-09-02 22:04:12 +00:00
|
|
|
pb.proxy = oauthproxy
|
|
|
|
pb.Handler = chain.Then(oauthproxy)
|
|
|
|
}
|