Skip to content

Commit f695e98

Browse files
author
Tibor Vass
committed
Revert "Remove the rest of v1 manifest support"
This reverts commit 98fc091 in order to keep registry v2 schema1 handling and libtrust-key-based engine ID. Because registry v2 schema1 was not officially deprecated and registries are still relying on it, this patch puts its logic back. However, registry v1 relics are not added back since v1 logic has been removed a while ago. This also fixes an engine upgrade issue in a swarm cluster. It was relying on the Engine ID to be the same upon upgrade, but the mentioned commit modified the logic to use UUID and from a different file. Since the libtrust key is always needed to support v2 schema1 pushes, that the old engine ID is based on the libtrust key, and that the engine ID needs to be conserved across upgrades, adding a UUID-based engine ID logic seems to add more complexity than it solves the problems. Hence reverting the engine ID changes as well. Signed-off-by: Tibor Vass <[email protected]>
1 parent 0811297 commit f695e98

File tree

17 files changed

+247
-54
lines changed

17 files changed

+247
-54
lines changed

cmd/dockerd/config.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
const (
1313
// defaultShutdownTimeout is the default shutdown timeout for the daemon
1414
defaultShutdownTimeout = 15
15+
// defaultTrustKeyFile is the default filename for the trust key
16+
defaultTrustKeyFile = "key.json"
1517
)
1618

1719
// installCommonConfigFlags adds flags to the pflag.FlagSet to configure the daemon
@@ -81,6 +83,13 @@ func installCommonConfigFlags(conf *config.Config, flags *pflag.FlagSet) error {
8183

8284
flags.IntVar(&conf.NetworkControlPlaneMTU, "network-control-plane-mtu", config.DefaultNetworkMtu, "Network Control plane MTU")
8385

86+
// "--deprecated-key-path" is to allow configuration of the key used
87+
// for the daemon ID and the deprecated image signing. It was never
88+
// exposed as a command line option but is added here to allow
89+
// overriding the default path in configuration.
90+
flags.Var(opts.NewQuotedString(&conf.TrustKeyPath), "deprecated-key-path", "Path to key file for ID and image signing")
91+
flags.MarkHidden("deprecated-key-path")
92+
8493
conf.MaxConcurrentDownloads = &maxConcurrentDownloads
8594
conf.MaxConcurrentUploads = &maxConcurrentUploads
8695
return nil
@@ -94,4 +103,10 @@ func installRegistryServiceFlags(options *registry.ServiceOptions, flags *pflag.
94103
flags.Var(ana, "allow-nondistributable-artifacts", "Allow push of nondistributable artifacts to registry")
95104
flags.Var(mirrors, "registry-mirror", "Preferred Docker registry mirror")
96105
flags.Var(insecureRegistries, "insecure-registry", "Enable insecure registry communication")
106+
107+
if runtime.GOOS != "windows" {
108+
// TODO: Remove this flag after 3 release cycles (18.03)
109+
flags.BoolVar(&options.V2Only, "disable-legacy-registry", true, "Disable contacting legacy registries")
110+
flags.MarkHidden("disable-legacy-registry")
111+
}
97112
}

cmd/dockerd/daemon.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,14 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
429429
conf.CommonTLSOptions.KeyFile = opts.TLSOptions.KeyFile
430430
}
431431

432+
if conf.TrustKeyPath == "" {
433+
daemonConfDir, err := getDaemonConfDir(conf.Root)
434+
if err != nil {
435+
return nil, err
436+
}
437+
conf.TrustKeyPath = filepath.Join(daemonConfDir, defaultTrustKeyFile)
438+
}
439+
432440
if flags.Changed("graph") && flags.Changed("data-root") {
433441
return nil, errors.New(`cannot specify both "--graph" and "--data-root" option`)
434442
}
@@ -451,6 +459,17 @@ func loadDaemonCliConfig(opts *daemonOptions) (*config.Config, error) {
451459
return nil, err
452460
}
453461

462+
if runtime.GOOS != "windows" {
463+
if flags.Changed("disable-legacy-registry") {
464+
// TODO: Remove this error after 3 release cycles (18.03)
465+
return nil, errors.New("ERROR: The '--disable-legacy-registry' flag has been removed. Interacting with legacy (v1) registries is no longer supported")
466+
}
467+
if !conf.V2Only {
468+
// TODO: Remove this error after 3 release cycles (18.03)
469+
return nil, errors.New("ERROR: The 'disable-legacy-registry' configuration option has been removed. Interacting with legacy (v1) registries is no longer supported")
470+
}
471+
}
472+
454473
if flags.Changed("graph") {
455474
logrus.Warnf(`The "-g / --graph" flag is deprecated. Please use "--data-root" instead`)
456475
}

cmd/dockerd/daemon_unix.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ func setDefaultUmask() error {
5858
return nil
5959
}
6060

61+
func getDaemonConfDir(_ string) (string, error) {
62+
return getDefaultDaemonConfigDir()
63+
}
64+
6165
func (cli *DaemonCli) getPlatformContainerdDaemonOpts() ([]supervisor.DaemonOpt, error) {
6266
opts := []supervisor.DaemonOpt{
6367
supervisor.WithOOMScore(cli.Config.OOMScoreAdjust),

cmd/dockerd/daemon_windows.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"net"
77
"os"
8+
"path/filepath"
89
"time"
910

1011
"github.com/docker/docker/daemon/config"
@@ -23,6 +24,10 @@ func setDefaultUmask() error {
2324
return nil
2425
}
2526

27+
func getDaemonConfDir(root string) (string, error) {
28+
return filepath.Join(root, `\config`), nil
29+
}
30+
2631
// preNotifySystem sends a message to the host when the API is active, but before the daemon is
2732
func preNotifySystem() {
2833
// start the service now to prevent timeouts waiting for daemon to start

daemon/config/config.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"io/ioutil"
99
"os"
1010
"reflect"
11+
"runtime"
1112
"strings"
1213
"sync"
1314

@@ -134,6 +135,12 @@ type CommonConfig struct {
134135
SocketGroup string `json:"group,omitempty"`
135136
CorsHeaders string `json:"api-cors-header,omitempty"`
136137

138+
// TrustKeyPath is used to generate the daemon ID and for signing schema 1 manifests
139+
// when pushing to a registry which does not support schema 2. This field is marked as
140+
// deprecated because schema 1 manifests are deprecated in favor of schema 2 and the
141+
// daemon ID will use a dedicated identifier not shared with exported signatures.
142+
TrustKeyPath string `json:"deprecated-key-path,omitempty"`
143+
137144
// LiveRestoreEnabled determines whether we should keep containers
138145
// alive upon daemon shutdown/start
139146
LiveRestoreEnabled bool `json:"live-restore,omitempty"`
@@ -240,6 +247,9 @@ func New() *Config {
240247
config.LogConfig.Config = make(map[string]string)
241248
config.ClusterOpts = make(map[string]string)
242249

250+
if runtime.GOOS != "linux" {
251+
config.V2Only = true
252+
}
243253
return &config
244254
}
245255

daemon/daemon.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -973,7 +973,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
973973
return nil, err
974974
}
975975

976-
uuid, err := loadOrCreateUUID(filepath.Join(config.Root, "engine_uuid"))
976+
trustKey, err := loadOrCreateTrustKey(config.TrustKeyPath)
977977
if err != nil {
978978
return nil, err
979979
}
@@ -1018,7 +1018,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
10181018
return nil, errors.New("Devices cgroup isn't mounted")
10191019
}
10201020

1021-
d.ID = uuid
1021+
d.ID = trustKey.PublicKey().KeyID()
10221022
d.repository = daemonRepo
10231023
d.containers = container.NewMemoryStore()
10241024
if d.containersReplica, err = container.NewViewDB(); err != nil {
@@ -1049,6 +1049,7 @@ func NewDaemon(ctx context.Context, config *config.Config, pluginStore *plugin.S
10491049
MaxConcurrentUploads: *config.MaxConcurrentUploads,
10501050
ReferenceStore: rs,
10511051
RegistryService: registryService,
1052+
TrustKey: trustKey,
10521053
})
10531054

10541055
go d.execCommandGC()

daemon/images/image_push.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ func (i *ImageService) PushImage(ctx context.Context, image, tag string, metaHea
5454
},
5555
ConfigMediaType: schema2.MediaTypeImageConfig,
5656
LayerStores: distribution.NewLayerProvidersFromStores(i.layerStores),
57+
TrustKey: i.trustKey,
5758
UploadManager: i.uploadManager,
5859
}
5960

daemon/images/service.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/docker/docker/layer"
1515
dockerreference "github.com/docker/docker/reference"
1616
"github.com/docker/docker/registry"
17+
"github.com/docker/libtrust"
1718
"github.com/opencontainers/go-digest"
1819
"github.com/pkg/errors"
1920
"github.com/sirupsen/logrus"
@@ -39,6 +40,7 @@ type ImageServiceConfig struct {
3940
MaxConcurrentUploads int
4041
ReferenceStore dockerreference.Store
4142
RegistryService registry.Service
43+
TrustKey libtrust.PrivateKey
4244
}
4345

4446
// NewImageService returns a new ImageService from a configuration
@@ -54,6 +56,7 @@ func NewImageService(config ImageServiceConfig) *ImageService {
5456
layerStores: config.LayerStores,
5557
referenceStore: config.ReferenceStore,
5658
registryService: config.RegistryService,
59+
trustKey: config.TrustKey,
5760
uploadManager: xfer.NewLayerUploadManager(config.MaxConcurrentUploads),
5861
}
5962
}
@@ -69,6 +72,7 @@ type ImageService struct {
6972
pruneRunning int32
7073
referenceStore dockerreference.Store
7174
registryService registry.Service
75+
trustKey libtrust.PrivateKey
7276
uploadManager *xfer.LayerUploadManager
7377
}
7478

daemon/trustkey.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package daemon // import "github.com/docker/docker/daemon"
2+
3+
import (
4+
"encoding/json"
5+
"encoding/pem"
6+
"fmt"
7+
"os"
8+
"path/filepath"
9+
10+
"github.com/docker/docker/pkg/ioutils"
11+
"github.com/docker/docker/pkg/system"
12+
"github.com/docker/libtrust"
13+
)
14+
15+
// LoadOrCreateTrustKey attempts to load the libtrust key at the given path,
16+
// otherwise generates a new one
17+
// TODO: this should use more of libtrust.LoadOrCreateTrustKey which may need
18+
// a refactor or this function to be moved into libtrust
19+
func loadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
20+
err := system.MkdirAll(filepath.Dir(trustKeyPath), 0755, "")
21+
if err != nil {
22+
return nil, err
23+
}
24+
trustKey, err := libtrust.LoadKeyFile(trustKeyPath)
25+
if err == libtrust.ErrKeyFileDoesNotExist {
26+
trustKey, err = libtrust.GenerateECP256PrivateKey()
27+
if err != nil {
28+
return nil, fmt.Errorf("Error generating key: %s", err)
29+
}
30+
encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
31+
if err != nil {
32+
return nil, fmt.Errorf("Error serializing key: %s", err)
33+
}
34+
if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
35+
return nil, fmt.Errorf("Error saving key file: %s", err)
36+
}
37+
} else if err != nil {
38+
return nil, fmt.Errorf("Error loading key file %s: %s", trustKeyPath, err)
39+
}
40+
return trustKey, nil
41+
}
42+
43+
func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
44+
if ext == ".json" || ext == ".jwk" {
45+
encoded, err = json.Marshal(key)
46+
if err != nil {
47+
return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
48+
}
49+
} else {
50+
pemBlock, err := key.PEMBlock()
51+
if err != nil {
52+
return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
53+
}
54+
encoded = pem.EncodeToMemory(pemBlock)
55+
}
56+
return
57+
}

daemon/trustkey_test.go

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package daemon // import "github.com/docker/docker/daemon"
2+
3+
import (
4+
"io/ioutil"
5+
"os"
6+
"path/filepath"
7+
"testing"
8+
9+
"gotest.tools/assert"
10+
is "gotest.tools/assert/cmp"
11+
"gotest.tools/fs"
12+
)
13+
14+
// LoadOrCreateTrustKey
15+
func TestLoadOrCreateTrustKeyInvalidKeyFile(t *testing.T) {
16+
tmpKeyFolderPath, err := ioutil.TempDir("", "api-trustkey-test")
17+
assert.NilError(t, err)
18+
defer os.RemoveAll(tmpKeyFolderPath)
19+
20+
tmpKeyFile, err := ioutil.TempFile(tmpKeyFolderPath, "keyfile")
21+
assert.NilError(t, err)
22+
23+
_, err = loadOrCreateTrustKey(tmpKeyFile.Name())
24+
assert.Check(t, is.ErrorContains(err, "Error loading key file"))
25+
}
26+
27+
func TestLoadOrCreateTrustKeyCreateKeyWhenFileDoesNotExist(t *testing.T) {
28+
tmpKeyFolderPath := fs.NewDir(t, "api-trustkey-test")
29+
defer tmpKeyFolderPath.Remove()
30+
31+
// Without the need to create the folder hierarchy
32+
tmpKeyFile := tmpKeyFolderPath.Join("keyfile")
33+
34+
key, err := loadOrCreateTrustKey(tmpKeyFile)
35+
assert.NilError(t, err)
36+
assert.Check(t, key != nil)
37+
38+
_, err = os.Stat(tmpKeyFile)
39+
assert.NilError(t, err, "key file doesn't exist")
40+
}
41+
42+
func TestLoadOrCreateTrustKeyCreateKeyWhenDirectoryDoesNotExist(t *testing.T) {
43+
tmpKeyFolderPath := fs.NewDir(t, "api-trustkey-test")
44+
defer tmpKeyFolderPath.Remove()
45+
tmpKeyFile := tmpKeyFolderPath.Join("folder/hierarchy/keyfile")
46+
47+
key, err := loadOrCreateTrustKey(tmpKeyFile)
48+
assert.NilError(t, err)
49+
assert.Check(t, key != nil)
50+
51+
_, err = os.Stat(tmpKeyFile)
52+
assert.NilError(t, err, "key file doesn't exist")
53+
}
54+
55+
func TestLoadOrCreateTrustKeyCreateKeyNoPath(t *testing.T) {
56+
defer os.Remove("keyfile")
57+
key, err := loadOrCreateTrustKey("keyfile")
58+
assert.NilError(t, err)
59+
assert.Check(t, key != nil)
60+
61+
_, err = os.Stat("keyfile")
62+
assert.NilError(t, err, "key file doesn't exist")
63+
}
64+
65+
func TestLoadOrCreateTrustKeyLoadValidKey(t *testing.T) {
66+
tmpKeyFile := filepath.Join("testdata", "keyfile")
67+
key, err := loadOrCreateTrustKey(tmpKeyFile)
68+
assert.NilError(t, err)
69+
expected := "AWX2:I27X:WQFX:IOMK:CNAK:O7PW:VYNB:ZLKC:CVAE:YJP2:SI4A:XXAY"
70+
assert.Check(t, is.Contains(key.String(), expected))
71+
}

0 commit comments

Comments
 (0)