-
Notifications
You must be signed in to change notification settings - Fork 18.8k
IPv6 only: Allow IPv4 and IPv6 host-gateway-ip addresses #48807
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
Conversation
c008b37
to
0e5047e
Compare
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.
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-gateway
value; 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")
opts/host_gateway.go
Outdated
@@ -0,0 +1,65 @@ | |||
package opts |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
internal/opts/host_gateway_test.go
Outdated
{ | ||
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
integration/network/network_test.go
Outdated
@@ -4,6 +4,7 @@ import ( | |||
"bytes" | |||
"encoding/json" | |||
"fmt" | |||
"github.com/docker/docker/internal/testutils/networking" |
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.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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).
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 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...
|
daemon/config/config.go
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
jsonString, _ := json.Marshal(&struct { | |
*config.Config | |
config.Proxies `json:"proxies"` | |
}{ | |
Config: &newCfg.Config, | |
Proxies: config.Proxies{ | |
HTTPProxy: config.MaskCredentials(newCfg.HTTPProxy), | |
HTTPSProxy: config.MaskCredentials(newCfg.HTTPSProxy), | |
NoProxy: config.MaskCredentials(newCfg.NoProxy), | |
}, | |
}) | |
log.G(context.TODO()).Infof("Reloaded configuration: %s", jsonString) |
daemon/config/config.go
Outdated
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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
// IPSliceVar defines a []net.IP flag with specified name, default value, and usage string. | |
// The argument p points to a []net.IP variable in which to store the value of the flag. | |
func IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) { | |
CommandLine.VarP(newIPSliceValue(value, p), name, "", usage) | |
} | |
// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash. | |
func IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) { | |
CommandLine.VarP(newIPSliceValue(value, p), name, shorthand, usage) | |
} |
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.
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 <rob.murray@docker.com>
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 <rob.murray@docker.com>
Signed-off-by: Rob Murray <rob.murray@docker.com>
937311f
to
c9a1e4d
Compare
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.
LGTM, thanks!
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-gateway
adds an/etc/hosts
entry for hostblah
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:
/etc/hosts
entry with that address--host-gateway-ip
options--host-gateway-ips
(plural).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"
- How I did it
Notes:
/etc/hosts
entries follow the usual rules...- How to verify it
- Description for the changelog