1// +build !windows
2
3package authz
4
5import (
6	"context"
7	"fmt"
8	"io/ioutil"
9	"os"
10	"strings"
11	"testing"
12
13	"github.com/docker/docker/api/types"
14	"github.com/docker/docker/api/types/container"
15	"github.com/docker/docker/api/types/filters"
16	networktypes "github.com/docker/docker/api/types/network"
17	volumetypes "github.com/docker/docker/api/types/volume"
18	"github.com/docker/docker/client"
19	"github.com/docker/docker/integration/util/requirement"
20	"github.com/gotestyourself/gotestyourself/skip"
21	"github.com/stretchr/testify/require"
22)
23
24var (
25	authzPluginName            = "riyaz/authz-no-volume-plugin"
26	authzPluginTag             = "latest"
27	authzPluginNameWithTag     = authzPluginName + ":" + authzPluginTag
28	authzPluginBadManifestName = "riyaz/authz-plugin-bad-manifest"
29	nonexistentAuthzPluginName = "riyaz/nonexistent-authz-plugin"
30)
31
32func setupTestV2(t *testing.T) func() {
33	skip.IfCondition(t, testEnv.DaemonInfo.OSType != "linux")
34	skip.IfCondition(t, !requirement.HasHubConnectivity(t))
35
36	teardown := setupTest(t)
37
38	d.Start(t)
39
40	return teardown
41}
42
43func TestAuthZPluginV2AllowNonVolumeRequest(t *testing.T) {
44	skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
45	defer setupTestV2(t)()
46
47	client, err := d.NewClient()
48	require.Nil(t, err)
49
50	// Install authz plugin
51	err = pluginInstallGrantAllPermissions(client, authzPluginNameWithTag)
52	require.Nil(t, err)
53	// start the daemon with the plugin and load busybox, --net=none build fails otherwise
54	// because it needs to pull busybox
55	d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
56	d.LoadBusybox(t)
57
58	// Ensure docker run command and accompanying docker ps are successful
59	createResponse, err := client.ContainerCreate(context.Background(), &container.Config{Cmd: []string{"top"}, Image: "busybox"}, &container.HostConfig{}, &networktypes.NetworkingConfig{}, "")
60	require.Nil(t, err)
61
62	err = client.ContainerStart(context.Background(), createResponse.ID, types.ContainerStartOptions{})
63	require.Nil(t, err)
64
65	_, err = client.ContainerInspect(context.Background(), createResponse.ID)
66	require.Nil(t, err)
67}
68
69func TestAuthZPluginV2Disable(t *testing.T) {
70	skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
71	defer setupTestV2(t)()
72
73	client, err := d.NewClient()
74	require.Nil(t, err)
75
76	// Install authz plugin
77	err = pluginInstallGrantAllPermissions(client, authzPluginNameWithTag)
78	require.Nil(t, err)
79
80	d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
81	d.LoadBusybox(t)
82
83	_, err = client.VolumeCreate(context.Background(), volumetypes.VolumesCreateBody{Driver: "local"})
84	require.NotNil(t, err)
85	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
86
87	// disable the plugin
88	err = client.PluginDisable(context.Background(), authzPluginNameWithTag, types.PluginDisableOptions{})
89	require.Nil(t, err)
90
91	// now test to see if the docker api works.
92	_, err = client.VolumeCreate(context.Background(), volumetypes.VolumesCreateBody{Driver: "local"})
93	require.Nil(t, err)
94}
95
96func TestAuthZPluginV2RejectVolumeRequests(t *testing.T) {
97	skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
98	defer setupTestV2(t)()
99
100	client, err := d.NewClient()
101	require.Nil(t, err)
102
103	// Install authz plugin
104	err = pluginInstallGrantAllPermissions(client, authzPluginNameWithTag)
105	require.Nil(t, err)
106
107	// restart the daemon with the plugin
108	d.Restart(t, "--authorization-plugin="+authzPluginNameWithTag)
109
110	_, err = client.VolumeCreate(context.Background(), volumetypes.VolumesCreateBody{Driver: "local"})
111	require.NotNil(t, err)
112	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
113
114	_, err = client.VolumeList(context.Background(), filters.Args{})
115	require.NotNil(t, err)
116	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
117
118	// The plugin will block the command before it can determine the volume does not exist
119	err = client.VolumeRemove(context.Background(), "test", false)
120	require.NotNil(t, err)
121	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
122
123	_, err = client.VolumeInspect(context.Background(), "test")
124	require.NotNil(t, err)
125	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
126
127	_, err = client.VolumesPrune(context.Background(), filters.Args{})
128	require.NotNil(t, err)
129	require.True(t, strings.Contains(err.Error(), fmt.Sprintf("Error response from daemon: plugin %s failed with error:", authzPluginNameWithTag)))
130}
131
132func TestAuthZPluginV2BadManifestFailsDaemonStart(t *testing.T) {
133	skip.IfCondition(t, os.Getenv("DOCKER_ENGINE_GOARCH") != "amd64")
134	defer setupTestV2(t)()
135
136	client, err := d.NewClient()
137	require.Nil(t, err)
138
139	// Install authz plugin with bad manifest
140	err = pluginInstallGrantAllPermissions(client, authzPluginBadManifestName)
141	require.Nil(t, err)
142
143	// start the daemon with the plugin, it will error
144	err = d.RestartWithError("--authorization-plugin=" + authzPluginBadManifestName)
145	require.NotNil(t, err)
146
147	// restarting the daemon without requiring the plugin will succeed
148	d.Start(t)
149}
150
151func TestAuthZPluginV2NonexistentFailsDaemonStart(t *testing.T) {
152	defer setupTestV2(t)()
153
154	// start the daemon with a non-existent authz plugin, it will error
155	err := d.RestartWithError("--authorization-plugin=" + nonexistentAuthzPluginName)
156	require.NotNil(t, err)
157
158	// restarting the daemon without requiring the plugin will succeed
159	d.Start(t)
160}
161
162func pluginInstallGrantAllPermissions(client client.APIClient, name string) error {
163	ctx := context.Background()
164	options := types.PluginInstallOptions{
165		RemoteRef:            name,
166		AcceptAllPermissions: true,
167	}
168	responseReader, err := client.PluginInstall(ctx, "", options)
169	if err != nil {
170		return err
171	}
172	defer responseReader.Close()
173	// we have to read the response out here because the client API
174	// actually starts a goroutine which we can only be sure has
175	// completed when we get EOF from reading responseBody
176	_, err = ioutil.ReadAll(responseReader)
177	return err
178}
179