Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 17 additions & 10 deletions cmd/bridge/config/auth/authoptions.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (

"github.com/openshift/console/cmd/bridge/config/session"
"github.com/openshift/console/pkg/auth"
"github.com/openshift/console/pkg/auth/csrfverifier"
oauth2 "github.com/openshift/console/pkg/auth/oauth2"
"github.com/openshift/console/pkg/flags"
"github.com/openshift/console/pkg/proxy"
"github.com/openshift/console/pkg/server"
Expand Down Expand Up @@ -208,25 +210,33 @@ func (c *completedOptions) ApplyTo(
) error {
srv.InactivityTimeout = c.InactivityTimeoutSeconds

useSecureCookies := srv.BaseURL.Scheme == "https"
var err error
srv.Authenticator, err = c.getAuthenticator(
srv.BaseURL,
k8sEndpoint,
caCertFilePath,
srv.InternalProxiedK8SClientConfig,
useSecureCookies,
sessionConfig,
)

return err
if err != nil {
return err
}

srv.CSRFVerifier = csrfverifier.NewCSRFVerifier(srv.BaseURL, useSecureCookies)
return nil
}

func (c *completedOptions) getAuthenticator(
baseURL *url.URL,
k8sEndpoint *url.URL,
caCertFilePath string,
k8sClientConfig *rest.Config,
useSecureCookies bool,
sessionConfig *session.CompletedOptions,
) (*auth.Authenticator, error) {
) (auth.Authenticator, error) {

if c.AuthType == "disabled" {
klog.Warning("running with AUTHENTICATION DISABLED!")
Expand All @@ -242,17 +252,15 @@ func (c *completedOptions) getAuthenticator(
authLoginSuccessEndpoint = proxy.SingleJoiningSlash(baseURL.String(), server.AuthLoginSuccessEndpoint)
oidcClientSecret = c.ClientSecret
// Abstraction leak required by NewAuthenticator. We only want the browser to send the auth token for paths starting with basePath/api.
cookiePath = proxy.SingleJoiningSlash(baseURL.Path, "/api")
refererPath = baseURL.String()
useSecureCookies = baseURL.Scheme == "https"
cookiePath = proxy.SingleJoiningSlash(baseURL.Path, "/api")
)

var scopes []string
authSource := auth.AuthSourceOIDC
authSource := oauth2.AuthSourceOIDC

if c.AuthType == "openshift" {
scopes = []string{"user:full"}
authSource = auth.AuthSourceOpenShift
authSource = oauth2.AuthSourceOpenShift

userAuthOIDCIssuerURL = k8sEndpoint
} else {
Expand All @@ -264,7 +272,7 @@ func (c *completedOptions) getAuthenticator(
oidcClientSecret = c.ClientSecret

// Config for logging into console.
oidcClientConfig := &auth.Config{
oidcClientConfig := &oauth2.Config{
AuthSource: authSource,
IssuerURL: userAuthOIDCIssuerURL.String(),
IssuerCA: c.CAFilePath,
Expand All @@ -282,7 +290,6 @@ func (c *completedOptions) getAuthenticator(
SuccessURL: authLoginSuccessEndpoint,

CookiePath: cookiePath,
RefererPath: refererPath,
SecureCookies: useSecureCookies,
CookieEncryptionKey: sessionConfig.CookieEncryptionKey,
CookieAuthenticationKey: sessionConfig.CookieAuthenticationKey,
Expand All @@ -294,7 +301,7 @@ func (c *completedOptions) getAuthenticator(
oidcClientConfig.LogoutRedirectOverride = c.LogoutRedirectURL.String()
}

authenticator, err := auth.NewAuthenticator(context.Background(), oidcClientConfig)
authenticator, err := oauth2.NewOAuth2Authenticator(context.Background(), oidcClientConfig)
if err != nil {
klog.Fatalf("Error initializing authenticator: %v", err)
}
Expand Down
47 changes: 15 additions & 32 deletions cmd/bridge/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import (
authopts "github.com/openshift/console/cmd/bridge/config/auth"
"github.com/openshift/console/cmd/bridge/config/session"
"github.com/openshift/console/pkg/auth"
"github.com/openshift/console/pkg/auth/static"
"github.com/openshift/console/pkg/flags"
"github.com/openshift/console/pkg/knative"
"github.com/openshift/console/pkg/proxy"
Expand Down Expand Up @@ -87,7 +88,7 @@ func main() {
fK8sModeOffClusterThanos := fs.String("k8s-mode-off-cluster-thanos", "", "DEV ONLY. URL of the cluster's Thanos server.")
fK8sModeOffClusterAlertmanager := fs.String("k8s-mode-off-cluster-alertmanager", "", "DEV ONLY. URL of the cluster's AlertManager server.")

fK8sAuth := fs.String("k8s-auth", "service-account", "service-account | bearer-token | oidc | openshift")
fK8sAuth := fs.String("k8s-auth", "", "this option is deprecated, setting it has no effect")
fK8sAuthBearerToken := fs.String("k8s-auth-bearer-token", "", "Authorization token to send with proxied Kubernetes API requests.")

fK8sModeOffClusterGitOps := fs.String("k8s-mode-off-cluster-gitops", "", "DEV ONLY. URL of the GitOps backend service")
Expand Down Expand Up @@ -308,10 +309,6 @@ func main() {
k8sCertPEM []byte
)

var (
k8sAuthServiceAccountBearerToken string
)

var k8sEndpoint *url.URL
switch *fK8sMode {
case "in-cluster":
Expand All @@ -329,13 +326,9 @@ func main() {
RootCAs: rootCAs,
})

bearerToken, err := ioutil.ReadFile(k8sInClusterBearerToken)
if err != nil {
klog.Fatalf("failed to read bearer token: %v", err)
}

srv.InternalProxiedK8SClientConfig = &rest.Config{
Host: k8sEndpoint.String(),
Host: k8sEndpoint.String(),
BearerTokenFile: k8sInClusterBearerToken,
TLSClientConfig: rest.TLSClientConfig{
CAFile: k8sInClusterCA,
},
Expand All @@ -347,8 +340,6 @@ func main() {
Endpoint: k8sEndpoint,
}

k8sAuthServiceAccountBearerToken = string(bearerToken)

// If running in an OpenShift cluster, set up a proxy to the prometheus-k8s service running in the openshift-monitoring namespace.
if *fServiceCAFile != "" {
serviceCertPEM, err := ioutil.ReadFile(*fServiceCAFile)
Expand Down Expand Up @@ -513,25 +504,8 @@ func main() {
Endpoint: clusterManagementURL,
}

switch *fK8sAuth {
case "service-account":
flags.FatalIfFailed(flags.ValidateFlagIs("k8s-mode", *fK8sMode, "in-cluster"))
srv.StaticUser = &auth.User{
Token: k8sAuthServiceAccountBearerToken, // FIXME: make it read the token from the file and periodically re-read?
}
srv.InternalProxiedK8SClientConfig.BearerTokenFile = k8sInClusterBearerToken
case "bearer-token":
flags.FatalIfFailed(flags.ValidateFlagNotEmpty("k8s-auth-bearer-token", *fK8sAuthBearerToken))

srv.StaticUser = &auth.User{
Token: *fK8sAuthBearerToken,
}
srv.InternalProxiedK8SClientConfig.BearerToken = *fK8sAuthBearerToken
case "oidc", "openshift":
flags.FatalIfFailed(flags.ValidateFlagIs("user-auth", authOptions.AuthType, "oidc", "openshift"))
srv.InternalProxiedK8SClientConfig.BearerTokenFile = k8sInClusterBearerToken
default:
flags.FatalIfFailed(flags.NewInvalidFlagError("k8s-mode", "must be one of: service-account, bearer-token, oidc, openshift"))
if len(*fK8sAuth) > 0 {
klog.Warning("DEPRECATED: --k8s-auth is deprecated and setting it has no effect")
}

internalProxiedK8SRT, err := rest.TransportFor(srv.InternalProxiedK8SClientConfig)
Expand Down Expand Up @@ -587,6 +561,15 @@ func main() {
os.Exit(1)
}

// Setting the --k8s-auth-bearer-token flag will override both the authenticator
// and the internal proxy user
if len(*fK8sAuthBearerToken) > 0 {
srv.Authenticator = static.NewStaticAuthenticator(auth.User{
Token: *fK8sAuthBearerToken,
})
srv.InternalProxiedK8SClientConfig.BearerToken = *fK8sAuthBearerToken
}

listenURL, err := flags.ValidateFlagIsURL("listen", *fListen, false)
flags.FatalIfFailed(err)

Expand Down
109 changes: 109 additions & 0 deletions pkg/auth/csrfverifier/csfr_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package csrfverifier

import (
"net/http"
"net/url"
"testing"

"github.com/stretchr/testify/require"
)

const validReferer string = "https://example.com/asdf/"

func testReferer(t *testing.T, referer string, accept bool) {
refererURL, err := url.Parse(validReferer)
require.NoError(t, err)

a := CSRFVerifier{refererURL: refererURL}

r, err := http.NewRequest("POST", "/some-path", nil)

if err != nil {
t.Fatal(err)
return
}

if len(referer) > 0 {
r.Header.Set("Referer", referer)
}

err = a.verifySourceOrigin(r)

if err != nil && accept {
t.Errorf("Unexpected error for referer `%v`:\n%v", referer, err)
return
}

if err == nil && !accept {
t.Errorf("Unexpected pass for referer: `%v:`\n%v", referer, err)
}

if accept {
t.Logf("referer accepted %v", referer)
} else {
t.Logf("referer rejected %v", referer)
}
}

func TestReferer(t *testing.T) {
testReferer(t, validReferer, true)
testReferer(t, validReferer, true)
testReferer(t, validReferer, true)
testReferer(t, validReferer+"other/path", true)
testReferer(t, validReferer+"?a=b&b=c#33", true)
testReferer(t, "", false)
testReferer(t, "http://example.com/asdf/", false)
testReferer(t, "http://example.com:8000/asdf/", false)
testReferer(t, "https://google.com/asdf/", false)
testReferer(t, "https://example.com/", false)
testReferer(t, "https://example.com/asdff/", false)
testReferer(t, "/asdff/", false)
testReferer(t, "🍆🍆🍆🍆🍆🍆", false)
testReferer(t, "https://google.com/asdf/", false)
}

func testCSRF(t *testing.T, token string, cookie string, accept bool) {
a := CSRFVerifier{secureCookies: false}

r, err := http.NewRequest("POST", "/some-path", nil)

if err != nil {
t.Fatal(err)
return
}

if len(cookie) > 0 {
r.Header.Set(CSRFHeader, token)
r.AddCookie(&http.Cookie{
Name: CSRFCookieName,
Value: cookie,
MaxAge: 1000000,
HttpOnly: true,
Path: "/",
})
}

err = a.verifyCSRFToken(r)

if err != nil && accept {
t.Errorf("Unexpected error for CSRF `%v//%v`:\n%v", token, cookie, err)
return
}

if err == nil && !accept {
t.Errorf("Unexpected pass for CSRF `%v//%v`:\n%v", token, cookie, err)
}

if accept {
t.Logf("CSRF accepted `%v` / `%v`", cookie, token)
} else {
t.Logf("CSRF rejected `%v` / `%v`", cookie, token)
}
}
func TestCSRF(t *testing.T) {
testCSRF(t, "a", "a", true)
testCSRF(t, "a", "b", false)
testCSRF(t, "a", "", false)
testCSRF(t, "", "b", false)
testCSRF(t, "", "", false)
}
Loading