diff --git a/cmd/start.go b/cmd/start.go index 5d2755e5..f6503000 100644 --- a/cmd/start.go +++ b/cmd/start.go @@ -45,6 +45,7 @@ Run 'colima template' to set the default configurations or 'colima start --edit' " colima start --arch aarch64\n" + " colima start --dns 1.1.1.1 --dns 8.8.8.8\n" + " colima start --dns-host example.com=1.2.3.4\n" + + " colima start --gateway-address 192.168.6.2\n" + " colima start --kubernetes --k3s-arg='\"--disable=coredns,servicelb,traefik,local-storage,metrics-server\"'", Args: cobra.MaximumNArgs(1), RunE: func(cmd *cobra.Command, args []string) error { @@ -210,6 +211,9 @@ func init() { } } + // Gateway Address + startCmd.Flags().StringVar(&startCmdArgs.Network.GatewayAddress, "gateway-address", "192.168.5.2", "gateway address") + // binfmt startCmd.Flags().BoolVar(&startCmdArgs.Flags.Binfmt, "binfmt", true, binfmtDesc) @@ -518,6 +522,9 @@ func prepareConfig(cmd *cobra.Command) { if !cmd.Flag("dns-host").Changed { startCmdArgs.Network.DNSHosts = current.Network.DNSHosts } + if !cmd.Flag("gateway-address").Changed { + startCmdArgs.Network.GatewayAddress = current.Network.GatewayAddress + } if !cmd.Flag("env").Changed { startCmdArgs.Env = current.Env } diff --git a/config/config.go b/config/config.go index dc3c82f7..f33e5af5 100644 --- a/config/config.go +++ b/config/config.go @@ -88,6 +88,7 @@ type Network struct { Mode string `yaml:"mode"` // shared, bridged BridgeInterface string `yaml:"interface"` PreferredRoute bool `yaml:"preferredRoute"` + GatewayAddress string `yaml:"gatewayAddress"` } // Mount is volume mount diff --git a/config/configmanager/configmanager.go b/config/configmanager/configmanager.go index 111a5715..9457936a 100644 --- a/config/configmanager/configmanager.go +++ b/config/configmanager/configmanager.go @@ -2,6 +2,7 @@ package configmanager import ( "fmt" + "net" "os" "strings" @@ -80,6 +81,12 @@ func ValidateConfig(c config.Config) error { return fmt.Errorf("invalid port forwarder: '%s'", c.PortForwarder) } + if c.Network.GatewayAddress != "" { + if err := validateGatewayAddress(c.Network.GatewayAddress); err != nil { + return err + } + } + return nil } @@ -108,3 +115,27 @@ func Teardown() error { } return nil } + +// Validates that gateway is a valid IPv4 address and that the last octet is “2”. +// Lima uses the last octet as 2 for gateways. +func validateGatewayAddress(gateway string) error { + ip := net.ParseIP(gateway) + if ip == nil { + return fmt.Errorf("gateway %q is not a valid IP address", gateway) + } + ip4 := ip.To4() + if ip4 == nil { + return fmt.Errorf("gateway %q is not IPv4", gateway) + } + + parts := strings.Split(gateway, ".") + if len(parts) != 4 { + return fmt.Errorf("gateway %q does not have 4 octets", gateway) + } + + if parts[3] != "2" { + return fmt.Errorf("the last octet of gateway %q is not 2", gateway) + } + + return nil +} diff --git a/embedded/defaults/colima.yaml b/embedded/defaults/colima.yaml index b76d04d0..d13dab49 100644 --- a/embedded/defaults/colima.yaml +++ b/embedded/defaults/colima.yaml @@ -107,6 +107,15 @@ network: # Default: false hostAddresses: false + # Custom gateway address for the virtual machine. + # The last octet needs to be 2. + # + # EXAMPLE + # gatewayAddress: 192.168.10.2 + # + # Default: 192.168.5.2 + gatewayAddress: 192.168.5.2 + # ===================================================================== # # ADVANCED CONFIGURATION # ===================================================================== # diff --git a/environment/vm/lima/dns.go b/environment/vm/lima/dns.go index 00a9f1f8..ca67b092 100644 --- a/environment/vm/lima/dns.go +++ b/environment/vm/lima/dns.go @@ -9,15 +9,10 @@ import ( ) const ( - localhostAddr = "127.0.0.1" - gatewayAddr = "192.168.5.2" + localhostAddr = "127.0.0.1" + defaultGatewayAddress = "192.168.5.2" ) -var dnsHosts = map[string]string{ - "host.docker.internal": gatewayAddr, - "host.lima.internal": gatewayAddr, -} - func hasDnsmasq(l *limaVM) bool { // check if dnsmasq is installed return l.RunQuiet("sh", "-c", `apt list | grep 'dnsmasq\/' | grep '\[installed'`) == nil @@ -30,6 +25,18 @@ func (l *limaVM) setupDNS(conf config.Config) error { return nil } + // use custom gateway address + var gatewayAddr = defaultGatewayAddress + customGatewayAddress := conf.Network.GatewayAddress + if customGatewayAddress != "" { + gatewayAddr = customGatewayAddress + } + + var dnsHosts = map[string]string{ + "host.docker.internal": gatewayAddr, + "host.lima.internal": gatewayAddr, + } + internalIP := limautil.InternalIPAddress(config.CurrentProfile().ID) // extra dns entries diff --git a/environment/vm/lima/lima.go b/environment/vm/lima/lima.go index cc891786..639cd33f 100644 --- a/environment/vm/lima/lima.go +++ b/environment/vm/lima/lima.go @@ -111,7 +111,8 @@ func (l *limaVM) Start(ctx context.Context, conf config.Config) error { return yamlutil.WriteYAML(l.limaConf, confFile) }) - a.Add(l.writeNetworkFile) + a.Add(func() error { return l.writeNetworkFile(conf) }) + a.Add(func() error { return l.host.Run(limactl, "start", "--tty=false", confFile) }) @@ -166,7 +167,7 @@ func (l *limaVM) resume(ctx context.Context, conf config.Config) error { return err }) - a.Add(l.writeNetworkFile) + a.Add(func() error { return l.writeNetworkFile(conf) }) a.Stage("starting") a.Add(func() error { diff --git a/environment/vm/lima/limautil/network.go b/environment/vm/lima/limautil/network.go index c2ceefac..10f63f26 100644 --- a/environment/vm/lima/limautil/network.go +++ b/environment/vm/lima/limautil/network.go @@ -51,3 +51,15 @@ func getIPAddress(profileID, interfaceName string) string { _ = cmd.Run() return strings.TrimSpace(buf.String()) } + +type LimaNetworkConfig struct { + Mode string `yaml:"mode"` + Gateway string `yaml:"gateway"` + Netmask string `yaml:"netmask"` +} + +type LimaNetwork struct { + Networks struct { + UserV2 LimaNetworkConfig `yaml:"user-v2"` + } `yaml:"networks"` +} diff --git a/environment/vm/lima/network.go b/environment/vm/lima/network.go index df01f32d..ddd2dedc 100644 --- a/environment/vm/lima/network.go +++ b/environment/vm/lima/network.go @@ -11,15 +11,33 @@ import ( "github.com/abiosoft/colima/environment/vm/lima/limautil" "github.com/abiosoft/colima/util" "github.com/sirupsen/logrus" + "gopkg.in/yaml.v3" ) -func (l *limaVM) writeNetworkFile() error { +func (l *limaVM) writeNetworkFile(conf config.Config) error { networkFile := limautil.NetworkFile() embeddedFile, err := embedded.Read("network/networks.yaml") if err != nil { return fmt.Errorf("error reading embedded network config file: %w", err) } + // use custom gateway address + gatewayAddress := conf.Network.GatewayAddress + if gatewayAddress != "" { + var cfg limautil.LimaNetwork + if err := yaml.Unmarshal(embeddedFile, &cfg); err != nil { + return fmt.Errorf("error unmarshalling the `networks.yaml` file: %w", err) + } + + cfg.Networks.UserV2.Gateway = gatewayAddress + + out, err := yaml.Marshal(&cfg) + if err != nil { + return fmt.Errorf("error marshalling the `networks.yaml` file: %w", err) + } + embeddedFile = out + } + // if there are no running instances, clear network directory if instances, err := limautil.RunningInstances(); err == nil && len(instances) == 0 { if err := os.RemoveAll(limautil.NetworkAssetsDirectory()); err != nil {