Skip to content

Commit 7397425

Browse files
authored
Merge pull request #650 from rancher-sandbox/hosts
Allow adding static names to hostresolver
2 parents 41087bf + 014fbc3 commit 7397425

File tree

9 files changed

+116
-10
lines changed

9 files changed

+116
-10
lines changed

cmd/limactl/debug.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func debugDNSAction(cmd *cobra.Command, args []string) error {
4747
return err
4848
}
4949
}
50-
srv, err := dns.Start(udpLocalPort, tcpLocalPort, ipv6)
50+
srv, err := dns.Start(udpLocalPort, tcpLocalPort, ipv6, map[string]string{})
5151
if err != nil {
5252
return err
5353
}

examples/docker.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ containerd:
2323
user: false
2424
provision:
2525
- mode: system
26+
# This script defines the host.docker.internal hostname when hostResolver is disabled.
27+
# It is also needed for lima 0.8.2 and earlier, which does not support hostResolver.hosts.
28+
# Names defined in /etc/hosts inside the VM are not resolved inside containers when
29+
# using the hostResolver; use hostResolver.hosts instead (requires lima 0.8.3 or later).
2630
script: |
2731
#!/bin/sh
2832
sed -i 's/host.lima.internal.*/host.lima.internal host.docker.internal/' /etc/hosts
@@ -56,6 +60,11 @@ probes:
5660
exit 1
5761
fi
5862
hint: See "/var/log/cloud-init-output.log". in the guest
63+
hostResolver:
64+
# hostResolver.hosts requires lima 0.8.3 or later. Names defined here will also
65+
# resolve inside containers, and not just inside the VM itself.
66+
hosts:
67+
host.docker.internal: host.lima.internal
5968
portForwards:
6069
- guestSocket: "/run/user/{{.UID}}/docker.sock"
6170
hostSocket: "{{.Dir}}/sock/docker.sock"
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
#!/bin/bash
22
set -eux -o pipefail
33

4+
# Define host.lima.internal in case the hostResolver is disabled. When using
5+
# the hostResolver, the name is provided by the lima resolver itself because
6+
# it doesn't have access to /etc/hosts inside the VM.
47
sed -i '/host.lima.internal/d' /etc/hosts
58
echo -e "${LIMA_CIDATA_SLIRP_GATEWAY}\thost.lima.internal" >>/etc/hosts

pkg/hostagent/dns/dns.go

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77
"net"
88
"strings"
99

10+
"github.com/lima-vm/lima/pkg/limayaml"
1011
"github.com/miekg/dns"
1112
"github.com/sirupsen/logrus"
1213
)
@@ -19,6 +20,8 @@ type Handler struct {
1920
clientConfig *dns.ClientConfig
2021
clients []*dns.Client
2122
IPv6 bool
23+
cname map[string]string
24+
ip map[string]net.IP
2225
}
2326

2427
type Server struct {
@@ -44,7 +47,7 @@ func newStaticClientConfig(ips []net.IP) (*dns.ClientConfig, error) {
4447
return dns.ClientConfigFromReader(r)
4548
}
4649

47-
func newHandler(IPv6 bool) (dns.Handler, error) {
50+
func newHandler(IPv6 bool, hosts map[string]string) (dns.Handler, error) {
4851
cc, err := dns.ClientConfigFromFile("/etc/resolv.conf")
4952
if err != nil {
5053
fallbackIPs := []net.IP{net.ParseIP("8.8.8.8"), net.ParseIP("1.1.1.1")}
@@ -62,6 +65,15 @@ func newHandler(IPv6 bool) (dns.Handler, error) {
6265
clientConfig: cc,
6366
clients: clients,
6467
IPv6: IPv6,
68+
cname: make(map[string]string),
69+
ip: make(map[string]net.IP),
70+
}
71+
for host, address := range hosts {
72+
if ip := net.ParseIP(address); ip != nil {
73+
h.ip[host] = ip
74+
} else {
75+
h.cname[host] = limayaml.Cname(address)
76+
}
6577
}
6678
return h, nil
6779
}
@@ -82,14 +94,32 @@ func (h *Handler) handleQuery(w dns.ResponseWriter, req *dns.Msg) {
8294
switch q.Qtype {
8395
case dns.TypeAAAA:
8496
if !h.IPv6 {
97+
handled = true
8598
break
8699
}
87100
fallthrough
88101
case dns.TypeCNAME, dns.TypeA:
89-
cname, err := net.LookupCNAME(q.Name)
90-
if err != nil {
102+
cname := q.Name
103+
seen := make(map[string]bool)
104+
for {
105+
// break cyclic definition
106+
if seen[cname] {
107+
break
108+
}
109+
if _, ok := h.cname[cname]; ok {
110+
seen[cname] = true
111+
cname = h.cname[cname]
112+
continue
113+
}
91114
break
92115
}
116+
var err error
117+
if _, ok := h.ip[cname]; !ok {
118+
cname, err = net.LookupCNAME(cname)
119+
if err != nil {
120+
break
121+
}
122+
}
93123
if cname != "" && cname != q.Name {
94124
hdr.Rrtype = dns.TypeCNAME
95125
a := &dns.CNAME{
@@ -103,7 +133,13 @@ func (h *Handler) handleQuery(w dns.ResponseWriter, req *dns.Msg) {
103133
break
104134
}
105135
hdr.Name = cname
106-
addrs, err := net.LookupIP(q.Name)
136+
var addrs []net.IP
137+
if _, ok := h.ip[cname]; ok {
138+
addrs = []net.IP{h.ip[cname]}
139+
err = nil
140+
} else {
141+
addrs, err = net.LookupIP(cname)
142+
}
107143
if err == nil && len(addrs) > 0 {
108144
for _, ip := range addrs {
109145
var a dns.RR
@@ -219,8 +255,8 @@ func (h *Handler) ServeDNS(w dns.ResponseWriter, req *dns.Msg) {
219255
}
220256
}
221257

222-
func Start(udpLocalPort, tcpLocalPort int, IPv6 bool) (*Server, error) {
223-
h, err := newHandler(IPv6)
258+
func Start(udpLocalPort, tcpLocalPort int, IPv6 bool, hosts map[string]string) (*Server, error) {
259+
h, err := newHandler(IPv6, hosts)
224260
if err != nil {
225261
return nil, err
226262
}

pkg/hostagent/hostagent.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import (
2727
"github.com/lima-vm/lima/pkg/hostagent/events"
2828
"github.com/lima-vm/lima/pkg/limayaml"
2929
"github.com/lima-vm/lima/pkg/qemu"
30+
qemuconst "github.com/lima-vm/lima/pkg/qemu/const"
3031
"github.com/lima-vm/lima/pkg/sshutil"
3132
"github.com/lima-vm/lima/pkg/store"
3233
"github.com/lima-vm/lima/pkg/store/filenames"
@@ -40,6 +41,7 @@ type HostAgent struct {
4041
udpDNSLocalPort int
4142
tcpDNSLocalPort int
4243
instDir string
44+
instName string
4345
sshConfig *ssh.SSHConfig
4446
portForwarder *portForwarder
4547
onClose []func() error // LIFO
@@ -145,6 +147,7 @@ func New(instName string, stdout io.Writer, sigintCh chan os.Signal, opts ...Opt
145147
udpDNSLocalPort: udpDNSLocalPort,
146148
tcpDNSLocalPort: tcpDNSLocalPort,
147149
instDir: inst.Dir,
150+
instName: instName,
148151
sshConfig: sshConfig,
149152
portForwarder: newPortForwarder(sshConfig, sshLocalPort, rules),
150153
qExe: qExe,
@@ -249,7 +252,10 @@ func (a *HostAgent) Run(ctx context.Context) error {
249252
}()
250253

251254
if *a.y.HostResolver.Enabled {
252-
dnsServer, err := dns.Start(a.udpDNSLocalPort, a.tcpDNSLocalPort, *a.y.HostResolver.IPv6)
255+
hosts := a.y.HostResolver.Hosts
256+
hosts["host.lima.internal."] = qemuconst.SlirpGateway
257+
hosts[fmt.Sprintf("lima-%s.", a.instName)] = qemuconst.SlirpIPAddress
258+
dnsServer, err := dns.Start(a.udpDNSLocalPort, a.tcpDNSLocalPort, *a.y.HostResolver.IPv6, hosts)
253259
if err != nil {
254260
return fmt.Errorf("cannot start DNS server: %w", err)
255261
}

pkg/limayaml/default.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,12 @@ hostResolver:
264264
enabled: null
265265
# Default: false
266266
ipv6: null
267+
# Static names can be defined here as an alternative to adding them to the hosts /etc/hosts.
268+
# Values can be either other hostnames, or IP addresses. The host.lima.internal name is
269+
# predefined to specify the gateway address to the host.
270+
hosts:
271+
# guest.name: 127.1.1.1
272+
# host.name: host.lima.internal
267273

268274
# If useHostResolver is false, then the following rules apply for configuring dns:
269275
# Explicitly set DNS addresses for qemu user-mode networking. By default qemu picks *one*

pkg/limayaml/defaults.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"path/filepath"
1111
"runtime"
1212
"strconv"
13+
"strings"
1314
"text/template"
1415

1516
"github.com/lima-vm/lima/pkg/guestagent/api"
@@ -188,6 +189,19 @@ func FillDefault(y, d, o *LimaYAML, filePath string) {
188189
y.SSH.ForwardAgent = pointer.Bool(false)
189190
}
190191

192+
hosts := make(map[string]string)
193+
// Values can be either names or IP addresses. Name values are canonicalized in the hostResolver.
194+
for k, v := range d.HostResolver.Hosts {
195+
hosts[Cname(k)] = v
196+
}
197+
for k, v := range y.HostResolver.Hosts {
198+
hosts[Cname(k)] = v
199+
}
200+
for k, v := range o.HostResolver.Hosts {
201+
hosts[Cname(k)] = v
202+
}
203+
y.HostResolver.Hosts = hosts
204+
191205
y.Provision = append(append(o.Provision, y.Provision...), d.Provision...)
192206
for i := range y.Provision {
193207
provision := &y.Provision[i]
@@ -498,3 +512,11 @@ func IsNativeArch(arch Arch) bool {
498512
nativeAARCH64 := arch == AARCH64 && runtime.GOARCH == "arm64"
499513
return nativeX8664 || nativeAARCH64
500514
}
515+
516+
func Cname(host string) string {
517+
host = strings.ToLower(host)
518+
if !strings.HasSuffix(host, ".") {
519+
host += "."
520+
}
521+
return host
522+
}

pkg/limayaml/defaults_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ func TestFillDefault(t *testing.T) {
9393
// All these slices and maps are empty in "builtin". Add minimal entries here to see that
9494
// their values are retained and defaults for their fields are applied correctly.
9595
y = LimaYAML{
96+
HostResolver: HostResolver{
97+
Hosts: map[string]string{
98+
"MY.Host": "host.lima.internal",
99+
},
100+
},
96101
Mounts: []Mount{
97102
{Location: "/tmp"},
98103
},
@@ -123,6 +128,10 @@ func TestFillDefault(t *testing.T) {
123128
}
124129

125130
expect := builtin
131+
expect.HostResolver.Hosts = map[string]string{
132+
"my.host.": "host.lima.internal",
133+
}
134+
126135
expect.Mounts = y.Mounts
127136
expect.Mounts[0].Writable = pointer.Bool(false)
128137
expect.Mounts[0].SSHFS.Cache = pointer.Bool(true)
@@ -202,6 +211,9 @@ func TestFillDefault(t *testing.T) {
202211
HostResolver: HostResolver{
203212
Enabled: pointer.Bool(false),
204213
IPv6: pointer.Bool(true),
214+
Hosts: map[string]string{
215+
"default": "localhost",
216+
},
205217
},
206218
PropagateProxyEnv: pointer.Bool(false),
207219

@@ -255,6 +267,9 @@ func TestFillDefault(t *testing.T) {
255267
expect.Containerd.Archives[0].Arch = *d.Arch
256268
expect.Mounts[0].SSHFS.Cache = pointer.Bool(true)
257269
expect.Mounts[0].SSHFS.FollowSymlinks = pointer.Bool(false)
270+
expect.HostResolver.Hosts = map[string]string{
271+
"default.": d.HostResolver.Hosts["default"],
272+
}
258273

259274
y = LimaYAML{}
260275
FillDefault(&y, &d, &LimaYAML{}, filePath)
@@ -277,6 +292,8 @@ func TestFillDefault(t *testing.T) {
277292
expect.Mounts = append(d.Mounts, y.Mounts...)
278293
expect.Networks = append(d.Networks, y.Networks...)
279294

295+
expect.HostResolver.Hosts["default."] = d.HostResolver.Hosts["default"]
296+
280297
// d.DNS will be ignored, and not appended to y.DNS
281298

282299
// "TWO" does not exist in filledDefaults.Env, so is set from d.Env
@@ -322,6 +339,9 @@ func TestFillDefault(t *testing.T) {
322339
HostResolver: HostResolver{
323340
Enabled: pointer.Bool(false),
324341
IPv6: pointer.Bool(false),
342+
Hosts: map[string]string{
343+
"override.": "underflow",
344+
},
325345
},
326346
PropagateProxyEnv: pointer.Bool(false),
327347

@@ -386,6 +406,9 @@ func TestFillDefault(t *testing.T) {
386406
expect.PortForwards = append(append(o.PortForwards, y.PortForwards...), d.PortForwards...)
387407
expect.Containerd.Archives = append(append(o.Containerd.Archives, y.Containerd.Archives...), d.Containerd.Archives...)
388408

409+
expect.HostResolver.Hosts["default."] = d.HostResolver.Hosts["default"]
410+
expect.HostResolver.Hosts["my.host."] = d.HostResolver.Hosts["host.lima.internal"]
411+
389412
// o.Mounts just makes d.Mounts[0] writable because the Location matches
390413
expect.Mounts = append(d.Mounts, y.Mounts...)
391414
expect.Mounts[0].Writable = pointer.Bool(true)

pkg/limayaml/limayaml.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,8 +137,9 @@ type Network struct {
137137
}
138138

139139
type HostResolver struct {
140-
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
141-
IPv6 *bool `yaml:"ipv6,omitempty" json:"ipv6,omitempty"`
140+
Enabled *bool `yaml:"enabled,omitempty" json:"enabled,omitempty"`
141+
IPv6 *bool `yaml:"ipv6,omitempty" json:"ipv6,omitempty"`
142+
Hosts map[string]string `yaml:"hosts,omitempty" json:"hosts,omitempty"`
142143
}
143144

144145
// DEPRECATED types below

0 commit comments

Comments
 (0)