Skip to content

Commit

Permalink
Merge branch 'podman'
Browse files Browse the repository at this point in the history
  • Loading branch information
jasonmadigan committed Mar 27, 2024
2 parents 15fda7f + f39e372 commit 3e00d73
Show file tree
Hide file tree
Showing 12 changed files with 810 additions and 211 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
podman-mac-net-connect
18 changes: 9 additions & 9 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,19 +1,19 @@
PROJECT := github.com/chipmk/docker-mac-net-connect
SETUP_IMAGE := ghcr.io/chipmk/docker-mac-net-connect/setup
VERSION := $(shell git describe --tags)
PROJECT := github.com/jasonmadigan/podman-mac-net-connect
SETUP_IMAGE := quay.io/jasonmadigan/podman-mac-net-connect
VERSION := 0.0.1
LD_FLAGS := -X ${PROJECT}/version.Version=${VERSION} -X ${PROJECT}/version.SetupImage=${SETUP_IMAGE}

run:: build-docker run-go
build:: build-docker build-go
run:: build-podman run-go
build:: build-podman build-go

run-go::
go run -ldflags "${LD_FLAGS}" ${PROJECT}

build-go::
go build -ldflags "-s -w ${LD_FLAGS}" ${PROJECT}

build-docker::
docker build -t ${SETUP_IMAGE}:${VERSION} ./client
build-podman::
podman build -t ${SETUP_IMAGE}:${VERSION} ./client

build-push-docker::
docker buildx build --platform linux/amd64,linux/arm64 --push -t ${SETUP_IMAGE}:${VERSION} ./client
build-push-quay::
podman buildx build --platform linux/amd64,linux/arm64 --push -t ${SETUP_IMAGE}:${VERSION} ./client
96 changes: 64 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,27 +1,33 @@
# Docker Mac Net Connect
# Podman Mac Net Connect

> Connect directly to Docker-for-Mac containers via IP address.
> **Note:** This project is a fork of [chipmk/docker-mac-net-connect](https://github.com/chipmk/docker-mac-net-connect) for Docker, an excellent tool that inspired this version for Podman.
> Connect directly to [Podman](https://podman.io/) containers on macOS via IP address.
## Features

- **L3 connectivity:** Connect to Docker containers from macOS host (without port binding).
- **L3 connectivity:** Connect to podman containers from macOS host (without port binding).
- **Lightweight:** Based on WireGuard (built-in to Linux kernel).
- **Hands-off:** Install once and forget. No need to re-configure every time you restart your Mac or Docker daemon.
- **Hands-off:** Install once and forget. No need to re-configure every time you restart your Mac or podman machines.
- **Automatic:** Docker networks are automatically added/removed from macOS routing table.
- **No bloat:** Everything is handled by a single binary. No external dependencies/tools are needed.

## Requirements

You must be using Docker Desktop v3.6.0 or higher to use this tool (see https://github.com/chipmk/docker-mac-net-connect/issues/10#issuecomment-1146662058).
You must be using podman for macOS v5.0.0 or higher to use this tool.

## Notes

The `wireguard` kernel module is not enabled in podman machines out of the box. Running this service will see it check for the existence of this module and, if necessary, enable it via via a podman system connection as root. This likely only works on `--rootful` podman machines for the time being.

## Installation

```bash
# Install via Homebrew
$ brew install chipmk/tap/docker-mac-net-connect
$ brew install jasonmadigan/tap/podman-mac-net-connect

# Run the service and register it to launch at boot
$ sudo brew services start chipmk/tap/docker-mac-net-connect
$ sudo brew services start jasonmadigan/tap/podman-mac-net-connect
```

### `GOPROXY` support
Expand All @@ -31,7 +37,7 @@ This Homebrew formulae is built using `go`. When Homebrew installs a formulae, i
Some users require changing `GOPROXY` due to firewalls. This formulae adds special support for `GOPROXY` using `HOMEBREW_GOPROXY`:

```bash
HOMEBREW_GOPROXY=https://my-proxy-url brew install chipmk/tap/docker-mac-net-connect
HOMEBREW_GOPROXY=https://my-proxy-url brew install jasonmadigan/tap/podman-mac-net-connect
```

## Usage
Expand All @@ -40,38 +46,64 @@ After installing, you will be able to do this:

```bash
# Run an nginx container
$ docker run --rm --name nginx -d nginx
podman run --rm --name nginx -d nginx
```

```bash
# Get the internal IP for the container
$ docker inspect nginx --format '{{.NetworkSettings.IPAddress}}'
172.17.0.2
podman inspect nginx --format '{{.NetworkSettings.IPAddress}}'
```

```bash
# internal IP for the container
10.88.0.19
```

```bash
# Make an HTTP request directly to its IP
$ curl -I 172.17.0.2
curl -I 10.88.0.19
```

```bash
# Response
HTTP/1.1 200 OK
Server: nginx/1.21.3
Date: Thu, 11 Nov 2021 21:00:37 GMT
Server: nginx/1.25.4
Date: Wed, 27 Mar 2024 08:59:22 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Tue, 07 Sep 2021 15:21:03 GMT
Last-Modified: Wed, 14 Feb 2024 16:03:00 GMT
Connection: keep-alive
ETag: "6137835f-267"
ETag: "65cce434-267"
Accept-Ranges: bytes
```

## Development

To run locally, build the client image:

```bash
make build-podman
```

Run (as root):

```bash
sudo make run-go
```

## Background

Accessing containers directly by IP (instead of port binding) can be useful and convenient.

### Problem

Unlike Docker on Linux, Docker-for-Mac does not expose container networks directly on the macOS host. Docker-for-Mac works by running a Linux VM under the hood (using [`hyperkit`](https://github.com/moby/hyperkit)) and creates containers within that VM.
Podman on macOS does not expose container networks directly on the macOS host. Podman on macOS works by running a Linux VM under the hood (using [`qemu`](https://www.qemu.org/)) and creates containers within that VM.

Docker-for-Mac supports connecting to containers over Layer 4 (port binding), but not Layer 3 (by IP address).
Podman on macOS supports connecting to containers over Layer 4 (port binding), but not Layer 3 (by IP address).

### Solution

Create a minimal network tunnel between macOS and the Docker Desktop Linux VM. The tunnel is implemented using WireGuard.
Create a minimal network tunnel between macOS and the Linux VM running Podman's containers. The tunnel is implemented using WireGuard.

### Why WireGuard?

Expand All @@ -83,7 +115,7 @@ WireGuard is an extremely lightweight and fast VPN. It’s also built in to the

### macOS side

A lightweight customized WireGuard server (_`docker-mac-net-connect`_) runs on your macOS host and creates a virtual network interface (`utun`) that acts as the link between your Mac and the Docker Desktop Linux VM.
A lightweight customized WireGuard server (_`podman-mac-net-connect`_) runs on your macOS host and creates a virtual network interface (`utun`) that acts as the link between your Mac and the Docker Desktop Linux VM.

### Linux VM side

Expand All @@ -95,14 +127,14 @@ The container creates the interface, configures WireGuard, then exits and is des

### Tying it together

The server on macOS monitors your Docker container networks and automatically adds their subnets to your macOS routing table (routing through the `utun` interface). Now you can connect to any container directly by it’s IP address from your macOS host. Eg.
The server on macOS monitors your podman container networks and automatically adds their subnets to your macOS routing table (routing through the `utun` interface). Now you can connect to any container directly by it’s IP address from your macOS host. Eg.

```bash
# Run an nginx container
$ docker run --rm --name nginx -d nginx
$ podman run --rm --name nginx -d nginx

# Get the internal IP for the container
$ docker inspect nginx --format '{{.NetworkSettings.IPAddress}}'
$ podman inspect nginx --format '{{.NetworkSettings.IPAddress}}'
172.17.0.2

# Make an HTTP request directly to its IP
Expand Down Expand Up @@ -144,31 +176,31 @@ Network traffic runs directly between the macOS host and local Linux VM - no ext

### Can I use this in production?

This tool was designed to assist with development on macOS. Since Docker-for-Mac isn't designed for production workloads, neither is this.
This tool was designed to assist with development on macOS, only.

### What happens if Docker Desktop restarts?
### What happens if a podman machine restarts?

The server detects when the Docker daemon stops and automatically reconfigures the tunnel when it starts back up.
The server detects when the podman stops and automatically reconfigures the tunnel when it starts back up.

### Do you add/remove routes when Docker networks change?
### Do you add/remove routes when Podman networks change?

Yes, the server watches the Docker daemon for both network creations and deletions and will add/remove routes accordingly.
Yes, the server watches the Podman daemon for both network creations and deletions and will add/remove routes accordingly.

For example, let's create a Docker network with subnet `172.200.0.0/16`:
For example, let's create a Podman network with subnet `172.200.0.0/16`:

```bash
# First validate that no route exists for the subnet
sudo netstat -rnf inet | grep 172.200

# Create the docker network
$ docker network create --subnet 172.200.0.0/16 my-network
podman network create --subnet 172.200.0.0/16 my-network

# Check the routing table - a new route exists
$ sudo netstat -rnf inet | grep 172.200
sudo netstat -rnf inet | grep 172.200
172.200 utun0 USc utun0

# Remove the docker network
$ docker network rm my-network
podman network rm my-network

# The route has been removed
sudo netstat -rnf inet | grep 172.200
Expand Down
3 changes: 2 additions & 1 deletion client/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ RUN go build -o app main.go
FROM debian:11-slim

RUN apt-get update && apt-get install -y \
iptables
iptables \
kmod

COPY --from=build /build/app .

Expand Down
2 changes: 1 addition & 1 deletion client/go.mod
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module github.com/chipmk/docker-mac-net-connect/client
module github.com/jasonmadigan/podman-mac-net-connect/client

go 1.17

Expand Down
9 changes: 6 additions & 3 deletions client/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const (
)

func main() {
interfaceName := "chip0"
interfaceName := "mad0"

serverPortString := os.Getenv("SERVER_PORT")
if serverPortString == "" {
Expand Down Expand Up @@ -126,13 +126,13 @@ func main() {
os.Exit(ExitSetupFailed)
}

ips, err := net.LookupIP("host.docker.internal")
ips, err := net.LookupIP("host.containers.internal")
if err != nil || len(ips) == 0 {
fmt.Printf("Failed to lookup IP: %v\n", err)
os.Exit(ExitSetupFailed)
}

persistentKeepaliveInterval, err := time.ParseDuration("25s")
persistentKeepaliveInterval, err := time.ParseDuration("5s")
if err != nil {
fmt.Printf("Failed to parse duration: %v\n", err)
os.Exit(ExitSetupFailed)
Expand All @@ -156,6 +156,9 @@ func main() {
})
if err != nil {
fmt.Printf("Failed to configure wireguard device: %v\n", err)
fmt.Printf("%v", interfaceName)
fmt.Printf("%v", ips[0])
fmt.Printf("done.")
os.Exit(ExitSetupFailed)
}

Expand Down
Binary file added docker-mac-net-connect
Binary file not shown.
Loading

0 comments on commit 3e00d73

Please sign in to comment.