Skip to content

Commit

Permalink
fix(auth): pass client cert chain to OPA
Browse files Browse the repository at this point in the history
  • Loading branch information
mtharp committed May 14, 2024
1 parent 6ece825 commit e00f5e2
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 0 deletions.
2 changes: 2 additions & 0 deletions doc/opa.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ relic will provide the following inputs to the policy:
- token - The Bearer token provided in the `Authorization` header, if provided
- fingerprint - Digest of the client's leaf certificate, if provided
- client_cert - The client's provided certificate chain, in PEM format,
with the leaf certificate last
- path - Path from the URL being accessed
- query - Query parameters from the URL being accessed

Expand Down
17 changes: 17 additions & 0 deletions internal/authmodel/opa.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package authmodel
import (
"bytes"
"context"
"crypto/x509"
"encoding/json"
"encoding/pem"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -44,6 +46,7 @@ func (a *PolicyAuth) Authenticate(req *http.Request) (UserInfo, error) {
return nil, err
} else if len(peerCerts) != 0 {
input.Fingerprint = fingerprint(peerCerts[0])
input.ClientCert = formatCerts(peerCerts)
}
if input.Token == "" && input.Fingerprint == "" {
return nil, httperror.ErrTokenRequired
Expand Down Expand Up @@ -164,6 +167,7 @@ type policyInput struct {
Query url.Values `json:"query"`
Token string `json:"token"`
Fingerprint string `json:"fingerprint"`
ClientCert string `json:"client_cert"`
}

type policyResponse struct {
Expand Down Expand Up @@ -205,6 +209,19 @@ func bearerToken(req *http.Request) string {
return auth[len(prefix):]
}

func formatCerts(peerCerts []*x509.Certificate) string {
var certs strings.Builder
for i := range peerCerts {
// OPA wants leaf cert last, which is the opposite of how they arrive
cert := peerCerts[len(peerCerts)-i-1]
_ = pem.Encode(&certs, &pem.Block{
Type: "CERTIFICATE",
Bytes: cert.Raw,
})
}
return certs.String()
}

var should401 = map[string]bool{
"token is missing or not well-formed": true,
"token issuer is not in known_issuers": true,
Expand Down

0 comments on commit e00f5e2

Please sign in to comment.