2014-11-09 21:51:10 +02:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2017-05-09 20:20:35 +02:00
|
|
|
"context"
|
2015-11-16 05:08:30 +02:00
|
|
|
"crypto"
|
2017-03-29 16:57:07 +02:00
|
|
|
"crypto/tls"
|
2016-06-20 13:17:39 +02:00
|
|
|
"encoding/base64"
|
2014-11-09 21:51:10 +02:00
|
|
|
"fmt"
|
2016-07-19 21:51:25 +02:00
|
|
|
"net/http"
|
2014-11-09 21:51:10 +02:00
|
|
|
"net/url"
|
2015-08-20 12:07:02 +02:00
|
|
|
"os"
|
2015-01-12 11:18:41 +02:00
|
|
|
"regexp"
|
2015-03-15 18:23:13 +02:00
|
|
|
"strings"
|
2015-01-19 18:10:37 +02:00
|
|
|
"time"
|
2015-03-30 21:48:30 +02:00
|
|
|
|
2017-05-09 20:20:35 +02:00
|
|
|
oidc "github.com/coreos/go-oidc"
|
2019-03-20 15:44:51 +02:00
|
|
|
"github.com/dgrijalva/jwt-go"
|
2017-09-13 00:59:00 +02:00
|
|
|
"github.com/mbland/hmacauth"
|
2018-11-29 16:26:41 +02:00
|
|
|
"github.com/pusher/oauth2_proxy/providers"
|
2014-11-09 21:51:10 +02:00
|
|
|
)
|
|
|
|
|
2018-11-29 16:26:41 +02:00
|
|
|
// Options holds Configuration Options that can be set by Command Line Flag,
|
|
|
|
// or Config File
|
2014-11-09 21:51:10 +02:00
|
|
|
type Options struct {
|
2019-03-20 15:44:51 +02:00
|
|
|
ProxyPrefix string `flag:"proxy-prefix" cfg:"proxy-prefix" env:"OAUTH2_PROXY_PROXY_PREFIX"`
|
|
|
|
ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets" env:"OAUTH2_PROXY_PROXY_WEBSOCKETS"`
|
|
|
|
HTTPAddress string `flag:"http-address" cfg:"http_address" env:"OAUTH2_PROXY_HTTP_ADDRESS"`
|
|
|
|
HTTPSAddress string `flag:"https-address" cfg:"https_address" env:"OAUTH2_PROXY_HTTPS_ADDRESS"`
|
|
|
|
RedirectURL string `flag:"redirect-url" cfg:"redirect_url" env:"OAUTH2_PROXY_REDIRECT_URL"`
|
2019-03-08 10:15:21 +02:00
|
|
|
ClientID string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
|
|
|
|
ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
|
2019-03-20 15:44:51 +02:00
|
|
|
TLSCertFile string `flag:"tls-cert" cfg:"tls_cert_file" env:"OAUTH2_PROXY_TLS_CERT_FILE"`
|
|
|
|
TLSKeyFile string `flag:"tls-key" cfg:"tls_key_file" env:"OAUTH2_PROXY_TLS_KEY_FILE"`
|
2015-03-17 21:15:15 +02:00
|
|
|
|
2019-03-20 15:44:51 +02:00
|
|
|
AuthenticatedEmailsFile string `flag:"authenticated-emails-file" cfg:"authenticated_emails_file" env:"OAUTH2_PROXY_AUTHENTICATED_EMAILS_FILE"`
|
|
|
|
AzureTenant string `flag:"azure-tenant" cfg:"azure_tenant" env:"OAUTH2_PROXY_AZURE_TENANT"`
|
|
|
|
EmailDomains []string `flag:"email-domain" cfg:"email_domains" env:"OAUTH2_PROXY_EMAIL_DOMAINS"`
|
2018-01-18 12:20:50 +02:00
|
|
|
WhitelistDomains []string `flag:"whitelist-domain" cfg:"whitelist_domains" env:"OAUTH2_PROXY_WHITELIST_DOMAINS"`
|
2019-03-20 15:44:51 +02:00
|
|
|
GitHubOrg string `flag:"github-org" cfg:"github_org" env:"OAUTH2_PROXY_GITHUB_ORG"`
|
|
|
|
GitHubTeam string `flag:"github-team" cfg:"github_team" env:"OAUTH2_PROXY_GITHUB_TEAM"`
|
|
|
|
GoogleGroups []string `flag:"google-group" cfg:"google_group" env:"OAUTH2_PROXY_GOOGLE_GROUPS"`
|
|
|
|
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email" env:"OAUTH2_PROXY_GOOGLE_ADMIN_EMAIL"`
|
|
|
|
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json" env:"OAUTH2_PROXY_GOOGLE_SERVICE_ACCOUNT_JSON"`
|
|
|
|
HtpasswdFile string `flag:"htpasswd-file" cfg:"htpasswd_file" env:"OAUTH2_PROXY_HTPASSWD_FILE"`
|
|
|
|
DisplayHtpasswdForm bool `flag:"display-htpasswd-form" cfg:"display_htpasswd_form" env:"OAUTH2_PROXY_DISPLAY_HTPASSWD_FORM"`
|
|
|
|
CustomTemplatesDir string `flag:"custom-templates-dir" cfg:"custom_templates_dir" env:"OAUTH2_PROXY_CUSTOM_TEMPLATES_DIR"`
|
|
|
|
Footer string `flag:"footer" cfg:"footer" env:"OAUTH2_PROXY_FOOTER"`
|
2015-03-17 21:15:15 +02:00
|
|
|
|
2015-06-08 05:52:28 +02:00
|
|
|
CookieName string `flag:"cookie-name" cfg:"cookie_name" env:"OAUTH2_PROXY_COOKIE_NAME"`
|
|
|
|
CookieSecret string `flag:"cookie-secret" cfg:"cookie_secret" env:"OAUTH2_PROXY_COOKIE_SECRET"`
|
|
|
|
CookieDomain string `flag:"cookie-domain" cfg:"cookie_domain" env:"OAUTH2_PROXY_COOKIE_DOMAIN"`
|
|
|
|
CookieExpire time.Duration `flag:"cookie-expire" cfg:"cookie_expire" env:"OAUTH2_PROXY_COOKIE_EXPIRE"`
|
|
|
|
CookieRefresh time.Duration `flag:"cookie-refresh" cfg:"cookie_refresh" env:"OAUTH2_PROXY_COOKIE_REFRESH"`
|
2019-03-20 15:44:51 +02:00
|
|
|
CookieSecure bool `flag:"cookie-secure" cfg:"cookie_secure" env:"OAUTH2_PROXY_COOKIE_SECURE"`
|
|
|
|
CookieHTTPOnly bool `flag:"cookie-httponly" cfg:"cookie_httponly" env:"OAUTH2_PROXY_COOKIE_HTTPONLY"`
|
|
|
|
|
|
|
|
Upstreams []string `flag:"upstream" cfg:"upstreams" env:"OAUTH2_PROXY_UPSTREAMS"`
|
|
|
|
SkipAuthRegex []string `flag:"skip-auth-regex" cfg:"skip_auth_regex" env:"OAUTH2_PROXY_SKIP_AUTH_REGEX"`
|
|
|
|
PassBasicAuth bool `flag:"pass-basic-auth" cfg:"pass_basic_auth" env:"OAUTH2_PROXY_PASS_BASIC_AUTH"`
|
|
|
|
BasicAuthPassword string `flag:"basic-auth-password" cfg:"basic_auth_password" env:"OAUTH2_PROXY_BASIC_AUTH_PASSWORD"`
|
|
|
|
PassAccessToken bool `flag:"pass-access-token" cfg:"pass_access_token" env:"OAUTH2_PROXY_PASS_ACCESS_TOKEN"`
|
|
|
|
PassHostHeader bool `flag:"pass-host-header" cfg:"pass_host_header" env:"OAUTH2_PROXY_PASS_HOST_HEADER"`
|
|
|
|
SkipProviderButton bool `flag:"skip-provider-button" cfg:"skip_provider_button" env:"OAUTH2_PROXY_SKIP_PROVIDER_BUTTON"`
|
|
|
|
PassUserHeaders bool `flag:"pass-user-headers" cfg:"pass_user_headers" env:"OAUTH2_PROXY_PASS_USER_HEADERS"`
|
|
|
|
SSLInsecureSkipVerify bool `flag:"ssl-insecure-skip-verify" cfg:"ssl_insecure_skip_verify" env:"OAUTH2_PROXY_SSL_INSECURE_SKIP_VERIFY"`
|
|
|
|
SetXAuthRequest bool `flag:"set-xauthrequest" cfg:"set_xauthrequest" env:"OAUTH2_PROXY_SET_XAUTHREQUEST"`
|
|
|
|
SetAuthorization bool `flag:"set-authorization-header" cfg:"set_authorization_header" env:"OAUTH2_PROXY_SET_AUTHORIZATION_HEADER"`
|
|
|
|
PassAuthorization bool `flag:"pass-authorization-header" cfg:"pass_authorization_header" env:"OAUTH2_PROXY_PASS_AUTHORIZATION_HEADER"`
|
|
|
|
SkipAuthPreflight bool `flag:"skip-auth-preflight" cfg:"skip_auth_preflight" env:"OAUTH2_PROXY_SKIP_AUTH_PREFLIGHT"`
|
|
|
|
FlushInterval time.Duration `flag:"flush-interval" cfg:"flush_interval" env:"OAUTH2_PROXY_FLUSH_INTERVAL"`
|
2014-11-09 21:51:10 +02:00
|
|
|
|
2015-03-30 21:48:30 +02:00
|
|
|
// These options allow for other providers besides Google, with
|
|
|
|
// potential overrides.
|
2019-03-20 15:44:51 +02:00
|
|
|
Provider string `flag:"provider" cfg:"provider" env:"OAUTH2_PROXY_PROVIDER"`
|
|
|
|
OIDCIssuerURL string `flag:"oidc-issuer-url" cfg:"oidc_issuer_url" env:"OAUTH2_PROXY_OIDC_ISSUER_URL"`
|
|
|
|
SkipOIDCDiscovery bool `flag:"skip-oidc-discovery" cfg:"skip_oidc_discovery" env:"OAUTH2_SKIP_OIDC_DISCOVERY"`
|
|
|
|
OIDCJwksURL string `flag:"oidc-jwks-url" cfg:"oidc_jwks_url" env:"OAUTH2_OIDC_JWKS_URL"`
|
|
|
|
LoginURL string `flag:"login-url" cfg:"login_url" env:"OAUTH2_PROXY_LOGIN_URL"`
|
|
|
|
RedeemURL string `flag:"redeem-url" cfg:"redeem_url" env:"OAUTH2_PROXY_REDEEM_URL"`
|
|
|
|
ProfileURL string `flag:"profile-url" cfg:"profile_url" env:"OAUTH2_PROXY_PROFILE_URL"`
|
|
|
|
ProtectedResource string `flag:"resource" cfg:"resource" env:"OAUTH2_PROXY_RESOURCE"`
|
|
|
|
ValidateURL string `flag:"validate-url" cfg:"validate_url" env:"OAUTH2_PROXY_VALIDATE_URL"`
|
|
|
|
Scope string `flag:"scope" cfg:"scope" env:"OAUTH2_PROXY_SCOPE"`
|
|
|
|
ApprovalPrompt string `flag:"approval-prompt" cfg:"approval_prompt" env:"OAUTH2_PROXY_APPROVAL_PROMPT"`
|
|
|
|
|
|
|
|
RequestLogging bool `flag:"request-logging" cfg:"request_logging" env:"OAUTH2_PROXY_REQUEST_LOGGING"`
|
|
|
|
RequestLoggingFormat string `flag:"request-logging-format" cfg:"request_logging_format" env:"OAUTH2_PROXY_REQUEST_LOGGING_FORMAT"`
|
2015-03-19 22:37:16 +02:00
|
|
|
|
2019-03-20 23:29:44 +02:00
|
|
|
SignatureKey string `flag:"signature-key" cfg:"signature_key" env:"OAUTH2_PROXY_SIGNATURE_KEY"`
|
|
|
|
AcrValues string `flag:"acr-values" cfg:"acr_values" env:"OAUTH2_PROXY_ACR_VALUES"`
|
|
|
|
JWTKey string `flag:"jwt-key" cfg:"jwt_key" env:"OAUTH2_PROXY_JWT_KEY"`
|
|
|
|
PubJWKURL string `flag:"pubjwk-url" cfg:"pubjwk_url" env:"OAUTH2_PROXY_PUBJWK_URL"`
|
|
|
|
GCPHealthChecks bool `flag:"gcp-healthchecks" cfg:"gcp_healthchecks" env:"OAUTH2_PROXY_GCP_HEALTHCHECKS"`
|
2015-11-16 05:08:30 +02:00
|
|
|
|
2014-11-09 21:51:10 +02:00
|
|
|
// internal values that are set after config validation
|
2015-11-09 01:47:44 +02:00
|
|
|
redirectURL *url.URL
|
|
|
|
proxyURLs []*url.URL
|
2015-01-12 11:18:41 +02:00
|
|
|
CompiledRegex []*regexp.Regexp
|
2015-03-30 21:48:30 +02:00
|
|
|
provider providers.Provider
|
2015-11-16 05:08:30 +02:00
|
|
|
signatureData *SignatureData
|
2017-05-09 20:20:35 +02:00
|
|
|
oidcVerifier *oidc.IDTokenVerifier
|
2015-11-16 05:08:30 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:30:42 +02:00
|
|
|
// SignatureData holds hmacauth signature hash and key
|
2015-11-16 05:08:30 +02:00
|
|
|
type SignatureData struct {
|
|
|
|
hash crypto.Hash
|
|
|
|
key string
|
2014-11-09 21:51:10 +02:00
|
|
|
}
|
|
|
|
|
2018-12-20 11:30:42 +02:00
|
|
|
// NewOptions constructs a new Options with defaulted values
|
2014-11-09 21:51:10 +02:00
|
|
|
func NewOptions() *Options {
|
2014-11-10 05:21:46 +02:00
|
|
|
return &Options{
|
2017-07-14 13:08:34 +02:00
|
|
|
ProxyPrefix: "/oauth2",
|
2019-03-08 10:15:21 +02:00
|
|
|
ProxyWebSockets: true,
|
2018-11-29 16:26:41 +02:00
|
|
|
HTTPAddress: "127.0.0.1:4180",
|
|
|
|
HTTPSAddress: ":443",
|
2017-07-14 13:08:34 +02:00
|
|
|
DisplayHtpasswdForm: true,
|
|
|
|
CookieName: "_oauth2_proxy",
|
|
|
|
CookieSecure: true,
|
2018-11-29 16:26:41 +02:00
|
|
|
CookieHTTPOnly: true,
|
2017-07-14 13:08:34 +02:00
|
|
|
CookieExpire: time.Duration(168) * time.Hour,
|
|
|
|
CookieRefresh: time.Duration(0),
|
|
|
|
SetXAuthRequest: false,
|
|
|
|
SkipAuthPreflight: false,
|
|
|
|
PassBasicAuth: true,
|
|
|
|
PassUserHeaders: true,
|
|
|
|
PassAccessToken: false,
|
|
|
|
PassHostHeader: true,
|
2018-01-27 12:14:19 +02:00
|
|
|
SetAuthorization: false,
|
|
|
|
PassAuthorization: false,
|
2017-07-14 13:08:34 +02:00
|
|
|
ApprovalPrompt: "force",
|
|
|
|
RequestLogging: true,
|
2019-03-04 15:54:22 +02:00
|
|
|
SkipOIDCDiscovery: false,
|
2017-07-14 13:08:34 +02:00
|
|
|
RequestLoggingFormat: defaultRequestLoggingFormat,
|
2014-11-10 05:21:46 +02:00
|
|
|
}
|
2014-11-09 21:51:10 +02:00
|
|
|
}
|
|
|
|
|
2018-11-29 16:26:41 +02:00
|
|
|
func parseURL(toParse string, urltype string, msgs []string) (*url.URL, []string) {
|
|
|
|
parsed, err := url.Parse(toParse)
|
2015-03-30 21:48:30 +02:00
|
|
|
if err != nil {
|
|
|
|
return nil, append(msgs, fmt.Sprintf(
|
2018-11-29 16:26:41 +02:00
|
|
|
"error parsing %s-url=%q %s", urltype, toParse, err))
|
2015-03-30 21:48:30 +02:00
|
|
|
}
|
|
|
|
return parsed, msgs
|
|
|
|
}
|
|
|
|
|
2018-12-20 11:30:42 +02:00
|
|
|
// Validate checks that required options are set and validates those that they
|
|
|
|
// are of the correct format
|
2014-11-09 21:51:10 +02:00
|
|
|
func (o *Options) Validate() error {
|
2017-05-09 20:20:35 +02:00
|
|
|
if o.SSLInsecureSkipVerify {
|
|
|
|
// TODO: Accept a certificate bundle.
|
|
|
|
insecureTransport := &http.Transport{
|
|
|
|
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
|
|
|
}
|
|
|
|
http.DefaultClient = &http.Client{Transport: insecureTransport}
|
|
|
|
}
|
|
|
|
|
2015-03-15 18:23:13 +02:00
|
|
|
msgs := make([]string, 0)
|
2014-11-09 21:51:10 +02:00
|
|
|
if o.CookieSecret == "" {
|
2015-03-15 18:23:13 +02:00
|
|
|
msgs = append(msgs, "missing setting: cookie-secret")
|
2014-11-09 21:51:10 +02:00
|
|
|
}
|
|
|
|
if o.ClientID == "" {
|
2015-03-15 18:23:13 +02:00
|
|
|
msgs = append(msgs, "missing setting: client-id")
|
2014-11-09 21:51:10 +02:00
|
|
|
}
|
2019-03-20 15:44:51 +02:00
|
|
|
// login.gov uses a signed JWT to authenticate, not a client-secret
|
|
|
|
if o.ClientSecret == "" && o.Provider != "login.gov" {
|
2015-03-15 18:23:13 +02:00
|
|
|
msgs = append(msgs, "missing setting: client-secret")
|
2014-11-09 21:51:10 +02:00
|
|
|
}
|
2015-07-24 22:09:33 +02:00
|
|
|
if o.AuthenticatedEmailsFile == "" && len(o.EmailDomains) == 0 && o.HtpasswdFile == "" {
|
2017-08-05 18:54:31 +02:00
|
|
|
msgs = append(msgs, "missing setting for email validation: email-domain or authenticated-emails-file required."+
|
|
|
|
"\n use email-domain=* to authorize all email addresses")
|
2015-07-24 22:09:33 +02:00
|
|
|
}
|
2014-11-09 21:51:10 +02:00
|
|
|
|
2017-05-09 20:20:35 +02:00
|
|
|
if o.OIDCIssuerURL != "" {
|
2019-03-04 15:54:22 +02:00
|
|
|
|
|
|
|
ctx := context.Background()
|
|
|
|
|
|
|
|
// Construct a manual IDTokenVerifier from issuer URL & JWKS URI
|
|
|
|
// instead of metadata discovery if we enable -skip-oidc-discovery.
|
|
|
|
// In this case we need to make sure the required endpoints for
|
|
|
|
// the provider are configured.
|
|
|
|
if o.SkipOIDCDiscovery {
|
|
|
|
if o.LoginURL == "" {
|
|
|
|
msgs = append(msgs, "missing setting: login-url")
|
|
|
|
}
|
|
|
|
if o.RedeemURL == "" {
|
|
|
|
msgs = append(msgs, "missing setting: redeem-url")
|
|
|
|
}
|
|
|
|
if o.OIDCJwksURL == "" {
|
|
|
|
msgs = append(msgs, "missing setting: oidc-jwks-url")
|
|
|
|
}
|
|
|
|
keySet := oidc.NewRemoteKeySet(ctx, o.OIDCJwksURL)
|
|
|
|
o.oidcVerifier = oidc.NewVerifier(o.OIDCIssuerURL, keySet, &oidc.Config{
|
|
|
|
ClientID: o.ClientID,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
// Configure discoverable provider data.
|
|
|
|
provider, err := oidc.NewProvider(ctx, o.OIDCIssuerURL)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
o.oidcVerifier = provider.Verifier(&oidc.Config{
|
|
|
|
ClientID: o.ClientID,
|
|
|
|
})
|
|
|
|
|
|
|
|
o.LoginURL = provider.Endpoint().AuthURL
|
|
|
|
o.RedeemURL = provider.Endpoint().TokenURL
|
2017-05-09 20:20:35 +02:00
|
|
|
}
|
|
|
|
if o.Scope == "" {
|
|
|
|
o.Scope = "openid email profile"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 01:47:44 +02:00
|
|
|
o.redirectURL, msgs = parseURL(o.RedirectURL, "redirect", msgs)
|
2014-11-09 21:51:10 +02:00
|
|
|
|
|
|
|
for _, u := range o.Upstreams {
|
2015-11-09 01:47:44 +02:00
|
|
|
upstreamURL, err := url.Parse(u)
|
2014-11-09 21:51:10 +02:00
|
|
|
if err != nil {
|
2017-08-05 18:48:36 +02:00
|
|
|
msgs = append(msgs, fmt.Sprintf("error parsing upstream: %s", err))
|
|
|
|
} else {
|
|
|
|
if upstreamURL.Path == "" {
|
|
|
|
upstreamURL.Path = "/"
|
|
|
|
}
|
|
|
|
o.proxyURLs = append(o.proxyURLs, upstreamURL)
|
2014-11-09 21:51:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-01-12 11:18:41 +02:00
|
|
|
for _, u := range o.SkipAuthRegex {
|
|
|
|
CompiledRegex, err := regexp.Compile(u)
|
|
|
|
if err != nil {
|
2017-05-05 21:47:40 +02:00
|
|
|
msgs = append(msgs, fmt.Sprintf("error compiling regex=%q %s", u, err))
|
|
|
|
continue
|
2015-01-12 11:18:41 +02:00
|
|
|
}
|
|
|
|
o.CompiledRegex = append(o.CompiledRegex, CompiledRegex)
|
|
|
|
}
|
2015-03-30 21:48:30 +02:00
|
|
|
msgs = parseProviderInfo(o, msgs)
|
2015-01-12 11:18:41 +02:00
|
|
|
|
2015-05-09 23:31:13 +02:00
|
|
|
if o.PassAccessToken || (o.CookieRefresh != time.Duration(0)) {
|
2018-11-29 16:26:41 +02:00
|
|
|
validCookieSecretSize := false
|
2015-04-05 15:43:40 +02:00
|
|
|
for _, i := range []int{16, 24, 32} {
|
2016-06-20 13:17:39 +02:00
|
|
|
if len(secretBytes(o.CookieSecret)) == i {
|
2018-11-29 16:26:41 +02:00
|
|
|
validCookieSecretSize = true
|
2015-04-05 15:43:40 +02:00
|
|
|
}
|
|
|
|
}
|
2016-06-20 13:17:39 +02:00
|
|
|
var decoded bool
|
|
|
|
if string(secretBytes(o.CookieSecret)) != o.CookieSecret {
|
|
|
|
decoded = true
|
|
|
|
}
|
2018-11-29 16:26:41 +02:00
|
|
|
if validCookieSecretSize == false {
|
2016-06-20 13:17:39 +02:00
|
|
|
var suffix string
|
|
|
|
if decoded {
|
|
|
|
suffix = fmt.Sprintf(" note: cookie secret was base64 decoded from %q", o.CookieSecret)
|
|
|
|
}
|
2015-04-05 15:43:40 +02:00
|
|
|
msgs = append(msgs, fmt.Sprintf(
|
|
|
|
"cookie_secret must be 16, 24, or 32 bytes "+
|
|
|
|
"to create an AES cipher when "+
|
2015-05-09 23:31:13 +02:00
|
|
|
"pass_access_token == true or "+
|
2016-06-20 13:17:39 +02:00
|
|
|
"cookie_refresh != 0, but is %d bytes.%s",
|
|
|
|
len(secretBytes(o.CookieSecret)), suffix))
|
2015-04-05 15:43:40 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-05-09 23:16:19 +02:00
|
|
|
if o.CookieRefresh >= o.CookieExpire {
|
|
|
|
msgs = append(msgs, fmt.Sprintf(
|
|
|
|
"cookie_refresh (%s) must be less than "+
|
|
|
|
"cookie_expire (%s)",
|
|
|
|
o.CookieRefresh.String(),
|
|
|
|
o.CookieExpire.String()))
|
|
|
|
}
|
|
|
|
|
2015-08-20 12:07:02 +02:00
|
|
|
if len(o.GoogleGroups) > 0 || o.GoogleAdminEmail != "" || o.GoogleServiceAccountJSON != "" {
|
|
|
|
if len(o.GoogleGroups) < 1 {
|
|
|
|
msgs = append(msgs, "missing setting: google-group")
|
|
|
|
}
|
|
|
|
if o.GoogleAdminEmail == "" {
|
|
|
|
msgs = append(msgs, "missing setting: google-admin-email")
|
|
|
|
}
|
|
|
|
if o.GoogleServiceAccountJSON == "" {
|
|
|
|
msgs = append(msgs, "missing setting: google-service-account-json")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-16 05:08:30 +02:00
|
|
|
msgs = parseSignatureKey(o, msgs)
|
2016-07-19 21:51:25 +02:00
|
|
|
msgs = validateCookieName(o, msgs)
|
2015-11-16 05:08:30 +02:00
|
|
|
|
2015-03-15 18:23:13 +02:00
|
|
|
if len(msgs) != 0 {
|
|
|
|
return fmt.Errorf("Invalid configuration:\n %s",
|
|
|
|
strings.Join(msgs, "\n "))
|
|
|
|
}
|
2014-11-09 21:51:10 +02:00
|
|
|
return nil
|
|
|
|
}
|
2015-03-30 21:48:30 +02:00
|
|
|
|
|
|
|
func parseProviderInfo(o *Options, msgs []string) []string {
|
2015-07-26 01:27:49 +02:00
|
|
|
p := &providers.ProviderData{
|
|
|
|
Scope: o.Scope,
|
|
|
|
ClientID: o.ClientID,
|
|
|
|
ClientSecret: o.ClientSecret,
|
|
|
|
ApprovalPrompt: o.ApprovalPrompt,
|
|
|
|
}
|
2015-11-09 01:47:44 +02:00
|
|
|
p.LoginURL, msgs = parseURL(o.LoginURL, "login", msgs)
|
|
|
|
p.RedeemURL, msgs = parseURL(o.RedeemURL, "redeem", msgs)
|
|
|
|
p.ProfileURL, msgs = parseURL(o.ProfileURL, "profile", msgs)
|
|
|
|
p.ValidateURL, msgs = parseURL(o.ValidateURL, "validate", msgs)
|
2015-11-09 10:28:34 +02:00
|
|
|
p.ProtectedResource, msgs = parseURL(o.ProtectedResource, "resource", msgs)
|
2015-05-21 05:23:48 +02:00
|
|
|
|
2015-03-30 21:48:30 +02:00
|
|
|
o.provider = providers.New(o.Provider, p)
|
2015-05-21 05:23:48 +02:00
|
|
|
switch p := o.provider.(type) {
|
2015-11-09 10:28:34 +02:00
|
|
|
case *providers.AzureProvider:
|
|
|
|
p.Configure(o.AzureTenant)
|
2015-05-21 05:23:48 +02:00
|
|
|
case *providers.GitHubProvider:
|
|
|
|
p.SetOrgTeam(o.GitHubOrg, o.GitHubTeam)
|
2015-08-20 12:07:02 +02:00
|
|
|
case *providers.GoogleProvider:
|
|
|
|
if o.GoogleServiceAccountJSON != "" {
|
|
|
|
file, err := os.Open(o.GoogleServiceAccountJSON)
|
|
|
|
if err != nil {
|
|
|
|
msgs = append(msgs, "invalid Google credentials file: "+o.GoogleServiceAccountJSON)
|
|
|
|
} else {
|
|
|
|
p.SetGroupRestriction(o.GoogleGroups, o.GoogleAdminEmail, file)
|
|
|
|
}
|
|
|
|
}
|
2017-05-09 20:20:35 +02:00
|
|
|
case *providers.OIDCProvider:
|
|
|
|
if o.oidcVerifier == nil {
|
|
|
|
msgs = append(msgs, "oidc provider requires an oidc issuer URL")
|
|
|
|
} else {
|
|
|
|
p.Verifier = o.oidcVerifier
|
|
|
|
}
|
2019-03-20 15:44:51 +02:00
|
|
|
case *providers.LoginGovProvider:
|
|
|
|
p.AcrValues = o.AcrValues
|
|
|
|
p.PubJWKURL, msgs = parseURL(o.PubJWKURL, "pubjwk", msgs)
|
|
|
|
if o.JWTKey == "" {
|
|
|
|
msgs = append(msgs, "login.gov provider requires a private key for signing JWTs")
|
|
|
|
} else {
|
|
|
|
signKey, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(o.JWTKey))
|
|
|
|
if err != nil {
|
|
|
|
msgs = append(msgs, "could not parse RSA Private Key PEM")
|
|
|
|
} else {
|
|
|
|
p.JWTKey = signKey
|
|
|
|
}
|
|
|
|
}
|
2015-05-21 05:23:48 +02:00
|
|
|
}
|
2015-03-30 21:48:30 +02:00
|
|
|
return msgs
|
|
|
|
}
|
2015-11-16 05:08:30 +02:00
|
|
|
|
|
|
|
func parseSignatureKey(o *Options, msgs []string) []string {
|
|
|
|
if o.SignatureKey == "" {
|
|
|
|
return msgs
|
|
|
|
}
|
|
|
|
|
|
|
|
components := strings.Split(o.SignatureKey, ":")
|
|
|
|
if len(components) != 2 {
|
|
|
|
return append(msgs, "invalid signature hash:key spec: "+
|
|
|
|
o.SignatureKey)
|
|
|
|
}
|
|
|
|
|
|
|
|
algorithm, secretKey := components[0], components[1]
|
2018-11-29 16:26:41 +02:00
|
|
|
var hash crypto.Hash
|
|
|
|
var err error
|
|
|
|
if hash, err = hmacauth.DigestNameToCryptoHash(algorithm); err != nil {
|
2015-11-16 05:08:30 +02:00
|
|
|
return append(msgs, "unsupported signature hash algorithm: "+
|
|
|
|
o.SignatureKey)
|
|
|
|
}
|
2018-11-29 16:26:41 +02:00
|
|
|
o.signatureData = &SignatureData{hash, secretKey}
|
2015-11-16 05:08:30 +02:00
|
|
|
return msgs
|
|
|
|
}
|
2016-06-20 13:17:39 +02:00
|
|
|
|
2016-07-19 21:51:25 +02:00
|
|
|
func validateCookieName(o *Options, msgs []string) []string {
|
|
|
|
cookie := &http.Cookie{Name: o.CookieName}
|
|
|
|
if cookie.String() == "" {
|
|
|
|
return append(msgs, fmt.Sprintf("invalid cookie name: %q", o.CookieName))
|
|
|
|
}
|
|
|
|
return msgs
|
|
|
|
}
|
|
|
|
|
2016-06-20 13:17:39 +02:00
|
|
|
func addPadding(secret string) string {
|
|
|
|
padding := len(secret) % 4
|
|
|
|
switch padding {
|
|
|
|
case 1:
|
|
|
|
return secret + "==="
|
|
|
|
case 2:
|
|
|
|
return secret + "=="
|
|
|
|
case 3:
|
|
|
|
return secret + "="
|
|
|
|
default:
|
|
|
|
return secret
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// secretBytes attempts to base64 decode the secret, if that fails it treats the secret as binary
|
|
|
|
func secretBytes(secret string) []byte {
|
|
|
|
b, err := base64.URLEncoding.DecodeString(addPadding(secret))
|
|
|
|
if err == nil {
|
|
|
|
return []byte(addPadding(string(b)))
|
|
|
|
}
|
|
|
|
return []byte(secret)
|
|
|
|
}
|