// InstallFlags adds flags for the common options on the FlagSet func (commonOpts *CommonOptions) InstallFlags(flags *pflag.FlagSet) { if dockerCertPath == "" { dockerCertPath = cliconfig.Dir() } flags.BoolVarP(&commonOpts.Debug, "debug", "D", false, "Enable debug mode") flags.StringVarP(&commonOpts.LogLevel, "log-level", "l", "info", "Set the logging level (\"debug\", \"info\", \"warn\", \"error\", \"fatal\")") flags.BoolVar(&commonOpts.TLS, "tls", false, "Use TLS; implied by --tlsverify") flags.BoolVar(&commonOpts.TLSVerify, FlagTLSVerify, dockerTLSVerify, "Use TLS and verify the remote") // TODO use flag flags.String("identity"}, "i", "", "Path to libtrust key file") commonOpts.TLSOptions = &tlsconfig.Options{ CAFile: filepath.Join(dockerCertPath, DefaultCaFile), CertFile: filepath.Join(dockerCertPath, DefaultCertFile), KeyFile: filepath.Join(dockerCertPath, DefaultKeyFile), } tlsOptions := commonOpts.TLSOptions flags.Var(opts.NewQuotedString(&tlsOptions.CAFile), "tlscacert", "Trust certs signed only by this CA") flags.Var(opts.NewQuotedString(&tlsOptions.CertFile), "tlscert", "Path to TLS certificate file") flags.Var(opts.NewQuotedString(&tlsOptions.KeyFile), "tlskey", "Path to TLS key file") hostOpt := opts.NewNamedListOptsRef("hosts", &commonOpts.Hosts, opts.ValidateHost) flags.VarP(hostOpt, "host", "H", "Daemon socket(s) to connect to") }
// Initialize the dockerCli runs initialization that must happen after command // line flags are parsed. func (cli *DockerCli) Initialize(opts *cliflags.ClientOptions) error { cli.configFile = LoadDefaultConfigFile(cli.err) var err error cli.client, err = NewAPIClientFromFlags(opts.Common, cli.configFile) if err != nil { return err } cli.defaultVersion = cli.client.ClientVersion() if opts.Common.TrustKey == "" { cli.keyFile = filepath.Join(cliconfig.Dir(), cliflags.DefaultTrustKeyFile) } else { cli.keyFile = opts.Common.TrustKey } if ping, err := cli.client.Ping(context.Background()); err == nil { cli.hasExperimental = ping.Experimental // since the new header was added in 1.25, assume server is 1.24 if header is not present. if ping.APIVersion == "" { ping.APIVersion = "1.24" } // if server version is lower than the current cli, downgrade if versions.LessThan(ping.APIVersion, cli.client.ClientVersion()) { cli.client.UpdateClientVersion(ping.APIVersion) } } return nil }
func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) { testRequires(c, NotaryHosting) repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL) targetName := fmt.Sprintf("%s:latest", repoName) s.notaryInitRepo(c, repoName) s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) s.notaryPublish(c, repoName) s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", targetName) icmd.RunCmd(icmd.Command(dockerBinary, "push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing) // check to make sure that the target has been added to targets/releases and not targets s.assertTargetInRoles(c, repoName, "latest", "targets/releases") s.assertTargetNotInRoles(c, repoName, "latest", "targets") // Try pull after push os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust")) icmd.RunCmd(icmd.Command(dockerBinary, "pull", targetName), trustedCmd).Assert(c, icmd.Expected{ Out: "Status: Image is up to date", }) }
func (s *DockerTrustSuite) TestTrustedPushSignsForRolesWithKeysAndValidPaths(c *check.C) { repoName := fmt.Sprintf("%v/dockerclirolesbykeysandpaths/trusted", privateRegistryURL) targetName := fmt.Sprintf("%s:latest", repoName) s.notaryInitRepo(c, repoName) s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public, "l", "z") s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public, "x", "y") s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public, "latest") s.notaryCreateDelegation(c, repoName, "targets/role4", s.not.keys[3].Public, "latest") // import everything except the third key s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) s.notaryImportKey(c, repoName, "targets/role4", s.not.keys[3].Private) s.notaryPublish(c, repoName) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", targetName) icmd.RunCmd(icmd.Command(dockerBinary, "push", targetName), trustedCmd).Assert(c, SuccessSigningAndPushing) // check to make sure that the target has been added to targets/role1 and targets/role4, and // not targets (because there are delegations) or targets/role2 (due to path restrictions) or // targets/role3 (due to missing key) s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role4") s.assertTargetNotInRoles(c, repoName, "latest", "targets") // Try pull after push os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust")) // pull should fail because none of these are the releases role icmd.RunCmd(icmd.Command(dockerBinary, "pull", targetName), trustedCmd).Assert(c, icmd.Expected{ ExitCode: 1, }) }
func (s *DockerTrustSuite) TestTrustedPushWithReleasesDelegationOnly(c *check.C) { testRequires(c, NotaryHosting) repoName := fmt.Sprintf("%v/dockerclireleasedelegationinitfirst/trusted", privateRegistryURL) targetName := fmt.Sprintf("%s:latest", repoName) s.notaryInitRepo(c, repoName) s.notaryCreateDelegation(c, repoName, "targets/releases", s.not.keys[0].Public) s.notaryPublish(c, repoName) s.notaryImportKey(c, repoName, "targets/releases", s.not.keys[0].Private) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", targetName) pushCmd := exec.Command(dockerBinary, "push", targetName) s.trustedCmd(pushCmd) out, _, err := runCommandWithOutput(pushCmd) c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) // check to make sure that the target has been added to targets/releases and not targets s.assertTargetInRoles(c, repoName, "latest", "targets/releases") s.assertTargetNotInRoles(c, repoName, "latest", "targets") // Try pull after push os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust")) pullCmd := exec.Command(dockerBinary, "pull", targetName) s.trustedCmd(pullCmd) out, _, err = runCommandWithOutput(pullCmd) c.Assert(err, check.IsNil, check.Commentf(out)) c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out)) }
// certificateDirectory returns the directory containing // TLS certificates for the given server. An error is // returned if there was an error parsing the server string. func certificateDirectory(server string) (string, error) { u, err := url.Parse(server) if err != nil { return "", err } return filepath.Join(cliconfig.Dir(), "tls", u.Host), nil }
// LoadDefaultConfigFile attempts to load the default config file and returns // an initialized ConfigFile struct if none is found. func LoadDefaultConfigFile(err io.Writer) *configfile.ConfigFile { configFile, e := cliconfig.Load(cliconfig.Dir()) if e != nil { fmt.Fprintf(err, "WARNING: Error loading config file:%v\n", e) } if !configFile.ContainsAuth() { credentials.DetectDefaultStore(configFile) } return configFile }
func (s *DockerTrustSuite) TearDownTest(c *check.C) { if s.reg != nil { s.reg.Close() } if s.not != nil { s.not.Close() } // Remove trusted keys and metadata after test os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust")) s.ds.TearDownTest(c) }
func migrateKey(config *daemon.Config) (err error) { // No migration necessary on Windows if runtime.GOOS == "windows" { return nil } // Migrate trust key if exists at ~/.docker/key.json and owned by current user oldPath := filepath.Join(cliconfig.Dir(), cliflags.DefaultTrustKeyFile) newPath := filepath.Join(getDaemonConfDir(config.Root), cliflags.DefaultTrustKeyFile) if _, statErr := os.Stat(newPath); os.IsNotExist(statErr) && currentUserIsOwner(oldPath) { defer func() { // Ensure old path is removed if no error occurred if err == nil { err = os.Remove(oldPath) } else { logrus.Warnf("Key migration failed, key file not removed at %s", oldPath) os.Remove(newPath) } }() if err := system.MkdirAll(getDaemonConfDir(config.Root), os.FileMode(0644)); err != nil { return fmt.Errorf("Unable to create daemon configuration directory: %s", err) } newFile, err := os.OpenFile(newPath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) if err != nil { return fmt.Errorf("error creating key file %q: %s", newPath, err) } defer newFile.Close() oldFile, err := os.Open(oldPath) if err != nil { return fmt.Errorf("error opening key file %q: %s", oldPath, err) } defer oldFile.Close() if _, err := io.Copy(newFile, oldFile); err != nil { return fmt.Errorf("error copying key: %s", err) } logrus.Infof("Migrated key from %s to %s", oldPath, newPath) } return nil }
func (s *DockerTrustSuite) TestTrustedPush(c *check.C) { repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", repoName) icmd.RunCmd(icmd.Command(dockerBinary, "push", repoName), trustedCmd).Assert(c, SuccessSigningAndPushing) // Try pull after push icmd.RunCmd(icmd.Command(dockerBinary, "pull", repoName), trustedCmd).Assert(c, icmd.Expected{ Out: "Status: Image is up to date", }) // Assert that we rotated the snapshot key to the server by checking our local keystore contents, err := ioutil.ReadDir(filepath.Join(cliconfig.Dir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest")) c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files")) // Check that we only have 1 key (targets key) c.Assert(contents, checker.HasLen, 1) }
func (s *DockerTrustSuite) TestTrustedPushSignsAllFirstLevelRolesWeHaveKeysFor(c *check.C) { testRequires(c, NotaryHosting) repoName := fmt.Sprintf("%v/dockerclimanyroles/trusted", privateRegistryURL) targetName := fmt.Sprintf("%s:latest", repoName) s.notaryInitRepo(c, repoName) s.notaryCreateDelegation(c, repoName, "targets/role1", s.not.keys[0].Public) s.notaryCreateDelegation(c, repoName, "targets/role2", s.not.keys[1].Public) s.notaryCreateDelegation(c, repoName, "targets/role3", s.not.keys[2].Public) // import everything except the third key s.notaryImportKey(c, repoName, "targets/role1", s.not.keys[0].Private) s.notaryImportKey(c, repoName, "targets/role2", s.not.keys[1].Private) s.notaryCreateDelegation(c, repoName, "targets/role1/subrole", s.not.keys[3].Public) s.notaryImportKey(c, repoName, "targets/role1/subrole", s.not.keys[3].Private) s.notaryPublish(c, repoName) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", targetName) pushCmd := exec.Command(dockerBinary, "push", targetName) s.trustedCmd(pushCmd) out, _, err := runCommandWithOutput(pushCmd) c.Assert(err, check.IsNil, check.Commentf("trusted push failed: %s\n%s", err, out)) c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push with existing tag")) // check to make sure that the target has been added to targets/role1 and targets/role2, and // not targets (because there are delegations) or targets/role3 (due to missing key) or // targets/role1/subrole (due to it being a second level delegation) s.assertTargetInRoles(c, repoName, "latest", "targets/role1", "targets/role2") s.assertTargetNotInRoles(c, repoName, "latest", "targets") // Try pull after push os.RemoveAll(filepath.Join(cliconfig.Dir(), "trust")) // pull should fail because none of these are the releases role pullCmd := exec.Command(dockerBinary, "pull", targetName) s.trustedCmd(pullCmd) out, _, err = runCommandWithOutput(pullCmd) c.Assert(err, check.NotNil, check.Commentf(out)) }
func (s *DockerTrustSuite) TestTrustedPush(c *check.C) { repoName := fmt.Sprintf("%v/dockerclitrusted/pushtest:latest", privateRegistryURL) // tag the image and upload it to the private registry dockerCmd(c, "tag", "busybox", repoName) pushCmd := exec.Command(dockerBinary, "push", repoName) s.trustedCmd(pushCmd) out, _, err := runCommandWithOutput(pushCmd) c.Assert(err, check.IsNil, check.Commentf("Error running trusted push: %s\n%s", err, out)) c.Assert(out, checker.Contains, "Signing and pushing trust metadata", check.Commentf("Missing expected output on trusted push")) // Try pull after push pullCmd := exec.Command(dockerBinary, "pull", repoName) s.trustedCmd(pullCmd) out, _, err = runCommandWithOutput(pullCmd) c.Assert(err, check.IsNil, check.Commentf(out)) c.Assert(string(out), checker.Contains, "Status: Image is up to date", check.Commentf(out)) // Assert that we rotated the snapshot key to the server by checking our local keystore contents, err := ioutil.ReadDir(filepath.Join(cliconfig.Dir(), "trust/private/tuf_keys", privateRegistryURL, "dockerclitrusted/pushtest")) c.Assert(err, check.IsNil, check.Commentf("Unable to read local tuf key files")) // Check that we only have 1 key (targets key) c.Assert(contents, checker.HasLen, 1) }
func newTestNotary(c *check.C) (*testNotary, error) { // generate server config template := `{ "server": { "http_addr": "%s", "tls_key_file": "%s", "tls_cert_file": "%s" }, "trust_service": { "type": "local", "hostname": "", "port": "", "key_algorithm": "ed25519" }, "logging": { "level": "debug" }, "storage": { "backend": "memory" } }` tmp, err := ioutil.TempDir("", "notary-test-") if err != nil { return nil, err } confPath := filepath.Join(tmp, "config.json") config, err := os.Create(confPath) if err != nil { return nil, err } defer config.Close() workingDir, err := os.Getwd() if err != nil { return nil, err } if _, err := fmt.Fprintf(config, template, notaryHost, filepath.Join(workingDir, "fixtures/notary/localhost.key"), filepath.Join(workingDir, "fixtures/notary/localhost.cert")); err != nil { os.RemoveAll(tmp) return nil, err } // generate client config clientConfPath := filepath.Join(tmp, "client-config.json") clientConfig, err := os.Create(clientConfPath) if err != nil { return nil, err } defer clientConfig.Close() template = `{ "trust_dir" : "%s", "remote_server": { "url": "%s", "skipTLSVerify": true } }` if _, err = fmt.Fprintf(clientConfig, template, filepath.Join(cliconfig.Dir(), "trust"), notaryURL); err != nil { os.RemoveAll(tmp) return nil, err } // load key fixture filenames var keys []keyPair for i := 1; i < 5; i++ { keys = append(keys, keyPair{ Public: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.crt", i)), Private: filepath.Join(workingDir, fmt.Sprintf("fixtures/notary/delgkey%v.key", i)), }) } // run notary-server cmd := exec.Command(notaryServerBinary, "-config", confPath) if err := cmd.Start(); err != nil { os.RemoveAll(tmp) if os.IsNotExist(err) { c.Skip(err.Error()) } return nil, err } testNotary := &testNotary{ cmd: cmd, dir: tmp, keys: keys, } // Wait for notary to be ready to serve requests. for i := 1; i <= 20; i++ { if err = testNotary.Ping(); err == nil { break } time.Sleep(10 * time.Millisecond * time.Duration(i*i)) } if err != nil { c.Fatalf("Timeout waiting for test notary to become available: %s", err) } return testNotary, nil }
func trustDirectory() string { return filepath.Join(cliconfig.Dir(), "trust") }
func defaultPath(filename string) string { return filepath.Join(cliconfig.Dir(), filename) }
func newDockerCommand(dockerCli *command.DockerCli) *cobra.Command { opts := cliflags.NewClientOptions() var flags *pflag.FlagSet cmd := &cobra.Command{ Use: "docker [OPTIONS] COMMAND [ARG...]", Short: "A self-sufficient runtime for containers", SilenceUsage: true, SilenceErrors: true, TraverseChildren: true, Args: noArgs, RunE: func(cmd *cobra.Command, args []string) error { if opts.Version { showVersion() return nil } return dockerCli.ShowHelp(cmd, args) }, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { // daemon command is special, we redirect directly to another binary if cmd.Name() == "daemon" { return nil } // flags must be the top-level command flags, not cmd.Flags() opts.Common.SetDefaultOptions(flags) dockerPreRun(opts) if err := dockerCli.Initialize(opts); err != nil { return err } return isSupported(cmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()) }, } cli.SetupRootCommand(cmd) cmd.SetHelpFunc(func(ccmd *cobra.Command, args []string) { if dockerCli.Client() == nil { // when using --help, PersistenPreRun is not called, so initialization is needed. // flags must be the top-level command flags, not cmd.Flags() opts.Common.SetDefaultOptions(flags) dockerPreRun(opts) dockerCli.Initialize(opts) } if err := isSupported(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()); err != nil { ccmd.Println(err) return } hideUnsupportedFeatures(ccmd, dockerCli.Client().ClientVersion(), dockerCli.HasExperimental()) if err := ccmd.Help(); err != nil { ccmd.Println(err) } }) flags = cmd.Flags() flags.BoolVarP(&opts.Version, "version", "v", false, "Print version information and quit") flags.StringVar(&opts.ConfigDir, "config", cliconfig.Dir(), "Location of client config files") opts.Common.InstallFlags(flags) cmd.SetOutput(dockerCli.Out()) cmd.AddCommand(newDaemonCommand()) commands.AddCommands(cmd, dockerCli) return cmd }