Subscribe to the blog feed .

This project is maintained by the containers organization.

Programmatic remote access to Podman via the varlink protocol

By Harald Hoyer GitHub Twitter

This guide shows how to access Podman remotely via the varlink interface with CLI tools and programmatically with python, go and rust.

This should work on Linux, MacOS and Windows 10.

The compatibility matrix shows which feature is supported on which OS in which language.

Note: replace <podman-machine> in this guide with the IP or hostname of your Podman machine

Prerequisites

Windows ssh

If you are on a windows client machine, install the OpenSSH Client built by Microsoft in a cmd.exe in admin mode:

> dism /online /Add-Capability /CapabilityName:OpenSSH.Client~~~~0.0.1.0

Close cmd.exe window.

Note: Works also with other ssh clients, e.g. ssh from Git Bash.

Generate ssh keys

If you don’t want to type your password all the time, or not use an ssh agent, set an empty password.

$ ssh-keygen -f ~/.ssh/podmanuser

Set up Podman on the Fedora/RHEL machine

$ sudo yum install podman libvarlink-util $ sudo groupadd podman

Copy /lib/tmpfiles.d/podman.conf to /etc/tmpfiles.d/podman.conf .

$ sudo cp /lib/tmpfiles.d/podman.conf /etc/tmpfiles.d/podman.conf

Edit /etc/tmpfiles.d/podman.conf :

d /run/podman 0750 root podman

Copy /lib/systemd/system/io.podman.socket to /etc/systemd/system/io.podman.socket .

$ sudo cp /lib/systemd/system/io.podman.socket /etc/systemd/system/io.podman.socket

Edit section [Socket] of /etc/systemd/system/io.podman.socket :

[Socket] ListenStream=/run/podman/io.podman SocketMode=0660 SocketGroup=podman

Then activate the changes:

$ sudo systemctl daemon-reload $ sudo systemd-tmpfiles --create $ sudo systemctl enable --now io.podman.socket

The directory and socket now belongs to the podman group

$ sudo ls -al /run/podman drwxr-x---. 2 root podman 60 14. Jan 14:50 . drwxr-xr-x. 51 root root 1420 14. Jan 14:36 .. srw-rw----. 1 root podman 0 14. Jan 14:50 io.podman

Note: Wouldn’t it be nice, if there was a Podman group owning the socket already? ;-)

Now we are adding a user podmanuser and set a password:

$ sudo useradd podmanuser -G podman $ sudo passwd podmanuser

From your client machine do

$ ssh-copy-id -f ~/.ssh/podmanuser podmanuser@<podman-machine>

ssh config

Edit .ssh/config

Host <podman-machine> RequestTTY no IdentityFile ~/.ssh/podmanuser User podmanuser VisualHostKey no RemoteCommand /usr/bin/varlink bridge --connect unix:/run/podman/io.podman GSSAPIAuthentication no ForwardX11 no

Optional Lock Down

Log into <podman-machine>

$ ssh podmanuser@<podman-machine>

Now we lock down podmanuser to only be used with the varlink bridge from your client machine:

Edit .ssh/authorized-keys so that the line begins with:

command="/usr/bin/varlink bridge --connect unix:/run/podman/io.podman",no-agent-forwarding,no-port-forwarding,no-pty,no-user-rc,no-X11-forwarding ssh-rsa […]

Log out of <podman-machine>

Python

Install Python

https://www.python.org/downloads/

Install varlink for Python

$ pip install --user "varlink>=30.0.2"

Test if the varlink cli module works

$ python -m varlink.cli --help usage: cli.py [ -h ] [ -r RESOLVER] [ -A ACTIVATE] [ -b BRIDGE] { info,help,bridge,call } ... …

Interfacing Podman with the python cli module

$ python -m varlink.cli --bridge "ssh <podman-machine>" info info .1:1234 Vendor: Atomic Product: podman Version: 0.10.1 URL: https://github.com/containers/podman Interfaces: org.varlink.service io.podman $ python -m varlink.cli --bridge "ssh <podman-machine>" call io.podman.Ping {} { "ping" : { "message" : "OK" } }

Python Client Example

podmanclient.py :

import varlink with varlink . Client . new_with_bridge ([ "ssh" , "<podman-machine>" ]) as client : with client . open ( "io.podman" ) as podman : print ( podman . Ping ()) print ( podman . GetInfo ()) print ( podman . GetVersion ()) info = podman . GetInfo () print ( "Uptime:" , info [ "info" ][ "host" ][ "uptime" ]) print ( "Os:" , info [ "info" ][ "host" ][ "os" ]) try : podman . MountContainer ( "container-id" ) except varlink . error . VarlinkError as e : print ( e . error (), e . parameters ()) print ( e . as_dict ())

To find out more about the Podman varlink interface read the io.podman.varlink file or the rendered API.md.

Or you can inspect, what methods your Podman version on <podman-machine> provides:

$ python -m varlink.cli --bridge "ssh <podman-machine>" help io.podman

Go

Installation

$ go get -u github.com/varlink/go/varlink $ go install github.com/varlink/go/cmd/varlink $ go install github.com/varlink/go/cmd/varlink-go-interface-generator

Running the varlink CLI command

The varlink CLI command in $GOPATH/bin should output:

$ varlink --bridge "ssh <podman-machine>" info Vendor: Atomic Product: podman Version: 0.10.1 URL: https://github.com/containers/podman Interfaces: org.varlink.service io.podman $ varlink --bridge "ssh <podman-machine>" call io.podman.Ping { "ping" : { "message" : "OK" } } $ varlink --bridge "ssh <podman-machine>" call io.podman.MountContainer "{ \" name \" : \" container-id \" }" Error: Call failed with error: io.podman.ErrorOccurred { "reason" : "no container with name or ID container-id found: no such container" }

To find out more about the Podman varlink interface read the io.podman.varlink file or the rendered API.md.

Or you can inspect, what methods your Podman version on <podman-machine> provides:

$ varlink --bridge "ssh <podman-machine>" help io.podman

Go Client Example

Either clone this repository or:

Create a new go project. Create a sub directory iopodman in the project.

Create the io.podman.varlink either from the podman github sources or dynamically with:

$ varlink --bridge "ssh <podman-machine>" help io.podman > iopodman/io.podman.varlink

Create iopodman/generate.go:

package iopodman //go:generate $GOPATH/bin/varlink-go-interface-generator io.podman.varlink

Run go generate :

$ go generate ./...

Create your main.go:

package main import ( "flag" "fmt" "github.com/haraldh/podmangoexampleclient/iopodman" "github.com/varlink/go/varlink" "io" "os" ) func printError ( methodname string , err error ) { fmt . Fprintf ( os . Stderr , "Error calling %s: " , methodname ) switch e := err . ( type ) { case * iopodman . ImageNotFound : //error ImageNotFound (name: string) fmt . Fprintf ( os . Stderr , "'%v' name='%s'

" , e , e . Name ) case * iopodman . ContainerNotFound : //error ContainerNotFound (name: string) fmt . Fprintf ( os . Stderr , "'%v' name='%s'

" , e , e . Name ) case * iopodman . NoContainerRunning : //error NoContainerRunning () fmt . Fprintf ( os . Stderr , "'%v'

" , e ) case * iopodman . PodNotFound : //error PodNotFound (name: string) fmt . Fprintf ( os . Stderr , "'%v' name='%s'

" , e , e . Name ) case * iopodman . PodContainerError : //error PodContainerError (podname: string, errors: []PodContainerErrorData) fmt . Fprintf ( os . Stderr , "'%v' podname='%s' errors='%v'

" , e , e . Podname , e . Errors ) case * iopodman . NoContainersInPod : //error NoContainersInPod (name: string) fmt . Fprintf ( os . Stderr , "'%v' name='%s'

" , e , e . Name ) case * iopodman . ErrorOccurred : //error ErrorOccurred (reason: string) fmt . Fprintf ( os . Stderr , "'%v' reason='%s'

" , e , e . Reason ) case * iopodman . RuntimeError : //error RuntimeError (reason: string) fmt . Fprintf ( os . Stderr , "'%v' reason='%s'

" , e , e . Reason ) case * varlink . InvalidParameter : fmt . Fprintf ( os . Stderr , "'%v' parameter='%s'

" , e , e . Parameter ) case * varlink . MethodNotFound : fmt . Fprintf ( os . Stderr , "'%v' method='%s'

" , e , e . Method ) case * varlink . MethodNotImplemented : fmt . Fprintf ( os . Stderr , "'%v' method='%s'

" , e , e . Method ) case * varlink . InterfaceNotFound : fmt . Fprintf ( os . Stderr , "'%v' interface='%s'

" , e , e . Interface ) case * varlink . Error : fmt . Fprintf ( os . Stderr , "'%v' parameters='%v'

" , e , e . Parameters ) default : if err == io . EOF { fmt . Fprintf ( os . Stderr , "Connection closed

" , ) } else if err == io . ErrUnexpectedEOF { fmt . Fprintf ( os . Stderr , "Connection aborted

" , ) } else { fmt . Fprintf ( os . Stderr , "%T - '%v'

" , err , err ) } } } func main () { var c * varlink . Connection var err error c , err = varlink . NewBridge ( "ssh <podman-machine>" ) if err != nil { fmt . Fprintf ( os . Stderr , "Error connecting: %T - '%v'

" , err , err ) os . Exit ( 1 ) } // Be nice and cleanup defer c . Close () info , err := iopodman . GetInfo () . Call ( c ) if err != nil { printError ( "GetInfo()" , err ) os . Exit ( 1 ) } fmt . Printf ( "Info: %+v



" , info ) fmt . Printf ( "Podman Version: %+v



" , info . Podman . Podman_version ) containers , err := iopodman . ListContainers () . Call ( c ) if err != nil { printError ( "ListContainers()" , err ) os . Exit ( 1 ) } for container := range containers { print ( container ) } mount , err := iopodman . MountContainer () . Call ( c , "foo" ) if err != nil { printError ( "MountContainer()" , err ) } else { print ( mount ) } }

Rust

Install the rust toolchain

Windows

First install the C++ part of https://visualstudio.microsoft.com/downloads/

All

https://rustup.rs/

Install varlink-cli

For non-Linux systems:

$ cargo install varlink-cli

Note: Ensure that $HOME/.cargo/bin is in your PATH or copy $HOME/.cargo/bin/varlink in one of your path directories

For Linux systems:

You can also use varlink util from libvarlink or install libvarlink-util on Fedora/RHEL machines.

Running the varlink CLI command

The varlink CLI command in ~/.cargo/bin should output:

$ varlink --bridge "ssh <podman-machine>" info Vendor: Atomic Product: podman Version: 0.10.1 URL: https://github.com/containers/podman Interfaces: org.varlink.service io.podman $ varlink --bridge "ssh <podman-machine>" call io.podman.Ping { "ping" : { "message" : "OK" } } $ varlink --bridge "ssh <podman-machine>" call io.podman.MountContainer "{ \" name \" : \" container-id \" }" Error: Call failed with error: io.podman.ErrorOccurred { "reason" : "no container with name or ID container-id found: no such container" }

To find out more about the Podman varlink interface read the io.podman.varlink file or the rendered API.md.

Or you can inspect, what methods your Podman version on <podman-machine> provides:

$ varlink --bridge "ssh <podman-machine>" help io.podman

Rust Client Example

Either clone this repository or:

$ cargo new --bin podmanrs $ cd podmanrs

Download the varlink interface from the running Podman varlink service:

$ varlink --bridge "ssh <podman-machine>" help io.podman > src/io.podman.varlink

create build.rs :

extern crate varlink_generator ; fn main () { varlink_generator :: cargo_build_tosource ( "src/io.podman.varlink" , true ); }

create Cargo.toml :

[package] name = "podmanrs" version = "0.1.0" authors = [ "Harald Hoyer <harald@redhat.com>" ] build = "build.rs" edition = "2018" [dependencies] varlink = "7" serde = "1" serde_derive = "1" serde_json = "1" chainerror = "0.4" [build-dependencies] varlink_generator = "7"

create src/main.rs :

mod io_podman ; use crate :: io_podman :: * ; use varlink :: Connection ; use std :: result :: Result ; use std :: error :: Error ; fn main () -> Result < (), Box < Error >> { let connection = Connection :: with_bridge ( "ssh <podman-machine>" , ) ? ; let mut podman = VarlinkClient :: new ( connection .clone ()); let reply = podman .ping () .call () ? ; println! ( "Ping() replied with '{}'" , reply .ping.message ); let reply = podman .get_info () .call () ? ; println! ( "Hostname: {}" , reply .info.host.hostname ); println! ( "Info: {:#?}" , reply .info ); Ok (()) }

Now run it: