Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 13 additions & 36 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,17 @@ GINKGO ?= ./bin/ginkgo
GINKGO_FLAKE_ATTEMPTS ?= 0
GINKGO_NO_COLOR ?= y

# The type of transport to use for testing remote service.
# Must be one of unix, tcp, tls, mtls
export REMOTESYSTEM_TRANSPORT ?= unix
export REMOTEINTEGRATION_TRANSPORT ?= unix

# Conditional required to produce empty-output if binary not built yet.
RELEASE_VERSION = $(shell if test -x test/version/version; then test/version/version; fi)
RELEASE_NUMBER = $(shell echo "$(call err_if_empty,RELEASE_VERSION)" | sed -e 's/^v\(.*\)/\1/')

# If non-empty, logs all output from server during remote system testing
PODMAN_SERVER_LOG ?=
# Logs all output from server during remote system testing to this file
PODMAN_SERVER_LOG ?= /dev/null

# Ensure GOBIN is not set so the default (`go env GOPATH`/bin) is used.
override undefine GOBIN
Expand Down Expand Up @@ -680,6 +685,7 @@ ginkgo-run: .install.ginkgo
ginkgo:
$(MAKE) ginkgo-run TAGS="$(BUILDTAGS)"


.PHONY: ginkgo-remote
ginkgo-remote:
$(MAKE) ginkgo-run TAGS="$(REMOTETAGS) remote_testing"
Expand Down Expand Up @@ -709,44 +715,15 @@ localsystem:
PODMAN=$(CURDIR)/bin/podman QUADLET=$(CURDIR)/bin/quadlet bats -T --filter-tags '!ci:parallel' test/system/
PODMAN=$(CURDIR)/bin/podman QUADLET=$(CURDIR)/bin/quadlet bats -T --filter-tags ci:parallel -j $$(nproc) test/system/


.PHONY: remotesystem
remotesystem:
# Wipe existing config, database, and cache: start with clean slate.
$(RM) -rf ${HOME}/.local/share/containers ${HOME}/.config/containers
# . Make sure there's no active podman server - if there is,
# it's not us, and we have no way to know what it is.
# . Start server. Wait to make sure it comes up.
# . Run tests, pretty much the same as localsystem.
# . Stop server.
rc=0;\
if timeout -v 1 true; then \
if ./bin/podman-remote info; then \
echo "Error: podman system service (not ours) is already running" >&2;\
exit 1;\
fi;\
./bin/podman system service --timeout=0 > $(if $(PODMAN_SERVER_LOG),$(PODMAN_SERVER_LOG),/dev/null) 2>&1 & \
retry=5;\
while [ $$retry -ge 0 ]; do\
echo Waiting for server...;\
sleep 1;\
./bin/podman-remote info >/dev/null 2>&1 && break;\
retry=$$(expr $$retry - 1);\
done;\
if [ $$retry -lt 0 ]; then\
echo "Error: ./bin/podman system service did not come up" >&2;\
exit 1;\
fi;\
env PODMAN="$(CURDIR)/bin/podman-remote" bats -T --filter-tags '!ci:parallel' test/system/ ;\
rc=$$?; \
if [ $$rc -eq 0 ]; then \
env PODMAN="$(CURDIR)/bin/podman-remote" bats -T --filter-tags ci:parallel -j $$(nproc) test/system/ ;\
rc=$$?;\
fi; \
kill %1;\
else \
echo "Skipping $@: 'timeout -v' unavailable'";\
fi;\
exit $$rc
PODMAN=$(CURDIR)/bin/podman-remote QUADLET=$(CURDIR)/bin/quadlet \
bats -T --filter-tags '!ci:parallel' test/system/
PODMAN=$(CURDIR)/bin/podman-remote QUADLET=$(CURDIR)/bin/quadlet \
bats -T --filter-tags ci:parallel -j $$(nproc) test/system/

.PHONY: localapiv2-bash
localapiv2-bash:
Expand Down
24 changes: 24 additions & 0 deletions cmd/podman/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig)
}
podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.TLSCertFile = con.TLSCert
podmanConfig.TLSKeyFile = con.TLSKey
podmanConfig.TLSCAFile = con.TLSCA
podmanConfig.MachineMode = con.IsMachine
case url.Changed:
podmanConfig.URI = url.Value.String()
Expand All @@ -179,6 +182,9 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig)
}
podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.TLSCertFile = con.TLSCert
podmanConfig.TLSKeyFile = con.TLSKey
podmanConfig.TLSCAFile = con.TLSCA
podmanConfig.MachineMode = con.IsMachine
}
case host.Changed:
Expand Down Expand Up @@ -213,6 +219,9 @@ func setupRemoteConnection(podmanConfig *entities.PodmanConfig) string {
}
podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.TLSCertFile = con.TLSCert
podmanConfig.TLSKeyFile = con.TLSKey
podmanConfig.TLSCAFile = con.TLSCA
podmanConfig.MachineMode = con.IsMachine
return con.Name
case hostEnv != "":
Expand All @@ -225,6 +234,9 @@ func setupRemoteConnection(podmanConfig *entities.PodmanConfig) string {
if err == nil {
podmanConfig.URI = con.URI
podmanConfig.Identity = con.Identity
podmanConfig.TLSCertFile = con.TLSCert
podmanConfig.TLSKeyFile = con.TLSKey
podmanConfig.TLSCAFile = con.TLSCA
podmanConfig.MachineMode = con.IsMachine
return con.Name
}
Expand Down Expand Up @@ -521,6 +533,18 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
lFlags.StringVar(&podmanConfig.Identity, identityFlagName, podmanConfig.Identity, "path to SSH identity file, (CONTAINER_SSHKEY)")
_ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault)

tlsCertFileFlagName := "tls-cert"
lFlags.StringVar(&podmanConfig.TLSCertFile, tlsCertFileFlagName, podmanConfig.TLSCertFile, "path to TLS client certificate PEM file for remote.")
_ = cmd.RegisterFlagCompletionFunc(tlsCertFileFlagName, completion.AutocompleteDefault)

tlsKeyFileFlagName := "tls-key"
lFlags.StringVar(&podmanConfig.TLSKeyFile, tlsKeyFileFlagName, podmanConfig.TLSKeyFile, "path to TLS client certificate private key PEM file for remote.")
_ = cmd.RegisterFlagCompletionFunc(tlsKeyFileFlagName, completion.AutocompleteDefault)

tlsCAFileFlagName := "tls-ca"
lFlags.StringVar(&podmanConfig.TLSCAFile, tlsCAFileFlagName, podmanConfig.TLSCAFile, "path to TLS certificate Authority PEM file for remote.")
_ = cmd.RegisterFlagCompletionFunc(tlsCAFileFlagName, completion.AutocompleteDefault)

// Flags that control or influence any kind of output.
outFlagName := "out"
lFlags.StringVar(&useStdout, outFlagName, "", "Send output (stdout) from podman to a file")
Expand Down
46 changes: 39 additions & 7 deletions cmd/podman/system/connection/add.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ var (
"destination" is one of the form:
[user@]hostname (will default to ssh)
ssh://[user@]hostname[:port][/path] (will obtain socket path from service, if not given.)
tcp://hostname:port (not secured)
tcp://hostname:port (not secured without TLS enabled)
unix://path (absolute path required)
`,
RunE: add,
Expand All @@ -36,6 +36,7 @@ var (
podman system connection add --identity ~/.ssh/dev_rsa testing ssh://[email protected]:2222
podman system connection add --identity ~/.ssh/dev_rsa --port 22 production [email protected]
podman system connection add debug tcp://localhost:8080
podman system connection add production-tls --tls-ca=ca.crt --tls-cert=tls.crt --tls-key=tls.key tcp://localhost:8080
`,
}

Expand All @@ -51,11 +52,14 @@ var (
dockerPath string

cOpts = struct {
Identity string
Port int
UDSPath string
Default bool
Farm string
Identity string
Port int
UDSPath string
Default bool
Farm string
TLSCertFile string
TLSKeyFile string
TLSCAFile string
}{}
)

Expand All @@ -74,6 +78,18 @@ func init() {
flags.StringVar(&cOpts.Identity, identityFlagName, "", "path to SSH identity file")
_ = addCmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault)

tlsCertFileFlagName := "tls-cert"
flags.StringVar(&cOpts.TLSCertFile, tlsCertFileFlagName, "", "path to TLS client certificate PEM file")
_ = addCmd.RegisterFlagCompletionFunc(tlsCertFileFlagName, completion.AutocompleteDefault)

tlsKeyFileFlagName := "tls-key"
flags.StringVar(&cOpts.TLSKeyFile, tlsKeyFileFlagName, "", "path to TLS client certificate private key PEM file")
_ = addCmd.RegisterFlagCompletionFunc(tlsKeyFileFlagName, completion.AutocompleteDefault)

tlsCAFileFlagName := "tls-ca"
flags.StringVar(&cOpts.TLSCAFile, tlsCAFileFlagName, "", "path to TLS certificate Authority PEM file")
_ = addCmd.RegisterFlagCompletionFunc(tlsCAFileFlagName, completion.AutocompleteDefault)

socketPathFlagName := "socket-path"
flags.StringVar(&cOpts.UDSPath, socketPathFlagName, "", "path to podman socket on remote host. (default '/run/podman/podman.sock' or '/run/user/{uid}/podman/podman.sock)")
_ = addCmd.RegisterFlagCompletionFunc(socketPathFlagName, completion.AutocompleteDefault)
Expand Down Expand Up @@ -139,14 +155,24 @@ func add(cmd *cobra.Command, args []string) error {
return fmt.Errorf("invalid ssh mode")
}

if uri.Scheme != "tcp" {
if cmd.Flags().Changed("tls-cert") {
return fmt.Errorf("--tls-cert option not supported for %s scheme", uri.Scheme)
}
if cmd.Flags().Changed("tls-key") {
return fmt.Errorf("--tls-key option not supported for %s scheme", uri.Scheme)
}
if cmd.Flags().Changed("tls-ca") {
return fmt.Errorf("--tls-ca option not supported for %s scheme", uri.Scheme)
}
}
switch uri.Scheme {
case "ssh":
return ssh.Create(entities, sshMode)
case "unix":
if cmd.Flags().Changed("identity") {
return errors.New("--identity option not supported for unix scheme")
}

if cmd.Flags().Changed("socket-path") {
uri.Path = cmd.Flag("socket-path").Value.String()
}
Expand All @@ -169,6 +195,9 @@ func add(cmd *cobra.Command, args []string) error {
if cmd.Flags().Changed("identity") {
return errors.New("--identity option not supported for tcp scheme")
}
if cmd.Flags().Changed("tls-cert") != cmd.Flags().Changed("tls-key") {
return errors.New("--tls-cert and --tls-key options must be both provided if one is provided")
}
if uri.Port() == "" {
return errors.New("tcp scheme requires a port either via --port or in destination URL")
}
Expand All @@ -179,6 +208,9 @@ func add(cmd *cobra.Command, args []string) error {
dst := config.Destination{
URI: uri.String(),
Identity: cOpts.Identity,
TLSCert: cOpts.TLSCertFile,
TLSKey: cOpts.TLSKeyFile,
TLSCA: cOpts.TLSCAFile,
}

connection := args[0]
Expand Down
22 changes: 18 additions & 4 deletions cmd/podman/system/connection/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,14 @@ var (
Short: "List destination for the Podman service(s)",
Long: `List destination information for the Podman service(s) in podman configuration`,
Example: `podman system connection list
# Format as table without TLS info
podman system connection ls
podman system connection ls --format=json`,
# Format as table with TLS info
podman system connection ls --format=tls
# Format as JSON
podman system connection ls --format=json
# Format as custom go template
podman system connection ls --format='{{range .}}{{.Name}}{{ "\n" }}{{ end }}'`,
ValidArgsFunction: completion.AutocompleteNone,
RunE: list,
TraverseChildren: false,
Expand Down Expand Up @@ -114,12 +120,17 @@ func inspect(cmd *cobra.Command, args []string) error {
return err
}

if format != "" {
rpt, err = rpt.Parse(report.OriginUser, format)
} else {
switch format {
case "tls":
Copy link
Collaborator

@mtrmac mtrmac Sep 15, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There’s some precedent to special-casing --format=… values (in particular we accept --format json in places), and to an extent I appreciate the ergonomics, but, on the principle of the thing, I personally dislike values that have two different semantics. If --format hello outputs literal "hello", it’s inconsistent that --format tls does not output literal "tls".

I’m not authoritative for Podman, and note that this was pre-approved in #24601 (comment) .

rpt, err = rpt.Parse(report.OriginPodman,
"{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.TLSCA}}\t{{.TLSCert}}\t{{.TLSKey}}\t{{.Default}}\t{{.ReadWrite}}\n{{end -}}")
case "":
rpt, err = rpt.Parse(report.OriginPodman,
"{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\t{{.ReadWrite}}\n{{end -}}")
default:
rpt, err = rpt.Parse(report.OriginUser, format)
}

if err != nil {
return err
}
Expand All @@ -128,6 +139,9 @@ func inspect(cmd *cobra.Command, args []string) error {
err = rpt.Execute([]map[string]string{{
"Default": "Default",
"Identity": "Identity",
"TLSCA": "TLSCA",
"TLSCert": "TLSCert",
"TLSKey": "TLSKey",
"Name": "Name",
"URI": "URI",
"ReadWrite": "ReadWrite",
Expand Down
43 changes: 35 additions & 8 deletions cmd/podman/system/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package system

import (
"fmt"
"net/url"
"os"
"path/filepath"
Expand Down Expand Up @@ -36,13 +37,19 @@ Enable a listening service for API access to Podman commands.
RunE: service,
ValidArgsFunction: common.AutocompleteDefaultOneArg,
Example: `podman system service --time=0 unix:///tmp/podman.sock
podman system service --time=0 tcp://localhost:8888`,
podman system service --time=0 tcp://localhost:8888
podman system service --time=0 --tls-cert=tls.crt --tls-key=tls.key tcp://localhost:8888
podman system service --time=0 --tls-cert=tls.crt --tls-key=tls.key --tls-client-ca=ca.crt tcp://localhost:8888
`,
}

srvArgs = struct {
CorsHeaders string
PProfAddr string
Timeout uint
CorsHeaders string
PProfAddr string
Timeout uint
TLSCertFile string
TLSKeyFile string
TLSClientCAFile string
}{}
)

Expand All @@ -67,6 +74,16 @@ func init() {
flags.StringVarP(&srvArgs.PProfAddr, "pprof-address", "", "",
"Binding network address for pprof profile endpoints, default: do not expose endpoints")
_ = flags.MarkHidden("pprof-address")

flags.StringVarP(&srvArgs.TLSCertFile, "tls-cert", "", "",
"PEM file containing TLS serving certificate.")
_ = srvCmd.RegisterFlagCompletionFunc("tls-cert", completion.AutocompleteDefault)
flags.StringVarP(&srvArgs.TLSKeyFile, "tls-key", "", "",
"PEM file containing TLS serving certificate private key")
_ = srvCmd.RegisterFlagCompletionFunc("tls-key", completion.AutocompleteDefault)
flags.StringVarP(&srvArgs.TLSClientCAFile, "tls-client-ca", "", "",
"Only trust client connections with certificates signed by this CA PEM file")
_ = srvCmd.RegisterFlagCompletionFunc("tls-client-ca", completion.AutocompleteDefault)
}

func aliasTimeoutFlag(_ *pflag.FlagSet, name string) pflag.NormalizedName {
Expand Down Expand Up @@ -99,11 +116,21 @@ func service(cmd *cobra.Command, args []string) error {
}
}

if len(srvArgs.TLSCertFile) != 0 && len(srvArgs.TLSKeyFile) == 0 {
return fmt.Errorf("--tls-cert provided without --tls-key")
}
if len(srvArgs.TLSKeyFile) != 0 && len(srvArgs.TLSCertFile) == 0 {
return fmt.Errorf("--tls-key provided without --tls-cert")
}

return restService(cmd.Flags(), registry.PodmanConfig(), entities.ServiceOptions{
CorsHeaders: srvArgs.CorsHeaders,
PProfAddr: srvArgs.PProfAddr,
Timeout: time.Duration(srvArgs.Timeout) * time.Second,
URI: apiURI,
CorsHeaders: srvArgs.CorsHeaders,
PProfAddr: srvArgs.PProfAddr,
Timeout: time.Duration(srvArgs.Timeout) * time.Second,
URI: apiURI,
TLSCertFile: srvArgs.TLSCertFile,
TLSKeyFile: srvArgs.TLSKeyFile,
TLSClientCAFile: srvArgs.TLSClientCAFile,
})
}

Expand Down
6 changes: 4 additions & 2 deletions cmd/podman/system/service_abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,13 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
}
}
case "tcp":
// We want to check if the user is requesting a TCP address.
// We want to check if the user is requesting a TCP address if TLS is not active.
// If so, warn that this is insecure.
// Ignore errors here, the actual backend code will handle them
// better than we can here.
logrus.Warnf("Using the Podman API service with TCP sockets is not recommended, please see `podman system service` manpage for details")
if opts.TLSKeyFile == "" || opts.TLSCertFile == "" {
logrus.Warnf("Using the Podman API service with TCP sockets without TLS is not recommended, please see `podman system service` manpage for details")
}

host := uri.Host
if host == "" {
Expand Down
Loading