Skip to content
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

Traffic Ops client certificate authentication #7392

Merged
merged 21 commits into from
May 8, 2023
Merged
Changes from 1 commit
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
96ea127
initial client certificate auth code for login
Sep 6, 2022
a8e192e
Add intermediate cert for chain testing. Add LDAP check for UID parse…
Oct 4, 2022
d104a8f
Add nil check for client TLS connection state
Oct 4, 2022
961bb77
Update cdn.conf to include Root cert location. Remove test certs
Oct 4, 2022
ec7f207
Remove filesystem tests for root cert
Oct 4, 2022
36e6ef7
Use long descriptive form in JSON for cdn.conf
Oct 4, 2022
a18d93c
Add checks for config values. Update example logic
Oct 10, 2022
10788e1
Initial documentation commit instead of stash
Nov 1, 2022
94e8542
Moved client.go and server.go to separate folders because they are bo…
Feb 8, 2023
317de5d
Add Apache license to example server+client
Feb 8, 2023
f0bd4d5
Remove commented code lines
zrhoffman Mar 9, 2023
1d4c668
Use JWT constanswconstants instead of string literals
zrhoffman Mar 9, 2023
3bf8acc
Refactor so `goto` is unnecessary
zrhoffman Mar 9, 2023
fa3809d
Lowercase error messages
zrhoffman Mar 9, 2023
7a2cec0
Use separte PKI directory for TO root certificates
zrhoffman Apr 24, 2023
893219b
Reject certs that are group or world-writable
zrhoffman Apr 24, 2023
2e56bb9
Do not fully bail on unpocessable files
zrhoffman Apr 24, 2023
fdfea30
- Reject certificate subjects with multiple UIDs
zrhoffman Apr 24, 2023
2ff5d88
Use %s for error in format string
zrhoffman Apr 24, 2023
746ea5b
Track github.com/apache/trafficcontrol/lib/go-rfc/ldap
zrhoffman Apr 28, 2023
73409b2
Use %s for error type
zrhoffman Apr 28, 2023
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
Prev Previous commit
Next Next commit
Refactor so goto is unnecessary
  • Loading branch information
zrhoffman committed Mar 9, 2023
commit 3bf8acca0323cd94d3ee79e070f6df32351f2ea3
93 changes: 48 additions & 45 deletions traffic_ops/traffic_ops_golang/login/login.go
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,53 @@ Subject: {{.InstanceName}} Password Reset Request` + "\r\n\r" + `
</html>
`))

func clientCertAuthentication(w http.ResponseWriter, r *http.Request, db *sqlx.DB, cfg config.Config, dbCtx context.Context, cancelTx context.CancelFunc, form auth.PasswordForm, authenticated bool) bool {
// No certs provided by the client. Skip to form authentication
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
return false
}

// If no configuration is set, skip to form auth
if cfg.ClientCertAuth == nil || len(cfg.ClientCertAuth.RootCertsDir) == 0 {
return false
}

// Perform certificate verification to ensure it is valid against Root CAs
err := auth.VerifyClientCertificate(r, cfg.ClientCertAuth.RootCertsDir)
if err != nil {
log.Warnf("ClientCertAuth: error attempting to verify client provided TLS certificate. err: %s\n", err)
return false
}

// Client provided a verified certificate. Extract UID value.
form.Username = auth.ParseClientCertificateUID(r.TLS.PeerCertificates[0])
if len(form.Username) == 0 {
zrhoffman marked this conversation as resolved.
Show resolved Hide resolved
log.Infoln("ClientCertAuth: client provided certificate did not contain a UID object identifier or value")
return false
}

// Check if user exists locally (TODB) and has a role.
var blockingErr error
authenticated, err, blockingErr = auth.CheckLocalUserIsAllowed(form.Username, db, dbCtx)
if blockingErr != nil {
api.HandleErr(w, r, nil, http.StatusServiceUnavailable, nil, fmt.Errorf("error checking local user has role: %s", blockingErr.Error()))
return false
}
if err != nil {
log.Warnf("ClientCertAuth: checking local user: %s\n", err.Error())
}

// Check LDAP if enabled
if !authenticated && cfg.LDAPEnabled {
_, authenticated, err = auth.LookupUserDN(form.Username, cfg.ConfigLDAP)
if err != nil {
log.Warnf("ClientCertAuth: checking ldap user: %s\n", err.Error())
}
}

return authenticated
}

// LoginHandler first attempts to verify and parse user information from an optionally
// provided client TLS certificate. If it fails at any point, it will fall back and
// continue with the standard submitted form authentication.
Expand All @@ -123,52 +170,8 @@ func LoginHandler(db *sqlx.DB, cfg config.Config) http.HandlerFunc {
// Attempt to perform client certificate authentication. If fails, goto standard form auth. If the
// certificate was verified, has a UID, and the UID matches an existing user we consider this to
// be a successful login.
{
// No certs provided by the client. Skip to form authentication
if r.TLS == nil || len(r.TLS.PeerCertificates) == 0 {
goto FormAuth
}

// If no configuration is set, skip to form auth
if cfg.ClientCertAuth == nil || len(cfg.ClientCertAuth.RootCertsDir) == 0 {
goto FormAuth
}

// Perform certificate verification to ensure it is valid against Root CAs
err := auth.VerifyClientCertificate(r, cfg.ClientCertAuth.RootCertsDir)
if err != nil {
log.Warnf("ClientCertAuth: error attempting to verify client provided TLS certificate. err: %s\n", err)
goto FormAuth
}

// Client provided a verified certificate. Extract UID value.
form.Username = auth.ParseClientCertificateUID(r.TLS.PeerCertificates[0])
if len(form.Username) == 0 {
log.Infoln("ClientCertAuth: client provided certificate did not contain a UID object identifier or value")
goto FormAuth
}

// Check if user exists locally (TODB) and has a role.
var blockingErr error
authenticated, err, blockingErr = auth.CheckLocalUserIsAllowed(form.Username, db, dbCtx)
if blockingErr != nil {
api.HandleErr(w, r, nil, http.StatusServiceUnavailable, nil, fmt.Errorf("error checking local user has role: %s", blockingErr.Error()))
return
}
if err != nil {
log.Warnf("ClientCertAuth: checking local user: %s\n", err.Error())
}

// Check LDAP if enabled
if !authenticated && cfg.LDAPEnabled {
_, authenticated, err = auth.LookupUserDN(form.Username, cfg.ConfigLDAP)
if err != nil {
log.Warnf("ClientCertAuth: checking ldap user: %s\n", err.Error())
}
}
}
authenticated = clientCertAuthentication(w, r, db, cfg, dbCtx, cancelTx, form, authenticated)

FormAuth:
// Failed certificate-based auth, perform standard form auth
if !authenticated {
// Perform form authentication
Expand Down