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

Implement AccessorID resolution and client ip propagation #19

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

ncode
Copy link

@ncode ncode commented Dec 27, 2024

Hi @mxab,

I've started the patch last week and would like to double check the direction it's going so far :).

My idea here was to resolve the token as early as possible to propagate down. The upstream ip address is very cheap to propagate, so I thought about always passing it so we can have cidr based validators and mutators even when not resolving the token

Addresses #18


Changelog

[Unreleased]

Breaking Changes

  • Controller Signature Refactor
    The Job-only signature in the admission controller has been replaced with a new types.Payload struct.

    • All mutators and validators now receive a Payload object containing both the Job definition and additional context (e.g., client IP, resolved token details).
    • Any custom integrations using the old Job-based method signatures must be updated to use types.Payload.
  • OPA Input Changes
    The embedded OPA validator has been updated to accept a new input structure containing job and caller context.

    • Policies and data references relying on the previous input format must be updated accordingly.
  • Remote Webhook Contract Change
    Webhook mutators and validators now receive a request body with the combined job and context data instead of job-only information.

    • Downstream services expecting the old JSON schema must be updated to parse the new Payload format.

Added

  • Token Resolution & Context Passing
    Hooks can now resolve Nomad tokens (with optional policy extraction) and pass the accessor ID, client IP, and other metadata through mutators and validators.

    • New configuration flag resolveToken enables token resolution for specific hooks to avoid unnecessary overhead when not required.
    • Enhanced support for use cases like CIDR-based validation, custom ACL logic, and extended audit logging.
  • Changelog Initialization
    Introduced a CHANGELOG.md to track significant updates, especially breaking changes and added features.

Rational

With these changes, you can now:

  • Perform CIDR-based validations by leveraging the client IP.
  • Create advanced ACL logic by passing resolved ACL token details (accessor ID, policies) to OPA or remote webhooks.
  • Implement more granular auditing or custom workflows by integrating the new, richer context data available in each request.

This pull request includes significant updates to the admissionctrl package, focusing on refactoring the Job handling to use a new types.Payload structure and adding support for context headers in webhook mutators. The most important changes are as follows:

Refactoring to use types.Payload:

  • admissionctrl/controller.go: Changed method signatures and internal logic to use types.Payload instead of api.Job. This includes updates to JobMutator, JobValidator, JobHandler, and their respective methods. [1] [2] [3] [4]
  • admissionctrl/controller_test.go: Updated test cases to use types.Payload and modified TestJobHandler_ApplyAdmissionControllers to include the new resolveToken parameter. [1] [2] [3]

Adding context headers in webhook mutators:

Updates to mutator and OPA logic:

Test updates:

@mxab
Copy link
Owner

mxab commented Dec 29, 2024

Hi @ncode, awesome! This goes definitely in the right direction!

Some thoughts so far but theses are things we also could stabilise later:

  • the token resolution provides additional context, e.g. policies afaik, should we pass those to the webhooks as well?
  • afaik X-... headers are discouraged, should we use NACP-.... ?
  • passing everything as headers might get a bit messy in cases there will be more values, we could break the API so far and pass it as a combined document to the webhook ´{ job: , context : { remote_ip_addr: ..., ... }}`

I'm also currently waiting for a recommendation from the OPA communityhow to model this there

@ncode
Copy link
Author

ncode commented Jan 2, 2025

Hi @mxab,

Hi @ncode, awesome! This goes definitely in the right direction!

Some thoughts so far but theses are things we also could stabilise later:

  • the token resolution provides additional context, e.g. policies afaik, should we pass those to the webhooks as well?

I didn't think of it but it's a pretty nice idea to pass all the available information since we've already resolved it :)

  • afaik X-... headers are discouraged, should we use NACP-.... ?

Yes. Let's use NACP-...

  • passing everything as headers might get a bit messy in cases there will be more values, we could break the API so far and pass it as a combined document to the webhook ´{ job: , context : { remote_ip_addr: ..., ... }}`

I agree that passing the context like you describe will make things a bit more flexible, specially with the extra metadata from the token it will be cleaner. I'm only in doubt about X-FORWARD-FOR and the NACP-Nomad-Accessor-ID, these would be useful to have available as readers without the need to parse the content. What do you think?

I'm also currently waiting for a recommendation from the OPA communityhow to model this there

Nice!

@mxab
Copy link
Owner

mxab commented Jan 8, 2025

Sorry for the late response.

X-FORWARD-FOR and the NACP-Nomad-Accessor-ID, these would be useful to have available as readers without the need to parse the content

Ah so if I get you correctly your "validation" part in this case is solely on the Client IP Address + Accessor ID. Yeah I guess it makes sense.

Regarding the OPA part, they recommended to create an input consisting out of the context and the job. So I guess we need to update this part here and create a input struct the consists out of a caller context (client ip addr, resolved token) and the job

So for the remote hooks I think it make sense to use the same structure, we could still leave the headers as well so it works for your use case

@ncode
Copy link
Author

ncode commented Jan 15, 2025

Sorry for the late response.

No worries!

X-FORWARD-FOR and the NACP-Nomad-Accessor-ID, these would be useful to have available as readers without the need to parse the content

Ah so if I get you correctly your "validation" part in this case is solely on the Client IP Address + Accessor ID. Yeah I guess it makes sense.

Regarding the OPA part, they recommended to create an input consisting out of the context and the job. So I guess we need to update this part here and create a input struct the consists out of a caller context (client ip addr, resolved token) and the job

So for the remote hooks I think it make sense to use the same structure, we could still leave the headers as well so it works for your use case

Deal! I will start working on it this weekend!

@ncode
Copy link
Author

ncode commented Feb 3, 2025

Hi @mxab,

I think at this stage we have the bare bones of how to propagate the data. Now I'm staring to add the context passing to mutators and validators. I will add those to the same PR and we can break it into different pieces if it's a too big of a PR. One thing I'm not entire sure is if we should break the api and have:

type Payload struct {
	Job     *api.Job               `json:"job"`
	Context *config.RequestContext `json:"context,omitempty"`
}

I will add a few extra tests and deploy a release to test this week to validate in production :)

@ncode ncode marked this pull request as ready for review February 3, 2025 15:41
@mxab
Copy link
Owner

mxab commented Feb 5, 2025

Hi,
Nice. I'll review it asap, just a bit stuffed until the weekend.

@ncode
Copy link
Author

ncode commented Feb 5, 2025

Hi, Nice. I'll review it asap, just a bit stuffed until the weekend.

No worries!

return ip
}

func resolveTokenAccessor(transport http.RoundTripper, nomadAddress *url.URL, token string) (*api.ACLToken, error) {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm actual not too knowledgeable when it comes to http lib of golang, what is it we do with the transport here :)

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah this was for the tls part right?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeap. Since we have a transport coming from the upstream function call, we use the same settings to call the nomad endpoint. To ensure if we have the tls set we use it.

@mxab
Copy link
Owner

mxab commented Feb 15, 2025

Overall I would say this looks really great. I guess it would be a good time to start CHANGELOG.md to document those breaking changes :D

@ncode
Copy link
Author

ncode commented Feb 15, 2025

Overall I would say this looks really great. I guess it would be a good time to start CHANGELOG.md to document those breaking changes :D

Nice! It indeed make sense to add those to a CHANGELOG.md. I'm happy to update MR the with a changelog section that details the change to help :)

@ncode
Copy link
Author

ncode commented Feb 25, 2025

@mxab I've added a changelog section to the PR message. Now that I'm thinking about it, should I actually push the CHANGELOG.md file?

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

Successfully merging this pull request may close these issues.

2 participants