Browse code

Atomically save libtrust key file

The libtrust keyfile which is used to set the "ID" property of a daemon must be generated or loaded on every startup.
If the process crashes during startup this could cause the file to be incomplete causing future startup errors.
Ensure that the file is written atomically to ensure the file is never in an incomplete state.

Fixes #23985

Signed-off-by: Derek McGowan <derek@mcgstyle.net> (github: dmcgowan)

Derek McGowan authored on 2016/06/28 07:54:39
Showing 1 changed files
... ...
@@ -1,14 +1,18 @@
1 1
 package api
2 2
 
3 3
 import (
4
+	"encoding/json"
5
+	"encoding/pem"
4 6
 	"fmt"
5 7
 	"mime"
8
+	"os"
6 9
 	"path/filepath"
7 10
 	"sort"
8 11
 	"strconv"
9 12
 	"strings"
10 13
 
11 14
 	"github.com/Sirupsen/logrus"
15
+	"github.com/docker/docker/pkg/ioutils"
12 16
 	"github.com/docker/docker/pkg/system"
13 17
 	"github.com/docker/engine-api/types"
14 18
 	"github.com/docker/libtrust"
... ...
@@ -135,7 +139,11 @@ func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
135 135
 		if err != nil {
136 136
 			return nil, fmt.Errorf("Error generating key: %s", err)
137 137
 		}
138
-		if err := libtrust.SaveKey(trustKeyPath, trustKey); err != nil {
138
+		encodedKey, err := serializePrivateKey(trustKey, filepath.Ext(trustKeyPath))
139
+		if err != nil {
140
+			return nil, fmt.Errorf("Error serializing key: %s", err)
141
+		}
142
+		if err := ioutils.AtomicWriteFile(trustKeyPath, encodedKey, os.FileMode(0600)); err != nil {
139 143
 			return nil, fmt.Errorf("Error saving key file: %s", err)
140 144
 		}
141 145
 	} else if err != nil {
... ...
@@ -143,3 +151,19 @@ func LoadOrCreateTrustKey(trustKeyPath string) (libtrust.PrivateKey, error) {
143 143
 	}
144 144
 	return trustKey, nil
145 145
 }
146
+
147
+func serializePrivateKey(key libtrust.PrivateKey, ext string) (encoded []byte, err error) {
148
+	if ext == ".json" || ext == ".jwk" {
149
+		encoded, err = json.Marshal(key)
150
+		if err != nil {
151
+			return nil, fmt.Errorf("unable to encode private key JWK: %s", err)
152
+		}
153
+	} else {
154
+		pemBlock, err := key.PEMBlock()
155
+		if err != nil {
156
+			return nil, fmt.Errorf("unable to encode private key PEM: %s", err)
157
+		}
158
+		encoded = pem.EncodeToMemory(pemBlock)
159
+	}
160
+	return
161
+}