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

SPIKE audit logs shall include user information. #36

Open
v0lkan opened this issue Nov 16, 2024 · 6 comments
Open

SPIKE audit logs shall include user information. #36

v0lkan opened this issue Nov 16, 2024 · 6 comments
Assignees
Labels
good first issue Good for newcomers

Comments

@v0lkan
Copy link
Contributor

v0lkan commented Nov 16, 2024

Right now SPIKE audit logs pass empty string as the user name; however, we can get those details from the JWT and add it to the audit logs.

Here is a sample JWT payload that's sent with every authenticated API requeset:

{
  "adminTokenId": "spike-admin-jwt",
  "iss": "spike-nexus",
  "sub": "spike-admin",
  "exp": 1731808095,
  "nbf": 1731721695,
  "iat": 1731721695
}
@v0lkan v0lkan added the good first issue Good for newcomers label Nov 16, 2024
@sahinakyol
Copy link
Contributor

@v0lkan !assign

@v0lkan
Copy link
Contributor Author

v0lkan commented Dec 4, 2024

This has changed a bit (since we don’t use JWT but we use SVIDs instead); I'll update the description and then reassign.

@kfox1111
Copy link

kfox1111 commented Dec 4, 2024

I think we should be using both?

x509 svids for machines, jwt's for users.

For cases where we're using spire svid as the user auth mechanism for bootstrapping / recovery, we still could use both.
x509 svid for the client, jwt svid for the user.

Then, for auditing, can log the jwt info.

@v0lkan
Copy link
Contributor Author

v0lkan commented Dec 4, 2024

I think we should be using both?

Yes, for named admins we'd need JWT and along with an IDM integration (like Keycloak)

For cases where we're using spire svid as the user auth mechanism for bootstrapping / recovery, we still could use both. x509 svid for the client, jwt svid for the user.

Need to think about that.

But the uber-super initial admin uses (or "issues" to be more specific) an x509 SVID for the spike binary. That SVID attests the user's Linux credentials (user, group etc) and the binary's SHA hash. So I think it's a secure enough mechanism (arguably, even more secure than just issuing a JWT because the user has to physically log into the box to use it -- in a sense, it is similar to ESXi requesting SSH access for some highly-critical admin functionality (where you are not allowed to do them from the vSphere UI)).

We can make the client issue a JWT SVID and consumers (SPIKE Nexus) validate that JWT SVID, but that will complicate things since we already know that the x509 svid with that specific spiffeid can only belong to the uber-super nameless admin.

So for the sake of this task (I think) we can look at the x509 SVID (if there is one) and log the SPIFFEID into the audit log.

When we deploy keycloak and start playing with named admins the audit entrypoint will probably need to intercept JWT in the header too; and if it detects it, it can log it too.

@v0lkan
Copy link
Contributor Author

v0lkan commented Dec 5, 2024

@sahinakyol I'm a bit swamped but I'll add some details to this ticket this weekend hopefully. -- I also want to ensure that the current SPIKE and SDK is in a good shape.

^ after that, I'll assign it to you.

@v0lkan
Copy link
Contributor Author

v0lkan commented Dec 9, 2024

Okay; some more details ( for @sahinakyol )

The HandleRoute() function already intercepts all API requests. So it's the best way to enrich the audit log. Pasting the current version of the function for reference:

func HandleRoute(h Handler) {
	http.HandleFunc("/", func(
		writer http.ResponseWriter, request *http.Request,
	) {
		now := time.Now()
		entry := log.AuditEntry{
			TrailId:   crypto.Id(),
			Timestamp: now,
			UserId:    "",
			Action:    log.AuditEnter,
			Path:      request.URL.Path,
			Resource:  "",
			SessionID: "",
			State:     log.AuditCreated,
		}
		log.Audit(entry)

		err := h(writer, request, &entry)
		if err == nil {
			entry.State = log.AuditSuccess
		} else {
			entry.State = log.AuditErrored
			entry.Err = err.Error()
		}

		entry.Duration = time.Since(now)
		log.Audit(entry)
	})
}

Here's the audit entry:

		entry := log.AuditEntry{
			TrailId:   crypto.Id(),
			Timestamp: now,
			UserId:    "",  
			Action:    log.AuditEnter,
			Path:      request.URL.Path,
			Resource:  "",
			SessionID: "",
			State:     log.AuditCreated,
		}

Firstly, I think the audit object needs some cleanup (for example, SessionID is not needed since we don't have a session concept)

At this interception point we can do two things:

  1. If there is a JWT Bearer header, we can parse the token from the header and log info from the payload section of the JWT

you can assume that the payload will at least have these standard fields:

  "iss": "spike-nexus",
  "sub": "spike-admin",
  "exp": 1731808095,
  "nbf": 1731721695,
  "iat": 1731721695

we can upate the code if we feel wee need to track any additional field in the audit logs.

  1. The SPIFFE ID: Since SPIKE Nexus requires SPIFFE mTLS, the request will always have a TLS cert with a SPIFFE ID in the SAN field.

The SPIKE Go SDK has a spiffe.IdFromRequest() helper to extract that ID from the request.

we should add that ID to the audit log too.

--

That's about it for now, I think.

I'm assigning the issue. It's all yours @sahinakyol :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
good first issue Good for newcomers
Projects
None yet
Development

No branches or pull requests

3 participants