-
Notifications
You must be signed in to change notification settings - Fork 109
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
Add inbound support for svc hostname in authority #1420
base: master
Are you sure you want to change the base?
Conversation
Skipping CI for Draft Pull Request. |
😊 Welcome @jaellio! This is either your first contribution to the Istio ztunnel repo, or it's been You can learn more about the Istio working groups, Code of Conduct, and contribution guidelines Thanks for contributing! Courtesy of your friendly welcome wagon. |
/test all |
src/proxy/inbound.rs
Outdated
// TODO(jaellio): Should we also validate the hbone_addr port? | ||
HboneAddress::SvcHostname(hbone_host, hbone_port) => { | ||
// Validate a service or workload exists for the hostname | ||
let parts: Vec<&str> = hbone_host.split('.').collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO - figure out if there is a better way to parse a hostname
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can. There is not really a mapping from hostname -> namespace. For example, I could have a hostname foo.com
. We could have a guess or heuristic, but this doesn't seem like a time when doing the check only some of the time provides much value?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since its to look up the service, perhaps we do something like find all the services with the hostname and pick the first preferring one with the same namespace (as the workload) if available
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a shot at this with ranking - might be a simpler way to do it in rust but I am not at all familiar :) Let me know if it's similar to what you had in mind.
/test test |
1 similar comment
/test test |
|
||
// Determine the next hop. | ||
let hbone_addr_clone = hbone_addr.clone(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Making hbone_addr
an Arc
may help with the clones
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could probably be changed so that find_inbound_upstream takes a borrow. Not sure why it accepts so many borrows and one owned TBH. All we really care about here is the port I think which is cheap to copy on the stack vs a clone of something more complex
src/proxy/inbound.rs
Outdated
HboneAddress::SvcHostname(hbone_host, hbone_port) => { | ||
// Validate a service or workload exists for the hostname | ||
let parts: Vec<&str> = hbone_host.split('.').collect(); | ||
if parts.len() <= 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can probably be a little stronger here and require an FQDN (i.e. error if we detect a short name with less than 5 parts). I think Istio always requires FQDNs for multi-cluster addressing (/cc @howardjohn)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree. Kept it simple for the draft but this isn't sufficient atm.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes to require FQDN. However I am not convinced we can/should do the mapping of service --> namespace at all (see comment 2 lines up)
src/proxy/inbound.rs
Outdated
hbone_addr: SocketAddr, | ||
) -> Result<(), Error> { | ||
hbone_addr: HboneAddress, | ||
) -> Result<Option<SocketAddr>, Error> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't love the idea of returning a value from this function. I know you need it for the proxy protocol stuff so maybe the initial hbone_addr matching can be a part of a separate function that's called only if the :authority is a hostname
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, just a temporary hack. Will fix
src/proxy/inbound.rs
Outdated
StatusCode::BAD_REQUEST, | ||
)); | ||
} | ||
// TODO(jaellio): Intelligently select the VIP |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keithmattix with regards to #1409 and intelligently selecting vip for a Address::Service, is it sufficient to rank vips by network comparison (higher rank if vip.network
matches conn.dst_network
) or should we also be comparing IpAddr ranges? Do we also want to implement ranking of Address::Services when finding hostnames (similar to how there would be duplicate workloads on lookup by IP)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, missed this. Are you still blocked here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Based on this comment we can't rely on the hostname to include the namespace. So right now we'll need to change some of the logic for finding a service by hostname and implement a ranking as suggested here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not blocked, I'll implement the service ranking and vip ranking as suggested above. What just curious if you had any thoughts on the approach.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keithmattix Implemented simple ranking for service (when searching without a namespace) and vip. There might be a simpler way to accomplish the ranking but took inspiration from #1409
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A
const PROXY_PROTOCOL_AUTHORITY_TLV: u8 = 0xD0; | ||
// Custom TLV for including the svc hostname in the proxy protocol header | ||
const PROXY_PROTOCOL_SVC_HOSTNAME_TLV: u8 = 0xD1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm this is an interesting one. I don't see any strong reason not to do this other than I don't know of any use cases to do so, and if we do we probably can never remove it since its part of our runtime API contract.
Did you have a specific use in mind for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I mean, I imagine that if there's custom waypoint sandwiched with the ztunnel, it would want to use the hostname for routing (vs. an IP address we lookup) , right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@keithmattix correct me if I am wrong here. We were concerned about not preserving the hostname in the proxy protocol. Not positive about the exact implications of this for logging, but maybe it has more implications for security tools?
src/proxy/inbound.rs
Outdated
@@ -262,38 +346,90 @@ impl Inbound { | |||
} | |||
|
|||
let start = Instant::now(); | |||
|
|||
// Extract the host or IP from the authority pseudo-header of the URI | |||
let hbone_addr = req |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: you could make this a
impl TryFrom<&http::Uri> for HboneAddress {
type Error = Error;
fn try_from(value: &http::Uri) -> Result<Self, Self::Error> { ... }
Then the calling code is just req.uri().try_into()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Took a shot at it, let me know if it's what you had in mind
src/proxy/inbound.rs
Outdated
let hbone_host = req.uri().host().unwrap(); | ||
let hbone_port = req.uri().port_u16().unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think these unwraps are safe. I suspect :authority: foo
(no port) would panic here for example. Which we want to reject (probably with a BAD_REQUEST), but shouldn't unwrap.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Moved this logic to the TryFrom implementation for HboneAddress. Returns an error result if port or host are not set.
src/proxy/inbound.rs
Outdated
// TODO(jaellio): Should we also validate the hbone_addr port? | ||
HboneAddress::SvcHostname(hbone_host, hbone_port) => { | ||
// Validate a service or workload exists for the hostname | ||
let parts: Vec<&str> = hbone_host.split('.').collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we can. There is not really a mapping from hostname -> namespace. For example, I could have a hostname foo.com
. We could have a guess or heuristic, but this doesn't seem like a time when doing the check only some of the time provides much value?
src/proxy/inbound.rs
Outdated
HboneAddress::SvcHostname(hbone_host, hbone_port) => { | ||
// Validate a service or workload exists for the hostname | ||
let parts: Vec<&str> = hbone_host.split('.').collect(); | ||
if parts.len() <= 1 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes to require FQDN. However I am not convinced we can/should do the mapping of service --> namespace at all (see comment 2 lines up)
src/proxy/inbound.rs
Outdated
// TODO(jaellio): Should we also validate the hbone_addr port? | ||
HboneAddress::SvcHostname(hbone_host, hbone_port) => { | ||
// Validate a service or workload exists for the hostname | ||
let parts: Vec<&str> = hbone_host.split('.').collect(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since its to look up the service, perhaps we do something like find all the services with the hostname and pick the first preferring one with the same namespace (as the workload) if available
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
Signed-off-by: Jackie Elliott <[email protected]>
cf45ea3
to
5a9a0b7
Compare
Implement TryFrom from uri to hboneAddress Signed-off-by: Jackie Elliott <[email protected]>
Hoping to get feedback on the overall approach for this change. I would appreciate any rust specific feedback as well.
PR still needs some minor refactoring.
Main changes:
Todo: