Skip to content

Add support for network interfaces in requests#6207

Closed
rezaf28 wants to merge 3 commits into
canonical:mainfrom
rezaf28:main
Closed

Add support for network interfaces in requests#6207
rezaf28 wants to merge 3 commits into
canonical:mainfrom
rezaf28:main

Conversation

@rezaf28

@rezaf28 rezaf28 commented May 5, 2025

Copy link
Copy Markdown

This change modifies the metadata retrieval logic to handle IPv6 link-local
addresses that require a specific network interface (scope_id). Previously,
cloud-init failed to connect when using addresses like [fe80::a9fe:a9fe%25{iface}] due
to improper handling in the underlying requests layer.

By introducing a scoped connection handling approach, this fix enables
cloud-init to operate correctly in IPv6-only OpenStack environments
and similar setups where metadata services are accessible via
scoped link-local addresses.

Summary of Patch Fixes:

  1. Interface in URL: Added proper support for link-local IPv6 addresses with network interface identifiers (e.g., %eth0), enabling reliable socket binding using SO_BINDTODEVICE.

  2. Enhanced URL Parsing: Improved URL parser to correctly handle hostnames, ports (with defaults), interfaces, and support for both % and %25 encoded formats across various schemes including http, https, ftp, sftp, and file.

Fixes #6205

Fix for canonical#6205

Note:
When using socket.SO_BINDTODEVICE in Python (e.g., with requests-toolbelt's SocketOptionsAdapter), the process requires the CAP_NET_RAW capability on Linux. This is because binding a socket to a specific network interface (iface) is a privileged operation.
@holmanb

holmanb commented May 7, 2025

Copy link
Copy Markdown
Member

How did you test? What was the test result? Please include evidence that this works. Bug fixes should include test coverage, please see this and this for getting started.

@holmanb

holmanb commented May 7, 2025

Copy link
Copy Markdown
Member

I just noticed the dependency. New dependencies are strongly discouraged. Is this dependency already available in Linux distributions? Was an alternative approach considered? Why isn't it possible to do the same thing without the new dependency?

@nmeyerhans

Copy link
Copy Markdown
Contributor

Not commenting on the code itself, but I think this approach is the right one until such a time as requests handles scoped link-local addresses correctly. Given the general tone of psf/requests#6927 I wouldn't count on that happening any time soon.

The additional dependency is somewhat problematic, though. Requests-toolbelt is available in Debian and Fedora, but doesn't seem to be included in Fedora-derivatives (checked Rocky 8 and 9, anyway).

@rezaf28

rezaf28 commented May 8, 2025

Copy link
Copy Markdown
Author

@nmeyerhans
Please check again
Logs :
cloud-init.log
cloud-init-output.log

@ani-sinha

ani-sinha commented May 8, 2025

Copy link
Copy Markdown
Contributor

@xiachen-rh Can we do some testing on this change on RHEL-10 and using the same repro case we had before? Also does this fix your test case psf/requests#6735 (comment) ?

@xiachen-rh

Copy link
Copy Markdown
Contributor

@ani-sinha OK, let me test it

@github-actions

Copy link
Copy Markdown

Hello! Thank you for this proposed change to cloud-init. This pull request is now marked as stale as it has not seen any activity in 14 days. If no activity occurs within the next 7 days, this pull request will automatically close.

If you are waiting for code review and you are seeing this message, apologies! Please reply, tagging TheRealFalcon, and he will ensure that someone takes a look soon.

(If the pull request is closed and you would like to continue working on it, please do tag TheRealFalcon to reopen it.)

@github-actions github-actions Bot added the stale-pr Pull request is stale; will be auto-closed soon label May 26, 2025
@github-actions github-actions Bot closed this Jun 2, 2025
@xiachen-rh

Copy link
Copy Markdown
Contributor

@ani-sinha I tested this changes on OpenStack env with ipv6 only network, cloud-init can get metadata via ipv6, so cloud-init can work without changing the code of 'requests'.

@ani-sinha

Copy link
Copy Markdown
Contributor

@ani-sinha I tested this changes on OpenStack env with ipv6 only network, cloud-init can get metadata via ipv6, so cloud-init can work without changing the code of 'requests'.

@holmanb what are we going to do with this PR? I have not followed the fix closely myself so can't comment. Do you think we can merge it to cloud-init?

@TheRealFalcon TheRealFalcon reopened this Jun 10, 2025
@github-actions github-actions Bot removed the stale-pr Pull request is stale; will be auto-closed soon label Jun 11, 2025
@ani-sinha

ani-sinha commented Jun 11, 2025

Copy link
Copy Markdown
Contributor

@ani-sinha I tested this changes on OpenStack env with ipv6 only network, cloud-init can get metadata via ipv6, so cloud-init can work without changing the code of 'requests'.

@holmanb what are we going to do with this PR? I have not followed the fix closely myself so can't comment. Do you think we can merge it to cloud-init?

Someone has to review URLParserPlus which is the crux of the change. Does it handle all URL types? Also run integration tests to make sure there are no functional breakage.

@github-actions

Copy link
Copy Markdown

Hello! Thank you for this proposed change to cloud-init. This pull request is now marked as stale as it has not seen any activity in 14 days. If no activity occurs within the next 7 days, this pull request will automatically close.

If you are waiting for code review and you are seeing this message, apologies! Please reply, tagging TheRealFalcon, and he will ensure that someone takes a look soon.

(If the pull request is closed and you would like to continue working on it, please do tag TheRealFalcon to reopen it.)

@github-actions github-actions Bot added the stale-pr Pull request is stale; will be auto-closed soon label Jun 26, 2025
@TheRealFalcon TheRealFalcon removed the stale-pr Pull request is stale; will be auto-closed soon label Jun 30, 2025

@TheRealFalcon TheRealFalcon left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This seems like it can be a workable solution. I left some comments inline.

Additionally, I'd like to see some testing with a test case that fails without this change, but passes with the new change.

Comment thread cloudinit/util.py
global _DNS_REDIRECT_IP
parsed_url = parse.urlparse(url)
name = parsed_url.hostname
parsed_url = url_parser_plus(url)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't see why any of the changes to this function are needed. The call to net.is_ip_address should return true for any ipv6 address, including those that are interface scoped. Since we strip the [] before passing it in, there shouldn't be a problem here.

If that fails, then we should have an actual non-IP hostname, so the call to socket.getaddrinfo also shouldn't need any changes.

Am I missing something?

Comment thread cloudinit/url_helper.py
if headers_cb:
headers = headers_cb(url)

headers["Host"] = parsed_url.hostname

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why is this line needed?

Comment thread cloudinit/util.py
TRUE_STRINGS = ("true", "1", "on", "yes")
FALSE_STRINGS = ("off", "0", "no", "false")

class URLParserPlus:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rather than a whole bucket of new parsing, I'd rather we just extract the iface from already parsed host. See my comment in url_helper.py. I don't think we need this new class.

Comment thread cloudinit/url_helper.py
if session is None:
session = requests.Session()

if iface is not None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we can remove all of the changes to the top of this function and add something like this above the if condition here:

    parsed_url = urlparse(url)
    iface = parsed_url.hostname.split("%")[1] if parsed_url.hostname else None

@TheRealFalcon

Copy link
Copy Markdown
Contributor

@rezaf28 , also, before we can accept your change, you need to sign the CLA. Please note that Canonical's CLA process has changed recently, so if you signed a long time ago, you are required to resign in order to contribute.

@github-actions

Copy link
Copy Markdown

Hello! Thank you for this proposed change to cloud-init. This pull request is now marked as stale as it has not seen any activity in 14 days. If no activity occurs within the next 7 days, this pull request will automatically close.

If you are waiting for code review and you are seeing this message, apologies! Please reply, tagging TheRealFalcon, and he will ensure that someone takes a look soon.

(If the pull request is closed and you would like to continue working on it, please do tag TheRealFalcon to reopen it.)

@github-actions github-actions Bot added the stale-pr Pull request is stale; will be auto-closed soon label Jul 17, 2025
@github-actions github-actions Bot closed this Jul 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stale-pr Pull request is stale; will be auto-closed soon

Projects

None yet

Development

Successfully merging this pull request may close these issues.

cloud-init fails to retrieve metadata over IPv6-only network

6 participants