Repository: GitHub
The Podman managed machine is a virtual machine (VM) and is enabled and managed via the Podman command line interface (CLI). QEMU is used as the "hypervisor". On non-Linux hosts Podman requires a VM (or another remote host) in order to orchestrate containers and container images. The approach that Podman has taken is highly similar to that of the Docker Desktop approach which is described briefly described in Podman as a Replacement for Docker Desktop ( Red Hat The Source Article: Podman as a Replacement for Docker Desktop).
1. Rationale
By default the Podman managed machine is accessible from the host system, including for remote access via Secure Shell
and Secure Copy (ssh
and scp
respectively). There are times when one may need (or wish) to enable similar
connectivity from the VM (guest) to the host.
One such example is in restrictive environments such as corporation or government environments that require a proxy to access external resources beyond the trusted security enclave. In some environments the proxy infrastructure requires authentication. In a circumstance such as this it is not an option to store security credentials in a configuration file let alone embed them in some in some script. In this case the organization may have an inner source solution (e.g.; a proxy for a proxy) to handle the case of CLI (Command Line Interface) tools that are not authenticating proxy aware.
Another example may simply be that one is testing out a number of things in an immutable system such as Fedora CoreOS
(FCOS) and simply wants to transfer the result of this work back to the host via Secure Copy (scp
) from the guest back
to the host.
Of course with host to guest SSH and this |
2. How to Enable
All approaches have been tested on Podman 3.4.4 and Podman 4.1.0 and on Linux and MacOS. |
The way we enable this is to modify the QEMU command line for the Podman managed machine. Before we can do this we must first create the Podman managed machine:
podman machine init
Once the Podman VM is created from podman machine init
and prior to starting it we merely modify the managed machine
JSON configuration file for the QEMU command line. We add a secondary NIC to the VM by appending the following QEMU
options to the configuration file:
-device virtio-net-pci,netdev=net1 -netdev user,id=net1
We can easily make use of jq to do this:
jq \
'.CmdLine += ["-device", "virtio-net-pci,netdev=net1", "-netdev", "user,id=net1"]' \
~/.config/containers/podman/machine/qemu/podman-machine-default.json > \
~/.config/containers/podman/machine/qemu/podman-machine-default.json.1
mv \
~/.config/containers/podman/machine/qemu/podman-machine-default.json.1 \
~/.config/containers/podman/machine/qemu/podman-machine-default.json
2.1. Validation
This is easily validated as such:
podman machine ssh ip address
Shows us the secondary NIC has been added and is on an entirely new private network: 10.0.2.0/24
and has received the
IP 10.0.2.15
from QEMU:
3: enp0s4: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether 52:54:00:12:34:56 brd ff:ff:ff:ff:ff:ff
inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic noprefixroute enp0s4
valid_lft 86276sec preferred_lft 86276sec
inet6 fec0::cb8a:cff7:4ca9:ab65/64 scope site dynamic noprefixroute
valid_lft 86279sec preferred_lft 14279sec
inet6 fe80::7e27:585d:5391:2a5e/64 scope link noprefixroute
valid_lft forever preferred_lft forever
And a new route has now been created as well:
podman machine ssh route -n
Yields:
Kernel IP routing table
Destination Gateway Genmask Flags Metric Ref Use Iface
0.0.0.0 192.168.127.1 0.0.0.0 UG 100 0 0 enp0s1
0.0.0.0 10.0.2.2 0.0.0.0 UG 101 0 0 enp0s4
10.0.2.0 0.0.0.0 255.255.255.0 U 101 0 0 enp0s4
192.168.127.0 0.0.0.0 255.255.255.0 U 100 0 0 enp0s1
Take note of the default gateway of 10.0.2.2
. This is the special IP that QEMU will accept connections from a VM to
the host, provided that there is a service bound to the host port.
2.2. Testing
Now we test our connectivity.
2.2.1. Netcat
On the host we will wait for an inbound connection and provide a response back to the remote TCP client.
cat <<EOM | nc -lv -w 1 8080
HTTP/1.1 200 OK
Hello from $(hostname -f)
EOM
Here we bind netcat on port 8080
to all available host network interfaces, wait for a connection, provide a response
back from an inline heredoc that also includes the FQDN of the host and then terminates the connection.
And from the Podman machine we will connect to port 8080
on the host:
podman machine ssh curl -iv http://10.0.2.2:8080
Which yields:
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
< HTTP/1.1 200 OK
HTTP/1.1 200 OK
* no chunk, no close, no size. Assume close to signal end
<
Hello from mac-mini.n7.priv
* Closing connection 0
Excellent, now, we will further illustrate by spinning up a one line HTTP server listening on all host interfaces, bind
it to port 8080
and fulfilling HTTP requests from the user home directory. On the host:
2.2.2. Simple HTTP Server
python3 -m http.server --directory ~ 8080
And from the Podman machine we will connect to port 8080 on the host:
podman machine ssh curl -iv http://10.0.2.2:8080
Which yields:
* Trying 127.0.0.1:8080...
* Connected to localhost (127.0.0.1) port 8080 (#0)
> GET / HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.79.1
> Accept: */*
>
* Mark bundle as not supporting multiuse
* HTTP 1.0, assume close after body
< HTTP/1.0 200 OK
HTTP/1.0 200 OK
< Server: SimpleHTTP/0.6 Python/3.9.13
Server: SimpleHTTP/0.6 Python/3.9.13
< Date: Wed, 08 Jun 2022 20:05:28 GMT
Date: Wed, 08 Jun 2022 20:05:28 GMT
< Content-type: text/html; charset=utf-8
Content-type: text/html; charset=utf-8
< Content-Length: 1509
Content-Length: 1509
<
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Directory listing for /</title>
</head>
<body>
<h1>Directory listing for /</h1>
<hr>
<ul>
<li><a href=".cache/">.cache/</a></li>
<li><a href=".CFUserTextEncoding">.CFUserTextEncoding</a></li>
<li><a href=".config/">.config/</a></li>
<li><a href=".lesshst">.lesshst</a></li>
<li><a href=".local/">.local/</a></li>
<li><a href=".oh-my-zsh/">.oh-my-zsh/</a></li>
<li><a href=".p10k.zsh">.p10k.zsh</a></li>
<li><a href=".ssh/">.ssh/</a></li>
<li><a href=".Trash/">.Trash/</a></li>
<li><a href=".vim/">.vim/</a></li>
<li><a href=".viminfo">.viminfo</a></li>
<li><a href=".vimrc">.vimrc</a></li>
<li><a href=".vimrc.local">.vimrc.local</a></li>
<li><a href=".zcompdump">.zcompdump</a></li>
<li><a href=".zcompdump-mac-mini-5.8">.zcompdump-mac-mini-5.8</a></li>
<li><a href=".zsh/">.zsh/</a></li>
<li><a href=".zsh_history">.zsh_history</a></li>
<li><a href=".zsh_sessions/">.zsh_sessions/</a></li>
<li><a href=".zshrc">.zshrc</a></li>
<li><a href="Desktop/">Desktop/</a></li>
<li><a href="Documents/">Documents/</a></li>
<li><a href="Downloads/">Downloads/</a></li>
<li><a href="Library/">Library/</a></li>
<li><a href="Movies/">Movies/</a></li>
<li><a href="Music/">Music/</a></li>
<li><a href="Pictures/">Pictures/</a></li>
<li><a href="projects/">projects/</a></li>
<li><a href="Public/">Public/</a></li>
</ul>
<hr>
</body>
</html>
* Closing connection 0
3. Summary
We have shown how to enable Podman managed machine (virtual machine, VM) connectivity to the host itself with minimal effort. This approach can easily be extended to enabling integration from the guest to the host. There exists restrictive environments such that access to external Internet resources are constrained by using authenticating proxy servers must be used on the internal security enclave and that one desires to orchestrate a proxy for proxy. This is merely only one singular use case.