Merge pull request #29668 from dmcgowan/plugins-dct

Support Docker Content Trust for plugins
This commit is contained in:
Brian Goff 2016-12-28 10:43:32 -05:00 коммит произвёл GitHub
Родитель 9fab262690 14e8bba4f5
Коммит a412c1b7a0
8 изменённых файлов: 179 добавлений и 10 удалений

Просмотреть файл

@ -170,7 +170,7 @@ func createContainer(ctx context.Context, dockerCli *command.DockerCli, config *
if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() { if ref, ok := ref.(reference.NamedTagged); ok && command.IsTrusted() {
var err error var err error
trustedRef, err = image.TrustedReference(ctx, dockerCli, ref) trustedRef, err = image.TrustedReference(ctx, dockerCli, ref, nil)
if err != nil { if err != nil {
return nil, err return nil, err
} }

Просмотреть файл

@ -235,7 +235,7 @@ func runBuild(dockerCli *command.DockerCli, options buildOptions) error {
var resolvedTags []*resolvedTag var resolvedTags []*resolvedTag
if command.IsTrusted() { if command.IsTrusted() {
translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) { translator := func(ctx context.Context, ref reference.NamedTagged) (reference.Canonical, error) {
return TrustedReference(ctx, dockerCli, ref) return TrustedReference(ctx, dockerCli, ref, nil)
} }
// Wrap the tar archive to replace the Dockerfile entry with the rewritten // Wrap the tar archive to replace the Dockerfile entry with the rewritten
// Dockerfile which uses trusted pulls. // Dockerfile which uses trusted pulls.

Просмотреть файл

@ -37,6 +37,11 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
defer responseBody.Close() defer responseBody.Close()
return PushTrustedReference(cli, repoInfo, ref, authConfig, responseBody)
}
// PushTrustedReference pushes a canonical reference to the trust server.
func PushTrustedReference(cli *command.DockerCli, repoInfo *registry.RepositoryInfo, ref reference.Named, authConfig types.AuthConfig, in io.Reader) error {
// If it is a trusted push we would like to find the target entry which match the // If it is a trusted push we would like to find the target entry which match the
// tag provided in the function and then do an AddTarget later. // tag provided in the function and then do an AddTarget later.
target := &client.Target{} target := &client.Target{}
@ -75,14 +80,14 @@ func trustedPush(ctx context.Context, cli *command.DockerCli, repoInfo *registry
default: default:
// We want trust signatures to always take an explicit tag, // We want trust signatures to always take an explicit tag,
// otherwise it will act as an untrusted push. // otherwise it will act as an untrusted push.
if err = jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), nil); err != nil { if err := jsonmessage.DisplayJSONMessagesToStream(in, cli.Out(), nil); err != nil {
return err return err
} }
fmt.Fprintln(cli.Out(), "No tag specified, skipping trust metadata push") fmt.Fprintln(cli.Out(), "No tag specified, skipping trust metadata push")
return nil return nil
} }
if err = jsonmessage.DisplayJSONMessagesToStream(responseBody, cli.Out(), handleTarget); err != nil { if err := jsonmessage.DisplayJSONMessagesToStream(in, cli.Out(), handleTarget); err != nil {
return err return err
} }
@ -315,8 +320,16 @@ func imagePullPrivileged(ctx context.Context, cli *command.DockerCli, authConfig
} }
// TrustedReference returns the canonical trusted reference for an image reference // TrustedReference returns the canonical trusted reference for an image reference
func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged) (reference.Canonical, error) { func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference.NamedTagged, rs registry.Service) (reference.Canonical, error) {
repoInfo, err := registry.ParseRepositoryInfo(ref) var (
repoInfo *registry.RepositoryInfo
err error
)
if rs != nil {
repoInfo, err = rs.ResolveRepository(ref)
} else {
repoInfo, err = registry.ParseRepositoryInfo(ref)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -332,7 +345,7 @@ func TrustedReference(ctx context.Context, cli *command.DockerCli, ref reference
t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole) t, err := notaryRepo.GetTargetByName(ref.Tag(), trust.ReleasesRole, data.CanonicalTargetsRole)
if err != nil { if err != nil {
return nil, err return nil, trust.NotaryError(repoInfo.FullName(), err)
} }
// Only list tags in the top level targets role or the releases delegation role - ignore // Only list tags in the top level targets role or the releases delegation role - ignore
// all other delegation roles // all other delegation roles

Просмотреть файл

@ -11,6 +11,7 @@ import (
registrytypes "github.com/docker/docker/api/types/registry" registrytypes "github.com/docker/docker/api/types/registry"
"github.com/docker/docker/cli" "github.com/docker/docker/cli"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/image"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
@ -46,6 +47,8 @@ func newInstallCommand(dockerCli *command.DockerCli) *cobra.Command {
flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install") flags.BoolVar(&options.disable, "disable", false, "Do not enable the plugin on install")
flags.StringVar(&options.alias, "alias", "", "Local name for plugin") flags.StringVar(&options.alias, "alias", "", "Local name for plugin")
command.AddTrustedFlags(flags, true)
return cmd return cmd
} }
@ -63,6 +66,24 @@ func getRepoIndexFromUnnormalizedRef(ref distreference.Named) (*registrytypes.In
return repoInfo.Index, nil return repoInfo.Index, nil
} }
type pluginRegistryService struct {
registry.Service
}
func (s pluginRegistryService) ResolveRepository(name reference.Named) (repoInfo *registry.RepositoryInfo, err error) {
repoInfo, err = s.Service.ResolveRepository(name)
if repoInfo != nil {
repoInfo.Class = "plugin"
}
return
}
func newRegistryService() registry.Service {
return pluginRegistryService{
Service: registry.NewService(registry.ServiceOptions{V2Only: true}),
}
}
func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error { func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
// Parse name using distribution reference package to support name // Parse name using distribution reference package to support name
// containing both tag and digest. Names with both tag and digest // containing both tag and digest. Names with both tag and digest
@ -85,13 +106,41 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
} }
alias = aref.String() alias = aref.String()
} }
ctx := context.Background()
index, err := getRepoIndexFromUnnormalizedRef(ref) index, err := getRepoIndexFromUnnormalizedRef(ref)
if err != nil { if err != nil {
return err return err
} }
ctx := context.Background() remote := ref.String()
_, isCanonical := ref.(distreference.Canonical)
if command.IsTrusted() && !isCanonical {
if alias == "" {
alias = ref.String()
}
var nt reference.NamedTagged
named, err := reference.ParseNamed(ref.Name())
if err != nil {
return err
}
if tagged, ok := ref.(distreference.Tagged); ok {
nt, err = reference.WithTag(named, tagged.Tag())
if err != nil {
return err
}
} else {
named = reference.WithDefaultTag(named)
nt = named.(reference.NamedTagged)
}
trusted, err := image.TrustedReference(ctx, dockerCli, nt, newRegistryService())
if err != nil {
return err
}
remote = trusted.String()
}
authConfig := command.ResolveAuthConfig(ctx, dockerCli, index) authConfig := command.ResolveAuthConfig(ctx, dockerCli, index)
@ -104,7 +153,7 @@ func runInstall(dockerCli *command.DockerCli, opts pluginOptions) error {
options := types.PluginInstallOptions{ options := types.PluginInstallOptions{
RegistryAuth: encodedAuth, RegistryAuth: encodedAuth,
RemoteRef: ref.String(), RemoteRef: remote,
Disabled: opts.disable, Disabled: opts.disable,
AcceptAllPermissions: opts.grantPerms, AcceptAllPermissions: opts.grantPerms,
AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.name), AcceptPermissionsFunc: acceptPrivileges(dockerCli, opts.name),

Просмотреть файл

@ -7,6 +7,7 @@ import (
"github.com/docker/docker/cli" "github.com/docker/docker/cli"
"github.com/docker/docker/cli/command" "github.com/docker/docker/cli/command"
"github.com/docker/docker/cli/command/image"
"github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/jsonmessage"
"github.com/docker/docker/reference" "github.com/docker/docker/reference"
"github.com/docker/docker/registry" "github.com/docker/docker/registry"
@ -22,6 +23,11 @@ func newPushCommand(dockerCli *command.DockerCli) *cobra.Command {
return runPush(dockerCli, args[0]) return runPush(dockerCli, args[0])
}, },
} }
flags := cmd.Flags()
command.AddTrustedFlags(flags, true)
return cmd return cmd
} }
@ -55,5 +61,11 @@ func runPush(dockerCli *command.DockerCli, name string) error {
return err return err
} }
defer responseBody.Close() defer responseBody.Close()
if command.IsTrusted() {
repoInfo.Class = "plugin"
return image.PushTrustedReference(dockerCli, repoInfo, named, authConfig, responseBody)
}
return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil) return jsonmessage.DisplayJSONMessagesToStream(responseBody, dockerCli.Out(), nil)
} }

Просмотреть файл

@ -147,8 +147,19 @@ func GetNotaryRepository(streams command.Streams, repoInfo *registry.RepositoryI
} }
} }
scope := auth.RepositoryScope{
Repository: repoInfo.FullName(),
Actions: actions,
Class: repoInfo.Class,
}
creds := simpleCredentialStore{auth: authConfig} creds := simpleCredentialStore{auth: authConfig}
tokenHandler := auth.NewTokenHandler(authTransport, creds, repoInfo.FullName(), actions...) tokenHandlerOptions := auth.TokenHandlerOptions{
Transport: authTransport,
Credentials: creds,
Scopes: []auth.Scope{scope},
ClientID: registry.AuthClientID,
}
tokenHandler := auth.NewTokenHandlerWithOptions(tokenHandlerOptions)
basicHandler := auth.NewBasicHandler(creds) basicHandler := auth.NewBasicHandler(creds)
modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler))) modifiers = append(modifiers, transport.RequestModifier(auth.NewAuthorizer(challengeManager, tokenHandler, basicHandler)))
tr := transport.NewTransport(base, modifiers...) tr := transport.NewTransport(base, modifiers...)

Просмотреть файл

@ -2,6 +2,7 @@ package main
import ( import (
"fmt" "fmt"
"os/exec"
"github.com/docker/docker/pkg/integration/checker" "github.com/docker/docker/pkg/integration/checker"
"github.com/go-check/check" "github.com/go-check/check"
@ -269,3 +270,63 @@ func (s *DockerSuite) TestPluginInspectOnWindows(c *check.C) {
c.Assert(out, checker.Contains, "plugins are not supported on this platform") c.Assert(out, checker.Contains, "plugins are not supported on this platform")
c.Assert(err.Error(), checker.Contains, "plugins are not supported on this platform") c.Assert(err.Error(), checker.Contains, "plugins are not supported on this platform")
} }
func (s *DockerTrustSuite) TestPluginTrustedInstall(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
trustedName := s.setupTrustedplugin(c, pNameWithTag, "trusted-plugin-install")
installCmd := exec.Command(dockerBinary, "plugin", "install", "--grant-all-permissions", trustedName)
s.trustedCmd(installCmd)
out, _, err := runCommandWithOutput(installCmd)
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "true")
out, _, err = dockerCmdWithError("plugin", "disable", trustedName)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
out, _, err = dockerCmdWithError("plugin", "enable", trustedName)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
out, _, err = dockerCmdWithError("plugin", "rm", "-f", trustedName)
c.Assert(err, checker.IsNil)
c.Assert(strings.TrimSpace(out), checker.Contains, trustedName)
// Try untrusted pull to ensure we pushed the tag to the registry
installCmd = exec.Command(dockerBinary, "plugin", "install", "--disable-content-trust=true", "--grant-all-permissions", trustedName)
s.trustedCmd(installCmd)
out, _, err = runCommandWithOutput(installCmd)
c.Assert(err, check.IsNil, check.Commentf(out))
c.Assert(string(out), checker.Contains, "Status: Downloaded", check.Commentf(out))
out, _, err = dockerCmdWithError("plugin", "ls")
c.Assert(err, checker.IsNil)
c.Assert(out, checker.Contains, "true")
}
func (s *DockerTrustSuite) TestPluginUntrustedInstall(c *check.C) {
testRequires(c, DaemonIsLinux, IsAmd64, Network)
pluginName := fmt.Sprintf("%v/dockercliuntrusted/plugintest:latest", privateRegistryURL)
// install locally and push to private registry
dockerCmd(c, "plugin", "install", "--grant-all-permissions", "--alias", pluginName, pNameWithTag)
dockerCmd(c, "plugin", "push", pluginName)
dockerCmd(c, "plugin", "rm", "-f", pluginName)
// Try trusted install on untrusted plugin
installCmd := exec.Command(dockerBinary, "plugin", "install", "--grant-all-permissions", pluginName)
s.trustedCmd(installCmd)
out, _, err := runCommandWithOutput(installCmd)
c.Assert(err, check.NotNil, check.Commentf(out))
c.Assert(string(out), checker.Contains, "Error: remote trust data does not exist", check.Commentf(out))
}

Просмотреть файл

@ -211,6 +211,29 @@ func (s *DockerTrustSuite) setupTrustedImage(c *check.C, name string) string {
return repoName return repoName
} }
func (s *DockerTrustSuite) setupTrustedplugin(c *check.C, source, name string) string {
repoName := fmt.Sprintf("%v/dockercli/%s:latest", privateRegistryURL, name)
// tag the image and upload it to the private registry
dockerCmd(c, "plugin", "install", "--grant-all-permissions", "--alias", repoName, source)
pushCmd := exec.Command(dockerBinary, "plugin", "push", repoName)
s.trustedCmd(pushCmd)
out, _, err := runCommandWithOutput(pushCmd)
if err != nil {
c.Fatalf("Error running trusted plugin push: %s\n%s", err, out)
}
if !strings.Contains(string(out), "Signing and pushing trust metadata") {
c.Fatalf("Missing expected output on trusted push:\n%s", out)
}
if out, status := dockerCmd(c, "plugin", "rm", "-f", repoName); status != 0 {
c.Fatalf("Error removing plugin %q\n%s", repoName, out)
}
return repoName
}
func notaryClientEnv(cmd *exec.Cmd) { func notaryClientEnv(cmd *exec.Cmd) {
pwd := "12345678" pwd := "12345678"
env := []string{ env := []string{