diff --git a/libnetwork/endpoint.go b/libnetwork/endpoint.go index f5c3e92c22..77e2550545 100644 --- a/libnetwork/endpoint.go +++ b/libnetwork/endpoint.go @@ -908,7 +908,22 @@ func (ep *Endpoint) sbLeave(ctx context.Context, sb *Sandbox, force bool) error log.G(ctx).WithError(e).Error("Failed to delete endpoint state for endpoint from cluster") } + // When a container is connected to a network, it gets /etc/hosts + // entries for its addresses on that network. So, when it's connected + // to two networks, it has a hosts entry for each. For example, if + // the hostname is the default short-id, and it's connected to two + // networks (172.19.0.0/16 and 172.20.0.0/17, plus IPv6 address for + // each), the hosts file might include: + // + // 172.19.0.2 4b92a573912d + // fd8c:c894:d68::2 4b92a573912d + // 172.20.0.2 4b92a573912d + // fd8c:c894:d68:1::2 4b92a573912d + // + // If the container is disconnected from 172.19.0.2, only remove + // the hosts entries with addresses on that network. sb.deleteHostsEntries(n.getSvcRecords(ep)) + if !sb.inDelete && sb.needDefaultGW() && sb.getEndpointInGWNetwork() == nil { return sb.setupDefaultGW() } diff --git a/libnetwork/etchosts/etchosts.go b/libnetwork/etchosts/etchosts.go index 5a3eab646a..436e788db1 100644 --- a/libnetwork/etchosts/etchosts.go +++ b/libnetwork/etchosts/etchosts.go @@ -8,6 +8,7 @@ import ( "net/netip" "os" "regexp" + "strings" "sync" ) @@ -127,11 +128,10 @@ func Add(path string, recs []Record) error { return err } -// Delete deletes an arbitrary number of Records already existing in /etc/hosts file -// -// FIXME(robmry) - this only matches on hostname, not address. So, if a container -// is connected to two networks then disconnected from one of them, the hosts -// entries for both networks are deleted. +// Delete deletes Records from /etc/hosts. +// The hostnames must be an exact match (if the user has modified the record, +// it won't be deleted). The address, parsed as a netip.Addr must also match +// the value in recs. func Delete(path string, recs []Record) error { if len(recs) == 0 { return nil @@ -160,8 +160,10 @@ loop: continue } for _, r := range recs { - if bytes.HasSuffix(b, []byte("\t"+r.Hosts)) { - continue loop + if before, found := strings.CutSuffix(string(b), "\t"+r.Hosts); found { + if addr, err := netip.ParseAddr(strings.TrimSpace(before)); err == nil && addr == r.IP { + continue loop + } } } buf.Write(b)