Skip to content

Commit 25ce30d

Browse files
authored
feat: add azure oidc PKI auth instead of client secret (#9054)
* feat: add azure oidc PKI auth instead of client secret * add client cert and key as deployment options * Custom token refresher to handle pki auth
1 parent 4e36f91 commit 25ce30d

File tree

13 files changed

+748
-35
lines changed

13 files changed

+748
-35
lines changed

cli/server.go

+56-10
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ import (
7676
"github.com/coder/coder/coderd/gitsshkey"
7777
"github.com/coder/coder/coderd/httpapi"
7878
"github.com/coder/coder/coderd/httpmw"
79+
"github.com/coder/coder/coderd/oauthpki"
7980
"github.com/coder/coder/coderd/prometheusmetrics"
8081
"github.com/coder/coder/coderd/schedule"
8182
"github.com/coder/coder/coderd/telemetry"
@@ -551,9 +552,9 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
551552
}
552553
}
553554

554-
if cfg.OIDC.ClientSecret != "" {
555+
if cfg.OIDC.ClientKeyFile != "" || cfg.OIDC.ClientSecret != "" {
555556
if cfg.OIDC.ClientID == "" {
556-
return xerrors.Errorf("OIDC client ID be set!")
557+
return xerrors.Errorf("OIDC client ID must be set!")
557558
}
558559
if cfg.OIDC.IssuerURL == "" {
559560
return xerrors.Errorf("OIDC issuer URL must be set!")
@@ -578,15 +579,33 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
578579
if slice.Contains(cfg.OIDC.Scopes, "groups") && cfg.OIDC.GroupField == "" {
579580
cfg.OIDC.GroupField = "groups"
580581
}
582+
oauthCfg := &oauth2.Config{
583+
ClientID: cfg.OIDC.ClientID.String(),
584+
ClientSecret: cfg.OIDC.ClientSecret.String(),
585+
RedirectURL: redirectURL.String(),
586+
Endpoint: oidcProvider.Endpoint(),
587+
Scopes: cfg.OIDC.Scopes,
588+
}
589+
590+
var useCfg httpmw.OAuth2Config = oauthCfg
591+
if cfg.OIDC.ClientKeyFile != "" {
592+
// PKI authentication is done in the params. If a
593+
// counter example is found, we can add a config option to
594+
// change this.
595+
oauthCfg.Endpoint.AuthStyle = oauth2.AuthStyleInParams
596+
if cfg.OIDC.ClientSecret != "" {
597+
return xerrors.Errorf("cannot specify both oidc client secret and oidc client key file")
598+
}
599+
600+
pkiCfg, err := configureOIDCPKI(oauthCfg, cfg.OIDC.ClientKeyFile.Value(), cfg.OIDC.ClientCertFile.Value())
601+
if err != nil {
602+
return xerrors.Errorf("configure oauth pki authentication: %w", err)
603+
}
604+
useCfg = pkiCfg
605+
}
581606
options.OIDCConfig = &coderd.OIDCConfig{
582-
OAuth2Config: &oauth2.Config{
583-
ClientID: cfg.OIDC.ClientID.String(),
584-
ClientSecret: cfg.OIDC.ClientSecret.String(),
585-
RedirectURL: redirectURL.String(),
586-
Endpoint: oidcProvider.Endpoint(),
587-
Scopes: cfg.OIDC.Scopes,
588-
},
589-
Provider: oidcProvider,
607+
OAuth2Config: useCfg,
608+
Provider: oidcProvider,
590609
Verifier: oidcProvider.Verifier(&oidc.Config{
591610
ClientID: cfg.OIDC.ClientID.String(),
592611
}),
@@ -1494,6 +1513,33 @@ func configureTLS(tlsMinVersion, tlsClientAuth string, tlsCertFiles, tlsKeyFiles
14941513
return tlsConfig, nil
14951514
}
14961515

1516+
func configureOIDCPKI(orig *oauth2.Config, keyFile string, certFile string) (*oauthpki.Config, error) {
1517+
// Read the files
1518+
keyData, err := os.ReadFile(keyFile)
1519+
if err != nil {
1520+
return nil, xerrors.Errorf("read oidc client key file: %w", err)
1521+
}
1522+
1523+
var certData []byte
1524+
// According to the spec, this is not required. So do not require it on the initial loading
1525+
// of the PKI config.
1526+
if certFile != "" {
1527+
certData, err = os.ReadFile(certFile)
1528+
if err != nil {
1529+
return nil, xerrors.Errorf("read oidc client cert file: %w", err)
1530+
}
1531+
}
1532+
1533+
return oauthpki.NewOauth2PKIConfig(oauthpki.ConfigParams{
1534+
ClientID: orig.ClientID,
1535+
TokenURL: orig.Endpoint.TokenURL,
1536+
Scopes: orig.Scopes,
1537+
PemEncodedKey: keyData,
1538+
PemEncodedCert: certData,
1539+
Config: orig,
1540+
})
1541+
}
1542+
14971543
func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
14981544
if tlsClientCAFile != "" {
14991545
caPool := x509.NewCertPool()

cli/testdata/coder_server_--help.golden

+10
Original file line numberDiff line numberDiff line change
@@ -304,9 +304,19 @@ can safely ignore these settings.
304304
--oidc-auth-url-params struct[map[string]string], $CODER_OIDC_AUTH_URL_PARAMS (default: {"access_type": "offline"})
305305
OIDC auth URL parameters to pass to the upstream provider.
306306

307+
--oidc-client-cert-file string, $CODER_OIDC_CLIENT_CERT_FILE
308+
Pem encoded certificate file to use for oauth2 PKI/JWT authorization.
309+
The public certificate that accompanies oidc-client-key-file. A
310+
standard x509 certificate is expected.
311+
307312
--oidc-client-id string, $CODER_OIDC_CLIENT_ID
308313
Client ID to use for Login with OIDC.
309314

315+
--oidc-client-key-file string, $CODER_OIDC_CLIENT_KEY_FILE
316+
Pem encoded RSA private key to use for oauth2 PKI/JWT authorization.
317+
This can be used instead of oidc-client-secret if your IDP supports
318+
it.
319+
310320
--oidc-client-secret string, $CODER_OIDC_CLIENT_SECRET
311321
Client secret to use for Login with OIDC.
312322

cli/testdata/server-config.yaml.golden

+9
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,15 @@ oidc:
244244
# Client ID to use for Login with OIDC.
245245
# (default: <unset>, type: string)
246246
clientID: ""
247+
# Pem encoded RSA private key to use for oauth2 PKI/JWT authorization. This can be
248+
# used instead of oidc-client-secret if your IDP supports it.
249+
# (default: <unset>, type: string)
250+
oidcClientKeyFile: ""
251+
# Pem encoded certificate file to use for oauth2 PKI/JWT authorization. The public
252+
# certificate that accompanies oidc-client-key-file. A standard x509 certificate
253+
# is expected.
254+
# (default: <unset>, type: string)
255+
oidcClientCertFile: ""
247256
# Email domains that clients logging in with OIDC must match.
248257
# (default: <unset>, type: string-array)
249258
emailDomain: []

coderd/apidoc/docs.go

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/apidoc/swagger.json

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)