diff options
Diffstat (limited to 'cmd')
-rw-r--r-- | cmd/cli/main.go | 113 | ||||
-rw-r--r-- | cmd/podman/common.go | 4 | ||||
-rw-r--r-- | cmd/podman/common_test.go | 15 | ||||
-rw-r--r-- | cmd/podman/remoteclientconfig/configfile_test.go | 39 | ||||
-rw-r--r-- | cmd/podman/shared/create.go | 30 | ||||
-rw-r--r-- | cmd/podman/shared/intermediate.go | 1 |
6 files changed, 162 insertions, 40 deletions
diff --git a/cmd/cli/main.go b/cmd/cli/main.go new file mode 100644 index 000000000..4eec05ef2 --- /dev/null +++ b/cmd/cli/main.go @@ -0,0 +1,113 @@ +package main + +import ( + "bytes" + "context" + "encoding/json" + "fmt" + "io/ioutil" + "net" + "net/http" + "net/url" + "os" + + "golang.org/x/crypto/ssh" +) + +// remote PODMAN_HOST=ssh://<user>@<host>[:port]/run/podman/podman.sock +// local PODMAN_HOST=unix://run/podman/podman.sock + +var ( + DefaultURL = "unix://root@localhost/run/podman/podman.sock" +) + +func main() { + connectionURL := DefaultURL + if value, found := os.LookupEnv("PODMAN_HOST"); found { + connectionURL = value + } + + _url, err := url.Parse(connectionURL) + if err != nil { + die("Value of PODMAN_HOST is not a valid url: %s\n", connectionURL) + } + + if _url.Scheme != "ssh" && _url.Scheme != "unix" { + die("Scheme from PODMAN_HOST is not supported: %s\n", _url.Scheme) + } + + // Now we setup the http client to use the connection above + client := &http.Client{} + if _url.Scheme == "ssh" { + var auth ssh.AuthMethod + if value, found := os.LookupEnv("PODMAN_SSHKEY"); found { + auth, err = publicKey(value) + if err != nil { + die("Failed to parse %s: %v\n", value, err) + } + } else { + die("PODMAN_SSHKEY was not defined\n") + } + + // Connect to sshd + bastion, err := ssh.Dial("tcp", + net.JoinHostPort(_url.Hostname(), _url.Port()), + &ssh.ClientConfig{ + User: _url.User.Username(), + Auth: []ssh.AuthMethod{auth}, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }, + ) + if err != nil { + die("Failed to build ssh tunnel") + } + defer bastion.Close() + + client.Transport = &http.Transport{ + DialContext: func(_ context.Context, _, _ string) (net.Conn, error) { + // Now we make the connection to the unix domain socket on the server using the ssh tunnel + return bastion.Dial("unix", _url.Path) + }, + } + } else { + client.Transport = &http.Transport{ + DialContext: func(ctx context.Context, _, _ string) (net.Conn, error) { + d := net.Dialer{} + return d.DialContext(ctx, "unix", _url.Path) + }, + DisableCompression: true, + } + } + + resp, err := client.Get("http://localhost/v1.24/images/json") + if err != nil { + die(err.Error()) + } + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + + var output bytes.Buffer + _ = json.Indent(&output, body, "", " ") + fmt.Printf("%s\n", output.String()) + os.Exit(0) +} + +func die(format string, a ...interface{}) { + fmt.Fprintf(os.Stderr, format, a...) + fmt.Fprintf(os.Stderr, "\n") + os.Exit(1) +} + +func publicKey(path string) (ssh.AuthMethod, error) { + key, err := ioutil.ReadFile(path) + if err != nil { + return nil, err + } + + signer, err := ssh.ParsePrivateKey(key) + if err != nil { + return nil, err + } + + return ssh.PublicKeys(signer), nil +} diff --git a/cmd/podman/common.go b/cmd/podman/common.go index dc7590590..9064ec219 100644 --- a/cmd/podman/common.go +++ b/cmd/podman/common.go @@ -538,6 +538,10 @@ func getCreateFlags(c *cliconfig.PodmanCommand) { "workdir", "w", "", "Working directory inside the container", ) + createFlags.String( + "seccomp-policy", "default", + "Policy for selecting a seccomp profile (experimental)", + ) } func getFormat(c *cliconfig.PodmanCommand) (string, error) { diff --git a/cmd/podman/common_test.go b/cmd/podman/common_test.go deleted file mode 100644 index a24173003..000000000 --- a/cmd/podman/common_test.go +++ /dev/null @@ -1,15 +0,0 @@ -package main - -import ( - "os/user" - "testing" -) - -func skipTestIfNotRoot(t *testing.T) { - u, err := user.Current() - if err != nil { - t.Skip("Could not determine user. Running without root may cause tests to fail") - } else if u.Uid != "0" { - t.Skip("tests will fail unless run as root") - } -} diff --git a/cmd/podman/remoteclientconfig/configfile_test.go b/cmd/podman/remoteclientconfig/configfile_test.go index 1710ee83f..4ad2c2100 100644 --- a/cmd/podman/remoteclientconfig/configfile_test.go +++ b/cmd/podman/remoteclientconfig/configfile_test.go @@ -92,14 +92,15 @@ func TestReadRemoteConfig(t *testing.T) { {"nouser", args{reader: strings.NewReader(noUser)}, makeNoUserResult(), false}, } for _, tt := range tests { + test := tt t.Run(tt.name, func(t *testing.T) { - got, err := ReadRemoteConfig(tt.args.reader) - if (err != nil) != tt.wantErr { - t.Errorf("ReadRemoteConfig() error = %v, wantErr %v", err, tt.wantErr) + got, err := ReadRemoteConfig(test.args.reader) + if (err != nil) != test.wantErr { + t.Errorf("ReadRemoteConfig() error = %v, wantErr %v", err, test.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("ReadRemoteConfig() = %v, want %v", got, tt.want) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("ReadRemoteConfig() = %v, want %v", got, test.want) } }) } @@ -150,17 +151,18 @@ func TestRemoteConfig_GetDefault(t *testing.T) { {"single", fields{Connections: none}, nil, true}, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + test := tt + t.Run(test.name, func(t *testing.T) { r := &RemoteConfig{ - Connections: tt.fields.Connections, + Connections: test.fields.Connections, } got, err := r.GetDefault() - if (err != nil) != tt.wantErr { - t.Errorf("RemoteConfig.GetDefault() error = %v, wantErr %v", err, tt.wantErr) + if (err != nil) != test.wantErr { + t.Errorf("RemoteConfig.GetDefault() error = %v, wantErr %v", err, test.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("RemoteConfig.GetDefault() = %v, want %v", got, tt.want) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("RemoteConfig.GetDefault() = %v, want %v", got, test.want) } }) } @@ -192,17 +194,18 @@ func TestRemoteConfig_GetRemoteConnection(t *testing.T) { {"none", fields{Connections: blank}, args{name: "foobar"}, nil, true}, } for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { + test := tt + t.Run(test.name, func(t *testing.T) { r := &RemoteConfig{ - Connections: tt.fields.Connections, + Connections: test.fields.Connections, } - got, err := r.GetRemoteConnection(tt.args.name) - if (err != nil) != tt.wantErr { - t.Errorf("RemoteConfig.GetRemoteConnection() error = %v, wantErr %v", err, tt.wantErr) + got, err := r.GetRemoteConnection(test.args.name) + if (err != nil) != test.wantErr { + t.Errorf("RemoteConfig.GetRemoteConnection() error = %v, wantErr %v", err, test.wantErr) return } - if !reflect.DeepEqual(got, tt.want) { - t.Errorf("RemoteConfig.GetRemoteConnection() = %v, want %v", got, tt.want) + if !reflect.DeepEqual(got, test.want) { + t.Errorf("RemoteConfig.GetRemoteConnection() = %v, want %v", got, test.want) } }) } diff --git a/cmd/podman/shared/create.go b/cmd/podman/shared/create.go index 05a3f5598..50a64b01c 100644 --- a/cmd/podman/shared/create.go +++ b/cmd/podman/shared/create.go @@ -31,6 +31,10 @@ import ( "github.com/sirupsen/logrus" ) +// seccompAnnotationKey is the key of the image annotation embedding a seccomp +// profile. +const seccompAnnotationKey = "io.containers.seccomp.profile" + func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod.Runtime) (*libpod.Container, *cc.CreateConfig, error) { var ( healthCheck *manifest.Schema2HealthConfig @@ -67,7 +71,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. } imageName := "" - var data *inspect.ImageData = nil + var imageData *inspect.ImageData = nil // Set the storage if there is no rootfs specified if rootfs == "" { @@ -99,17 +103,17 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. if err != nil { return nil, nil, err } - data, err = newImage.Inspect(ctx) + imageData, err = newImage.Inspect(ctx) if err != nil { return nil, nil, err } - if overrideOS == "" && data.Os != goruntime.GOOS { - return nil, nil, errors.Errorf("incompatible image OS %q on %q host", data.Os, goruntime.GOOS) + if overrideOS == "" && imageData.Os != goruntime.GOOS { + return nil, nil, errors.Errorf("incompatible image OS %q on %q host", imageData.Os, goruntime.GOOS) } - if overrideArch == "" && data.Architecture != goruntime.GOARCH { - return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", data.Architecture, goruntime.GOARCH) + if overrideArch == "" && imageData.Architecture != goruntime.GOARCH { + return nil, nil, errors.Errorf("incompatible image architecture %q on %q host", imageData.Architecture, goruntime.GOARCH) } names := newImage.Names() @@ -171,7 +175,7 @@ func CreateContainer(ctx context.Context, c *GenericCLIResults, runtime *libpod. } } - createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, data) + createConfig, err := ParseCreateOpts(ctx, c, runtime, imageName, imageData) if err != nil { return nil, nil, err } @@ -712,6 +716,18 @@ func ParseCreateOpts(ctx context.Context, c *GenericCLIResults, runtime *libpod. return nil, err } + // SECCOMP + if data != nil { + if value, exists := data.Annotations[seccompAnnotationKey]; exists { + secConfig.SeccompProfileFromImage = value + } + } + if policy, err := cc.LookupSeccompPolicy(c.String("seccomp-policy")); err != nil { + return nil, err + } else { + secConfig.SeccompPolicy = policy + } + config := &cc.CreateConfig{ Annotations: annotations, BuiltinImgVolumes: ImageVolumes, diff --git a/cmd/podman/shared/intermediate.go b/cmd/podman/shared/intermediate.go index e985e4dc0..d1f0e602e 100644 --- a/cmd/podman/shared/intermediate.go +++ b/cmd/podman/shared/intermediate.go @@ -463,6 +463,7 @@ func NewIntermediateLayer(c *cliconfig.PodmanCommand, remote bool) GenericCLIRes m["volume"] = newCRStringArray(c, "volume") m["volumes-from"] = newCRStringSlice(c, "volumes-from") m["workdir"] = newCRString(c, "workdir") + m["seccomp-policy"] = newCRString(c, "seccomp-policy") // global flag if !remote { m["authfile"] = newCRString(c, "authfile") |