Skip to content

Commit 3747e3d

Browse files
Merge pull request #24601 from meln5674/feature/remote-tls
Support (m)TLS API Socket
2 parents 353aa78 + feb36e4 commit 3747e3d

File tree

116 files changed

+1849
-617
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

116 files changed

+1849
-617
lines changed

Makefile

Lines changed: 13 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,17 @@ GINKGO ?= ./bin/ginkgo
150150
GINKGO_FLAKE_ATTEMPTS ?= 0
151151
GINKGO_NO_COLOR ?= y
152152

153+
# The type of transport to use for testing remote service.
154+
# Must be one of unix, tcp, tls, mtls
155+
export REMOTESYSTEM_TRANSPORT ?= unix
156+
export REMOTEINTEGRATION_TRANSPORT ?= unix
157+
153158
# Conditional required to produce empty-output if binary not built yet.
154159
RELEASE_VERSION = $(shell if test -x test/version/version; then test/version/version; fi)
155160
RELEASE_NUMBER = $(shell echo "$(call err_if_empty,RELEASE_VERSION)" | sed -e 's/^v\(.*\)/\1/')
156161

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

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

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

718+
712719
.PHONY: remotesystem
713720
remotesystem:
714721
# Wipe existing config, database, and cache: start with clean slate.
715722
$(RM) -rf ${HOME}/.local/share/containers ${HOME}/.config/containers
716-
# . Make sure there's no active podman server - if there is,
717-
# it's not us, and we have no way to know what it is.
718-
# . Start server. Wait to make sure it comes up.
719-
# . Run tests, pretty much the same as localsystem.
720-
# . Stop server.
721-
rc=0;\
722-
if timeout -v 1 true; then \
723-
if ./bin/podman-remote info; then \
724-
echo "Error: podman system service (not ours) is already running" >&2;\
725-
exit 1;\
726-
fi;\
727-
./bin/podman system service --timeout=0 > $(if $(PODMAN_SERVER_LOG),$(PODMAN_SERVER_LOG),/dev/null) 2>&1 & \
728-
retry=5;\
729-
while [ $$retry -ge 0 ]; do\
730-
echo Waiting for server...;\
731-
sleep 1;\
732-
./bin/podman-remote info >/dev/null 2>&1 && break;\
733-
retry=$$(expr $$retry - 1);\
734-
done;\
735-
if [ $$retry -lt 0 ]; then\
736-
echo "Error: ./bin/podman system service did not come up" >&2;\
737-
exit 1;\
738-
fi;\
739-
env PODMAN="$(CURDIR)/bin/podman-remote" bats -T --filter-tags '!ci:parallel' test/system/ ;\
740-
rc=$$?; \
741-
if [ $$rc -eq 0 ]; then \
742-
env PODMAN="$(CURDIR)/bin/podman-remote" bats -T --filter-tags ci:parallel -j $$(nproc) test/system/ ;\
743-
rc=$$?;\
744-
fi; \
745-
kill %1;\
746-
else \
747-
echo "Skipping $@: 'timeout -v' unavailable'";\
748-
fi;\
749-
exit $$rc
723+
PODMAN=$(CURDIR)/bin/podman-remote QUADLET=$(CURDIR)/bin/quadlet \
724+
bats -T --filter-tags '!ci:parallel' test/system/
725+
PODMAN=$(CURDIR)/bin/podman-remote QUADLET=$(CURDIR)/bin/quadlet \
726+
bats -T --filter-tags ci:parallel -j $$(nproc) test/system/
750727

751728
.PHONY: localapiv2-bash
752729
localapiv2-bash:

cmd/podman/root.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig)
167167
}
168168
podmanConfig.URI = con.URI
169169
podmanConfig.Identity = con.Identity
170+
podmanConfig.TLSCertFile = con.TLSCert
171+
podmanConfig.TLSKeyFile = con.TLSKey
172+
podmanConfig.TLSCAFile = con.TLSCA
170173
podmanConfig.MachineMode = con.IsMachine
171174
case url.Changed:
172175
podmanConfig.URI = url.Value.String()
@@ -179,6 +182,9 @@ func readRemoteCliFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig)
179182
}
180183
podmanConfig.URI = con.URI
181184
podmanConfig.Identity = con.Identity
185+
podmanConfig.TLSCertFile = con.TLSCert
186+
podmanConfig.TLSKeyFile = con.TLSKey
187+
podmanConfig.TLSCAFile = con.TLSCA
182188
podmanConfig.MachineMode = con.IsMachine
183189
}
184190
case host.Changed:
@@ -213,6 +219,9 @@ func setupRemoteConnection(podmanConfig *entities.PodmanConfig) string {
213219
}
214220
podmanConfig.URI = con.URI
215221
podmanConfig.Identity = con.Identity
222+
podmanConfig.TLSCertFile = con.TLSCert
223+
podmanConfig.TLSKeyFile = con.TLSKey
224+
podmanConfig.TLSCAFile = con.TLSCA
216225
podmanConfig.MachineMode = con.IsMachine
217226
return con.Name
218227
case hostEnv != "":
@@ -225,6 +234,9 @@ func setupRemoteConnection(podmanConfig *entities.PodmanConfig) string {
225234
if err == nil {
226235
podmanConfig.URI = con.URI
227236
podmanConfig.Identity = con.Identity
237+
podmanConfig.TLSCertFile = con.TLSCert
238+
podmanConfig.TLSKeyFile = con.TLSKey
239+
podmanConfig.TLSCAFile = con.TLSCA
228240
podmanConfig.MachineMode = con.IsMachine
229241
return con.Name
230242
}
@@ -521,6 +533,18 @@ func rootFlags(cmd *cobra.Command, podmanConfig *entities.PodmanConfig) {
521533
lFlags.StringVar(&podmanConfig.Identity, identityFlagName, podmanConfig.Identity, "path to SSH identity file, (CONTAINER_SSHKEY)")
522534
_ = cmd.RegisterFlagCompletionFunc(identityFlagName, completion.AutocompleteDefault)
523535

536+
tlsCertFileFlagName := "tls-cert"
537+
lFlags.StringVar(&podmanConfig.TLSCertFile, tlsCertFileFlagName, podmanConfig.TLSCertFile, "path to TLS client certificate PEM file for remote.")
538+
_ = cmd.RegisterFlagCompletionFunc(tlsCertFileFlagName, completion.AutocompleteDefault)
539+
540+
tlsKeyFileFlagName := "tls-key"
541+
lFlags.StringVar(&podmanConfig.TLSKeyFile, tlsKeyFileFlagName, podmanConfig.TLSKeyFile, "path to TLS client certificate private key PEM file for remote.")
542+
_ = cmd.RegisterFlagCompletionFunc(tlsKeyFileFlagName, completion.AutocompleteDefault)
543+
544+
tlsCAFileFlagName := "tls-ca"
545+
lFlags.StringVar(&podmanConfig.TLSCAFile, tlsCAFileFlagName, podmanConfig.TLSCAFile, "path to TLS certificate Authority PEM file for remote.")
546+
_ = cmd.RegisterFlagCompletionFunc(tlsCAFileFlagName, completion.AutocompleteDefault)
547+
524548
// Flags that control or influence any kind of output.
525549
outFlagName := "out"
526550
lFlags.StringVar(&useStdout, outFlagName, "", "Send output (stdout) from podman to a file")

cmd/podman/system/connection/add.go

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ var (
2727
"destination" is one of the form:
2828
[user@]hostname (will default to ssh)
2929
ssh://[user@]hostname[:port][/path] (will obtain socket path from service, if not given.)
30-
tcp://hostname:port (not secured)
30+
tcp://hostname:port (not secured without TLS enabled)
3131
unix://path (absolute path required)
3232
`,
3333
RunE: add,
@@ -36,6 +36,7 @@ var (
3636
podman system connection add --identity ~/.ssh/dev_rsa testing ssh://root@server.fubar.com:2222
3737
podman system connection add --identity ~/.ssh/dev_rsa --port 22 production root@server.fubar.com
3838
podman system connection add debug tcp://localhost:8080
39+
podman system connection add production-tls --tls-ca=ca.crt --tls-cert=tls.crt --tls-key=tls.key tcp://localhost:8080
3940
`,
4041
}
4142

@@ -51,11 +52,14 @@ var (
5152
dockerPath string
5253

5354
cOpts = struct {
54-
Identity string
55-
Port int
56-
UDSPath string
57-
Default bool
58-
Farm string
55+
Identity string
56+
Port int
57+
UDSPath string
58+
Default bool
59+
Farm string
60+
TLSCertFile string
61+
TLSKeyFile string
62+
TLSCAFile string
5963
}{}
6064
)
6165

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

81+
tlsCertFileFlagName := "tls-cert"
82+
flags.StringVar(&cOpts.TLSCertFile, tlsCertFileFlagName, "", "path to TLS client certificate PEM file")
83+
_ = addCmd.RegisterFlagCompletionFunc(tlsCertFileFlagName, completion.AutocompleteDefault)
84+
85+
tlsKeyFileFlagName := "tls-key"
86+
flags.StringVar(&cOpts.TLSKeyFile, tlsKeyFileFlagName, "", "path to TLS client certificate private key PEM file")
87+
_ = addCmd.RegisterFlagCompletionFunc(tlsKeyFileFlagName, completion.AutocompleteDefault)
88+
89+
tlsCAFileFlagName := "tls-ca"
90+
flags.StringVar(&cOpts.TLSCAFile, tlsCAFileFlagName, "", "path to TLS certificate Authority PEM file")
91+
_ = addCmd.RegisterFlagCompletionFunc(tlsCAFileFlagName, completion.AutocompleteDefault)
92+
7793
socketPathFlagName := "socket-path"
7894
flags.StringVar(&cOpts.UDSPath, socketPathFlagName, "", "path to podman socket on remote host. (default '/run/podman/podman.sock' or '/run/user/{uid}/podman/podman.sock)")
7995
_ = addCmd.RegisterFlagCompletionFunc(socketPathFlagName, completion.AutocompleteDefault)
@@ -139,14 +155,24 @@ func add(cmd *cobra.Command, args []string) error {
139155
return fmt.Errorf("invalid ssh mode")
140156
}
141157

158+
if uri.Scheme != "tcp" {
159+
if cmd.Flags().Changed("tls-cert") {
160+
return fmt.Errorf("--tls-cert option not supported for %s scheme", uri.Scheme)
161+
}
162+
if cmd.Flags().Changed("tls-key") {
163+
return fmt.Errorf("--tls-key option not supported for %s scheme", uri.Scheme)
164+
}
165+
if cmd.Flags().Changed("tls-ca") {
166+
return fmt.Errorf("--tls-ca option not supported for %s scheme", uri.Scheme)
167+
}
168+
}
142169
switch uri.Scheme {
143170
case "ssh":
144171
return ssh.Create(entities, sshMode)
145172
case "unix":
146173
if cmd.Flags().Changed("identity") {
147174
return errors.New("--identity option not supported for unix scheme")
148175
}
149-
150176
if cmd.Flags().Changed("socket-path") {
151177
uri.Path = cmd.Flag("socket-path").Value.String()
152178
}
@@ -169,6 +195,9 @@ func add(cmd *cobra.Command, args []string) error {
169195
if cmd.Flags().Changed("identity") {
170196
return errors.New("--identity option not supported for tcp scheme")
171197
}
198+
if cmd.Flags().Changed("tls-cert") != cmd.Flags().Changed("tls-key") {
199+
return errors.New("--tls-cert and --tls-key options must be both provided if one is provided")
200+
}
172201
if uri.Port() == "" {
173202
return errors.New("tcp scheme requires a port either via --port or in destination URL")
174203
}
@@ -179,6 +208,9 @@ func add(cmd *cobra.Command, args []string) error {
179208
dst := config.Destination{
180209
URI: uri.String(),
181210
Identity: cOpts.Identity,
211+
TLSCert: cOpts.TLSCertFile,
212+
TLSKey: cOpts.TLSKeyFile,
213+
TLSCA: cOpts.TLSCAFile,
182214
}
183215

184216
connection := args[0]

cmd/podman/system/connection/list.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,14 @@ var (
2424
Short: "List destination for the Podman service(s)",
2525
Long: `List destination information for the Podman service(s) in podman configuration`,
2626
Example: `podman system connection list
27+
# Format as table without TLS info
2728
podman system connection ls
28-
podman system connection ls --format=json`,
29+
# Format as table with TLS info
30+
podman system connection ls --format=tls
31+
# Format as JSON
32+
podman system connection ls --format=json
33+
# Format as custom go template
34+
podman system connection ls --format='{{range .}}{{.Name}}{{ "\n" }}{{ end }}'`,
2935
ValidArgsFunction: completion.AutocompleteNone,
3036
RunE: list,
3137
TraverseChildren: false,
@@ -114,12 +120,17 @@ func inspect(cmd *cobra.Command, args []string) error {
114120
return err
115121
}
116122

117-
if format != "" {
118-
rpt, err = rpt.Parse(report.OriginUser, format)
119-
} else {
123+
switch format {
124+
case "tls":
125+
rpt, err = rpt.Parse(report.OriginPodman,
126+
"{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.TLSCA}}\t{{.TLSCert}}\t{{.TLSKey}}\t{{.Default}}\t{{.ReadWrite}}\n{{end -}}")
127+
case "":
120128
rpt, err = rpt.Parse(report.OriginPodman,
121129
"{{range .}}{{.Name}}\t{{.URI}}\t{{.Identity}}\t{{.Default}}\t{{.ReadWrite}}\n{{end -}}")
130+
default:
131+
rpt, err = rpt.Parse(report.OriginUser, format)
122132
}
133+
123134
if err != nil {
124135
return err
125136
}
@@ -128,6 +139,9 @@ func inspect(cmd *cobra.Command, args []string) error {
128139
err = rpt.Execute([]map[string]string{{
129140
"Default": "Default",
130141
"Identity": "Identity",
142+
"TLSCA": "TLSCA",
143+
"TLSCert": "TLSCert",
144+
"TLSKey": "TLSKey",
131145
"Name": "Name",
132146
"URI": "URI",
133147
"ReadWrite": "ReadWrite",

cmd/podman/system/service.go

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package system
44

55
import (
6+
"fmt"
67
"net/url"
78
"os"
89
"path/filepath"
@@ -36,13 +37,19 @@ Enable a listening service for API access to Podman commands.
3637
RunE: service,
3738
ValidArgsFunction: common.AutocompleteDefaultOneArg,
3839
Example: `podman system service --time=0 unix:///tmp/podman.sock
39-
podman system service --time=0 tcp://localhost:8888`,
40+
podman system service --time=0 tcp://localhost:8888
41+
podman system service --time=0 --tls-cert=tls.crt --tls-key=tls.key tcp://localhost:8888
42+
podman system service --time=0 --tls-cert=tls.crt --tls-key=tls.key --tls-client-ca=ca.crt tcp://localhost:8888
43+
`,
4044
}
4145

4246
srvArgs = struct {
43-
CorsHeaders string
44-
PProfAddr string
45-
Timeout uint
47+
CorsHeaders string
48+
PProfAddr string
49+
Timeout uint
50+
TLSCertFile string
51+
TLSKeyFile string
52+
TLSClientCAFile string
4653
}{}
4754
)
4855

@@ -67,6 +74,16 @@ func init() {
6774
flags.StringVarP(&srvArgs.PProfAddr, "pprof-address", "", "",
6875
"Binding network address for pprof profile endpoints, default: do not expose endpoints")
6976
_ = flags.MarkHidden("pprof-address")
77+
78+
flags.StringVarP(&srvArgs.TLSCertFile, "tls-cert", "", "",
79+
"PEM file containing TLS serving certificate.")
80+
_ = srvCmd.RegisterFlagCompletionFunc("tls-cert", completion.AutocompleteDefault)
81+
flags.StringVarP(&srvArgs.TLSKeyFile, "tls-key", "", "",
82+
"PEM file containing TLS serving certificate private key")
83+
_ = srvCmd.RegisterFlagCompletionFunc("tls-key", completion.AutocompleteDefault)
84+
flags.StringVarP(&srvArgs.TLSClientCAFile, "tls-client-ca", "", "",
85+
"Only trust client connections with certificates signed by this CA PEM file")
86+
_ = srvCmd.RegisterFlagCompletionFunc("tls-client-ca", completion.AutocompleteDefault)
7087
}
7188

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

119+
if len(srvArgs.TLSCertFile) != 0 && len(srvArgs.TLSKeyFile) == 0 {
120+
return fmt.Errorf("--tls-cert provided without --tls-key")
121+
}
122+
if len(srvArgs.TLSKeyFile) != 0 && len(srvArgs.TLSCertFile) == 0 {
123+
return fmt.Errorf("--tls-key provided without --tls-cert")
124+
}
125+
102126
return restService(cmd.Flags(), registry.PodmanConfig(), entities.ServiceOptions{
103-
CorsHeaders: srvArgs.CorsHeaders,
104-
PProfAddr: srvArgs.PProfAddr,
105-
Timeout: time.Duration(srvArgs.Timeout) * time.Second,
106-
URI: apiURI,
127+
CorsHeaders: srvArgs.CorsHeaders,
128+
PProfAddr: srvArgs.PProfAddr,
129+
Timeout: time.Duration(srvArgs.Timeout) * time.Second,
130+
URI: apiURI,
131+
TLSCertFile: srvArgs.TLSCertFile,
132+
TLSKeyFile: srvArgs.TLSKeyFile,
133+
TLSClientCAFile: srvArgs.TLSClientCAFile,
107134
})
108135
}
109136

cmd/podman/system/service_abi.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,11 +76,13 @@ func restService(flags *pflag.FlagSet, cfg *entities.PodmanConfig, opts entities
7676
}
7777
}
7878
case "tcp":
79-
// We want to check if the user is requesting a TCP address.
79+
// We want to check if the user is requesting a TCP address if TLS is not active.
8080
// If so, warn that this is insecure.
8181
// Ignore errors here, the actual backend code will handle them
8282
// better than we can here.
83-
logrus.Warnf("Using the Podman API service with TCP sockets is not recommended, please see `podman system service` manpage for details")
83+
if opts.TLSKeyFile == "" || opts.TLSCertFile == "" {
84+
logrus.Warnf("Using the Podman API service with TCP sockets without TLS is not recommended, please see `podman system service` manpage for details")
85+
}
8486

8587
host := uri.Host
8688
if host == "" {

0 commit comments

Comments
 (0)