Skip to content

feat: add azure oidc PKI auth instead of client secret #9054

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Aug 14, 2023
66 changes: 56 additions & 10 deletions cli/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ import (
"github.com/coder/coder/coderd/gitsshkey"
"github.com/coder/coder/coderd/httpapi"
"github.com/coder/coder/coderd/httpmw"
"github.com/coder/coder/coderd/oauthpki"
"github.com/coder/coder/coderd/prometheusmetrics"
"github.com/coder/coder/coderd/schedule"
"github.com/coder/coder/coderd/telemetry"
Expand Down Expand Up @@ -551,9 +552,9 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
}
}

if cfg.OIDC.ClientSecret != "" {
if cfg.OIDC.ClientKeyFile != "" || cfg.OIDC.ClientSecret != "" {
if cfg.OIDC.ClientID == "" {
return xerrors.Errorf("OIDC client ID be set!")
return xerrors.Errorf("OIDC client ID must be set!")
}
if cfg.OIDC.IssuerURL == "" {
return xerrors.Errorf("OIDC issuer URL must be set!")
Expand All @@ -578,15 +579,33 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
if slice.Contains(cfg.OIDC.Scopes, "groups") && cfg.OIDC.GroupField == "" {
cfg.OIDC.GroupField = "groups"
}
oauthCfg := &oauth2.Config{
ClientID: cfg.OIDC.ClientID.String(),
ClientSecret: cfg.OIDC.ClientSecret.String(),
RedirectURL: redirectURL.String(),
Endpoint: oidcProvider.Endpoint(),
Scopes: cfg.OIDC.Scopes,
}

var useCfg httpmw.OAuth2Config = oauthCfg
if cfg.OIDC.ClientKeyFile != "" {
// PKI authentication is done in the params. If a
// counter example is found, we can add a config option to
// change this.
oauthCfg.Endpoint.AuthStyle = oauth2.AuthStyleInParams
if cfg.OIDC.ClientSecret != "" {
return xerrors.Errorf("cannot specify both oidc client secret and oidc client key file")
}

pkiCfg, err := configureOIDCPKI(oauthCfg, cfg.OIDC.ClientKeyFile.Value(), cfg.OIDC.ClientCertFile.Value())
if err != nil {
return xerrors.Errorf("configure oauth pki authentication: %w", err)
}
useCfg = pkiCfg
}
options.OIDCConfig = &coderd.OIDCConfig{
OAuth2Config: &oauth2.Config{
ClientID: cfg.OIDC.ClientID.String(),
ClientSecret: cfg.OIDC.ClientSecret.String(),
RedirectURL: redirectURL.String(),
Endpoint: oidcProvider.Endpoint(),
Scopes: cfg.OIDC.Scopes,
},
Provider: oidcProvider,
OAuth2Config: useCfg,
Provider: oidcProvider,
Verifier: oidcProvider.Verifier(&oidc.Config{
ClientID: cfg.OIDC.ClientID.String(),
}),
Expand Down Expand Up @@ -1494,6 +1513,33 @@ func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles
return tlsConfig, nil
}

func configureOIDCPKI(orig *oauth2.Config, keyFile string, certFile string) (*oauthpki.Config, error) {
// Read the files
keyData, err := os.ReadFile(keyFile)
if err != nil {
return nil, xerrors.Errorf("read oidc client key file: %w", err)
}

var certData []byte
// According to the spec, this is not required. So do not require it on the initial loading
// of the PKI config.
if certFile != "" {
certData, err = os.ReadFile(certFile)
if err != nil {
return nil, xerrors.Errorf("read oidc client cert file: %w", err)
}
}

return oauthpki.NewOauth2PKIConfig(oauthpki.ConfigParams{
ClientID: orig.ClientID,
TokenURL: orig.Endpoint.TokenURL,
Scopes: orig.Scopes,
PemEncodedKey: keyData,
PemEncodedCert: certData,
Config: orig,
})
}

func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
if tlsClientCAFile != "" {
caPool := x509.NewCertPool()
Expand Down
10 changes: 10 additions & 0 deletions cli/testdata/coder_server_--help.golden
Original file line number Diff line number Diff line change
Expand Up @@ -304,9 +304,19 @@ can safely ignore these settings.
--oidc-auth-url-params struct[map[string]string], $CODER_OIDC_AUTH_URL_PARAMS (default: {"access_type": "offline"})
OIDC auth URL parameters to pass to the upstream provider.

--oidc-client-cert-file string, $CODER_OIDC_CLIENT_CERT_FILE
Pem encoded certificate file to use for oauth2 PKI/JWT authorization.
The public certificate that accompanies oidc-client-key-file. A
standard x509 certificate is expected.

--oidc-client-id string, $CODER_OIDC_CLIENT_ID
Client ID to use for Login with OIDC.

--oidc-client-key-file string, $CODER_OIDC_CLIENT_KEY_FILE
Pem encoded RSA private key to use for oauth2 PKI/JWT authorization.
This can be used instead of oidc-client-secret if your IDP supports
it.

--oidc-client-secret string, $CODER_OIDC_CLIENT_SECRET
Client secret to use for Login with OIDC.

Expand Down
9 changes: 9 additions & 0 deletions cli/testdata/server-config.yaml.golden
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@ oidc:
# Client ID to use for Login with OIDC.
# (default: <unset>, type: string)
clientID: ""
# Pem encoded RSA private key to use for oauth2 PKI/JWT authorization. This can be
# used instead of oidc-client-secret if your IDP supports it.
# (default: <unset>, type: string)
oidcClientKeyFile: ""
# Pem encoded certificate file to use for oauth2 PKI/JWT authorization. The public
# certificate that accompanies oidc-client-key-file. A standard x509 certificate
# is expected.
# (default: <unset>, type: string)
oidcClientCertFile: ""
# Email domains that clients logging in with OIDC must match.
# (default: <unset>, type: string-array)
emailDomain: []
Expand Down
7 changes: 7 additions & 0 deletions coderd/apidoc/docs.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions coderd/apidoc/swagger.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading