IPv6 only: Allow IPv4 and IPv6 host-gateway-ip addresses#48807
IPv6 only: Allow IPv4 and IPv6 host-gateway-ip addresses#48807thaJeztah merged 3 commits intomoby:masterfrom
Conversation
c008b37 to
0e5047e
Compare
thaJeztah
left a comment
There was a problem hiding this comment.
It'd be nice to have a daemon.json "host-gateway-ips" that takes a []string, rather than the sightly-odd comma separated list in a single string. But...
We have to keep the "host-gateway-ip field for existing config files.
I think we should be able to achieve this, but it would require users to migrate to the new field if they need to have options for multiple;
- Keep the old, single-value field
- Add the new, multi-value field
- Users are allowed to set on or the other, but not BOTH
- Setting BOTH is a hard failure
- Setting the old one propagates the new field with a single value, and logs a warning (possibly warning in
docker info) - After X release(s) we can deprecate the old field (hard failure when used).
Some questions;
- IFF we'd introduce a new field, and if you'd design your ideal would that be only a
[]string, or are there other potential options to expect (i.e, would a[]struct{"field": "value", "field": "value}be something desirable?) - We currently limit to the special
host-gatewayvalue; I recall at some point we discussed having an option to add custom DNS entries for containers; mostly mentioning here, as possibly related to this effort (but not sure if that should be "same feature" or "other feature")
| @@ -0,0 +1,65 @@ | |||
| package opts | |||
There was a problem hiding this comment.
If this one's only gonna be used by the daemon, would it be ok to move this to internal/opts ? The opts package used to be somewhat shared between the CLI and Daemon, but trying to reduce public API surface if we don't need it exported.
There was a problem hiding this comment.
Oh, yes - hadn't spotted that package. I'll move it, thank you.
@akerouanton suggested handling it in the JSON unmarshal, so in And, on the command line, it'd just be two That seems like a good plan, I think it handles everything we need - I'll make the change.
Just the IP address I think. It'll only allow one IPv4 and one IPv6 address - so we could make it a JSON object and require something like I suppose we could make it possible to name a host interface, and look up its addresses (not saying we will do that, we'd probably have to monitor for address changes and make Or, worst case, if we do find a need for a more complicated type - we could pull the same trick again, and accept an object as well as a string/list.
I'm not sure - we could put the |
0e5047e to
4c4a4f9
Compare
| { | ||
| name: "ipv6 and ipv4", | ||
| input: []string{"fdb8:3037:2cb2::1", "10.1.1.1"}, | ||
| expStr: "10.1.1.1,fdb8:3037:2cb2::1", |
There was a problem hiding this comment.
Shouldn't it be a JSON array?
| expStr: "10.1.1.1,fdb8:3037:2cb2::1", | |
| expStr: `["10.1.1.1","fdb8:3037:2cb2::1"]`, |
There was a problem hiding this comment.
It'll never be used as JSON, so I went for something unambiguous and minimal for error messages etc. At-least opts/address_pools.go takes the same approach.
The two addresses will need to be passed to buildx, once it understands two addresses. Either version would be ok for that.
Can change it if needed though?
There was a problem hiding this comment.
Was mostly concerned if it should match the input in daemon.json it was parsed from.
If it doesn't matter though, either is fine.
0afc4a5 to
ab81e12
Compare
| "bytes" | ||
| "encoding/json" | ||
| "fmt" | ||
| "github.com/docker/docker/internal/testutils/networking" |
There was a problem hiding this comment.
You need to put this import in the block of imports below. I think that's why the CI is complaining.
There was a problem hiding this comment.
Yes, ta - I'll do that once we pick an approach (just didn't want to break the links I sent to reviewers by changing the commit numbers, and this PR is going to need at least one more push anyway).
akerouanton
left a comment
There was a problem hiding this comment.
Since you did the extra work to add --host-gateway-ips to the daemon, I think I'm in favor of that approach. It makes this flag follows the pattern used by other plural flags, etc... So this sounds better.
Is there any particular downside you see with this approach?
Right, I agree it fits better and probably wins on that basis. If forced to look for downsides, nothing too bad, but...
|
| DNS []net.IP `json:"dns,omitempty"` | ||
| DNSOptions []string `json:"dns-opts,omitempty"` | ||
| DNSSearch []string `json:"dns-search,omitempty"` | ||
| HostGatewayIP net.IP `json:"host-gateway-ip,omitempty"` // Deprecated: this single-IP is migrated to HostGatewayIPs |
There was a problem hiding this comment.
Looks like this would require it to be a pointer for the omitempty to work (also see my example below; https://go.dev/play/p/m-9f_mVwuGW)
There was a problem hiding this comment.
The only change here is to deprecate the existing field.
I don't think this struct is ever marshalled - just unmarshalled from the config file ... so I'm not sure why any of the fields have omitempty.
There was a problem hiding this comment.
Yeah, not super critical probably; I think we use the JSON format to log it when reloading the config; perhaps I should try if that would work with structured logs (although it can be useful to have the log log the config in the format used for config.json 🤔
Lines 118 to 129 in 803534f
| DNSOptions []string `json:"dns-opts,omitempty"` | ||
| DNSSearch []string `json:"dns-search,omitempty"` | ||
| HostGatewayIP net.IP `json:"host-gateway-ip,omitempty"` // Deprecated: this single-IP is migrated to HostGatewayIPs | ||
| HostGatewayIPs []string `json:"host-gateway-ips,omitempty"` |
There was a problem hiding this comment.
Curious; was there a reason not to use []net.IP for this? From some quick testing, marshalling / unmarshaling should work for that? See https://go.dev/play/p/m-9f_mVwuGW
package main
import (
"encoding/json"
"net"
"testing"
)
type config struct {
HostGatewayIP net.IP `json:"host-gateway-ip,omitempty"` // Deprecated: this single-IP is migrated to HostGatewayIPs
HostGatewayIPs []net.IP `json:"host-gateway-ips,omitempty"`
}
func TestGatewayIPS(t *testing.T) {
cfg1 := config{
HostGatewayIPs: []net.IP{
net.ParseIP("2001:db8::1234"),
net.ParseIP("192.0.2.1"),
},
}
out, err := json.Marshal(cfg1)
if err != nil {
t.Fatal(err)
}
t.Log(string(out))
const configJSON = `{"host-gateway-ips": ["2001:db8::1234", "192.0.2.1"]}`
var cfg config
err = json.Unmarshal([]byte(configJSON), &cfg)
if err != nil {
t.Fatal(err)
}
t.Logf("%+v\n", cfg.HostGatewayIPs)
}There was a problem hiding this comment.
Because we don't have any separation between config read from the file, config from the command line, and the structs used to represent config internally.
A []string can be used in flags.Var(opts.NewNamedListOptsRef("host-gateway-ips", &conf.HostGatewayIPs, opts.ValidateIPAddress) ... it'd be possible to write a []net.IP (or, rather, []netip.Addr) version of that. Perhaps it could also be rewritten to use generic types. But, I didn't do any of that! ... it wouldn't buy much in this case, the addresses are only used as strings, and they're validated as IP addresses.
There was a problem hiding this comment.
Ah, right, so there is a IPSliceVar (and related) option in spf13/pflag. Haven't checked what's needed to wrap that into a "NamedListOpt" (or whatever it's named) though;
moby/vendor/github.com/spf13/pflag/ip_slice.go
Lines 151 to 160 in 803534f
There was a problem hiding this comment.
It doesn't quite fit together like that, IPSliceVar takes a *[]net.IP, which can't be a NamedOption (so it can't implement the equivalence between command-line --host-gateway-ip and config file "host-gateway-ips").
So, I've added a NamedIPListOpts, which stores values in a []netip.Addr and implements interface NamedOption.
I added it as a third commit to make it easier to see the change ... how's it looking?
a80e24d to
4717ef0
Compare
4717ef0 to
937311f
Compare
Signed-off-by: Rob Murray <[email protected]>
Running a container with "--add-host blah:host-gateway" adds an /etc/hosts entry for host "blah" and an address on the docker host - to give the container a convenient way of reaching the host. If no --host-gateway-ip option is supplied, the IPv4 address of the default bridge is used - and that's been fine until now, it's a host address we know will exist. But, in a container that's only connected to IPv6-only networks, that doesn't work. So: - if the default bridge has an IPv6 address, create an additional /etc/hosts entry with that adddress - allow two --host-gateway-ip options - at most one IPv4 and one IPv6 address - in daemon.json, allow a JSON array value in --host-gateway-ips (plural) - for a single address, a JSON string is also allowed For example: --host-gateway-ip 192.0.2.1 --host-gateway-ip 2001:db8::1111 And the daemon.json version would be: "host-gateway-ips": ["192.0.2.1", "2001:db8::1111"] But, this is also still valid: "host-gateway-ip": "192.0.2.1" Note that the /etc/hosts entries follow the usual rules. If IPv6 is disabled in a container (by sysctl, or lack of kernel support), IPv6 addresses are not included in the file. In other cases, IPv4 and IPv6 addresses will both be included, whether or not the container currently has network endpoints that support IPv4 or IPv6. buildx has its own code to interpret the host-gateway-ip option. When it's updated to understand two addresses, moby will need to pass it both. For now, it passes an IPv4 address if there is one, else IPv6. Signed-off-by: Rob Murray <[email protected]>
Signed-off-by: Rob Murray <[email protected]>
937311f to
c9a1e4d
Compare
|
Edit: ignore this comment - too many PRs, and this one isn't involved! I'll deal with it separately. I've just found a problem with this, worth fixing before it's merged - disabling IPv6 on an endpoint means it can't be an IPv6 gateway ... |
- What I did
Running a container with
--add-host blah:host-gatewayadds an/etc/hostsentry for hostblahand an address on the docker host - to give the container a convenient way of reaching the host.If no
--host-gateway-ipoption is supplied, the IPv4 address of the default bridge is used - and that's been fine until now, it's a host address we know will exist. But, in a container that's only connected to IPv6-only networks, that doesn't work.So:
/etc/hostsentry with that address--host-gateway-ipoptions--host-gateway-ips(plural).For example:
--host-gateway-ip 192.0.2.1 --host-gateway-ip 2001:db8::1111And the daemon.json version would be:
"host-gateway-ips": ["192.0.2.1", "2001:db8::1111"]But, this is also still valid:
"host-gateway-ip": "192.0.2.1"- How I did it
Notes:
/etc/hostsentries follow the usual rules...- How to verify it
- Description for the changelog