mirror of
https://github.com/containers/podman.git
synced 2025-10-14 17:55:51 +08:00
Implement TLS API Support
* Added flags to point to TLS PEM files to use for exposing and connecting to an encrypted remote API socket with server and client authentication. * Added TLS fields for system connection ls templates. * Added special "tls" format for system connection ls to list TLS fields in human-readable table format. * Updated remote integration and system tests to allow specifying a "transport" to run the full suite against a unix, tcp, tls, or mtls system service. * Added system tests to verify basic operation of unix, tcp, tls, and mtls services, clients, and connections. Signed-off-by: Andrew Melnick <meln5674.5674@gmail.com>
This commit is contained in:
@ -3,6 +3,7 @@ package bindings
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -17,6 +18,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver/v4"
|
||||
"github.com/containers/podman/v5/pkg/util/tlsutil"
|
||||
"github.com/containers/podman/v5/version"
|
||||
"github.com/kevinburke/ssh_config"
|
||||
"github.com/sirupsen/logrus"
|
||||
@ -33,6 +35,7 @@ type APIResponse struct {
|
||||
type Connection struct {
|
||||
URI *url.URL
|
||||
Client *http.Client
|
||||
tls bool
|
||||
}
|
||||
|
||||
type valueKey string
|
||||
@ -89,7 +92,7 @@ func JoinURL(elements ...string) string {
|
||||
|
||||
// NewConnection creates a new service connection without an identity
|
||||
func NewConnection(ctx context.Context, uri string) (context.Context, error) {
|
||||
return NewConnectionWithIdentity(ctx, uri, "", false)
|
||||
return NewConnectionWithOptions(ctx, Options{URI: uri})
|
||||
}
|
||||
|
||||
// NewConnectionWithIdentity takes a URI as a string and returns a context with the
|
||||
@ -101,14 +104,31 @@ func NewConnection(ctx context.Context, uri string) (context.Context, error) {
|
||||
// or unix:///run/podman/podman.sock
|
||||
// or ssh://<user>@<host>[:port]/run/podman/podman.sock
|
||||
func NewConnectionWithIdentity(ctx context.Context, uri string, identity string, machine bool) (context.Context, error) {
|
||||
var err error
|
||||
if v, found := os.LookupEnv("CONTAINER_HOST"); found && uri == "" {
|
||||
uri = v
|
||||
}
|
||||
return NewConnectionWithOptions(ctx, Options{URI: uri, Identity: identity, Machine: machine})
|
||||
}
|
||||
|
||||
if v, found := os.LookupEnv("CONTAINER_SSHKEY"); found && len(identity) == 0 {
|
||||
identity = v
|
||||
type Options struct {
|
||||
URI string
|
||||
Identity string
|
||||
TLSCertFile string
|
||||
TLSKeyFile string
|
||||
TLSCAFile string
|
||||
Machine bool
|
||||
}
|
||||
|
||||
func orEnv(s string, env string) string {
|
||||
if len(s) != 0 {
|
||||
return s
|
||||
}
|
||||
s, _ = os.LookupEnv(env)
|
||||
return s
|
||||
}
|
||||
|
||||
func NewConnectionWithOptions(ctx context.Context, opts Options) (context.Context, error) {
|
||||
var err error
|
||||
|
||||
uri := orEnv(opts.URI, "CONTAINER_HOST")
|
||||
identity := orEnv(opts.Identity, "CONTAINER_SSHKEY")
|
||||
|
||||
_url, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
@ -119,7 +139,7 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string,
|
||||
var connection Connection
|
||||
switch _url.Scheme {
|
||||
case "ssh":
|
||||
conn, err := sshClient(_url, uri, identity, machine)
|
||||
conn, err := sshClient(_url, uri, identity, opts.Machine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -135,7 +155,7 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string,
|
||||
if !strings.HasPrefix(uri, "tcp://") {
|
||||
return nil, errors.New("tcp URIs should begin with tcp://")
|
||||
}
|
||||
conn, err := tcpClient(_url)
|
||||
conn, err := tcpClient(_url, opts.TLSCertFile, opts.TLSKeyFile, opts.TLSCAFile)
|
||||
if err != nil {
|
||||
return nil, newConnectError(err)
|
||||
}
|
||||
@ -151,7 +171,7 @@ func NewConnectionWithIdentity(ctx context.Context, uri string, identity string,
|
||||
}
|
||||
ctx = context.WithValue(ctx, versionKey, serviceVersion)
|
||||
|
||||
ctx = context.WithValue(ctx, machineModeKey, machine)
|
||||
ctx = context.WithValue(ctx, machineModeKey, opts.Machine)
|
||||
return ctx, nil
|
||||
}
|
||||
|
||||
@ -288,7 +308,7 @@ func sshClient(_url *url.URL, uri string, identity string, machine bool) (Connec
|
||||
return connection, nil
|
||||
}
|
||||
|
||||
func tcpClient(_url *url.URL) (Connection, error) {
|
||||
func tcpClient(_url *url.URL, tlsCertFile, tlsKeyFile, tlsCAFile string) (Connection, error) {
|
||||
connection := Connection{
|
||||
URI: _url,
|
||||
}
|
||||
@ -320,11 +340,34 @@ func tcpClient(_url *url.URL) (Connection, error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
transport := http.Transport{
|
||||
DialContext: dialContext,
|
||||
DisableCompression: true,
|
||||
}
|
||||
if len(tlsCAFile) != 0 || len(tlsCertFile) != 0 || len(tlsKeyFile) != 0 {
|
||||
logrus.Debugf("using TLS cert=%s key=%s ca=%s", tlsCertFile, tlsKeyFile, tlsCAFile)
|
||||
transport.TLSClientConfig = &tls.Config{}
|
||||
connection.tls = true
|
||||
}
|
||||
if len(tlsCAFile) != 0 {
|
||||
pool, err := tlsutil.ReadCertBundle(tlsCAFile)
|
||||
if err != nil {
|
||||
return connection, fmt.Errorf("unable to read CA bundle: %w", err)
|
||||
}
|
||||
transport.TLSClientConfig.RootCAs = pool
|
||||
}
|
||||
if (len(tlsCertFile) == 0) != (len(tlsKeyFile) == 0) {
|
||||
return connection, fmt.Errorf("TLS Key and Certificate must both or neither be provided")
|
||||
}
|
||||
if len(tlsCertFile) != 0 && len(tlsKeyFile) != 0 {
|
||||
keyPair, err := tls.LoadX509KeyPair(tlsCertFile, tlsKeyFile)
|
||||
if err != nil {
|
||||
return connection, fmt.Errorf("unable to read TLS key pair: %w", err)
|
||||
}
|
||||
transport.TLSClientConfig.Certificates = append(transport.TLSClientConfig.Certificates, keyPair)
|
||||
}
|
||||
connection.Client = &http.Client{
|
||||
Transport: &http.Transport{
|
||||
DialContext: dialContext,
|
||||
DisableCompression: true,
|
||||
},
|
||||
Transport: &transport,
|
||||
}
|
||||
return connection, nil
|
||||
}
|
||||
@ -405,8 +448,14 @@ func (c *Connection) DoRequest(ctx context.Context, httpBody io.Reader, httpMeth
|
||||
|
||||
baseURL := "http://d"
|
||||
if c.URI.Scheme == "tcp" {
|
||||
var scheme string
|
||||
if c.tls {
|
||||
scheme = "https"
|
||||
} else {
|
||||
scheme = "http"
|
||||
}
|
||||
// Allow path prefixes for tcp connections to match Docker behavior
|
||||
baseURL = "http://" + c.URI.Host + c.URI.Path
|
||||
baseURL = scheme + "://" + c.URI.Host + c.URI.Path
|
||||
}
|
||||
uri := fmt.Sprintf(baseURL+"/v%s/libpod"+endpoint, params...)
|
||||
logrus.Debugf("DoRequest Method: %s URI: %v", httpMethod, uri)
|
||||
|
Reference in New Issue
Block a user