Skip to content

Commit aec9406

Browse files
committed
Populates CA certificates
This PR allows users to add root CA certificates in minikube VM. CA certificates in $HOME/.minikube/certs will be populated to system certificate store. Note: This requires a change to minikube ISO so you may need to delete and start a brand new minikube VM. Signed-off-by: Zhongcheng Lao <[email protected]>
1 parent a7500b3 commit aec9406

File tree

3 files changed

+155
-1
lines changed

3 files changed

+155
-1
lines changed

pkg/minikube/bootstrapper/certs.go

+121-1
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,11 @@ limitations under the License.
1717
package bootstrapper
1818

1919
import (
20+
"encoding/pem"
2021
"fmt"
22+
"io/ioutil"
2123
"net"
24+
"os"
2225
"path"
2326
"path/filepath"
2427
"strings"
@@ -66,6 +69,19 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error {
6669
copyableFiles = append(copyableFiles, certFile)
6770
}
6871

72+
caCerts, err := collectCACerts()
73+
if err != nil {
74+
return err
75+
}
76+
for src, dst := range caCerts {
77+
certFile, err := assets.NewFileAsset(src, path.Dir(dst), path.Base(dst), "0644")
78+
if err != nil {
79+
return err
80+
}
81+
82+
copyableFiles = append(copyableFiles, certFile)
83+
}
84+
6985
kubeCfgSetup := &util.KubeConfigSetup{
7086
ClusterName: k8s.NodeName,
7187
ClusterServerAddress: fmt.Sprintf("https://localhost:%d", k8s.NodePort),
@@ -76,7 +92,7 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error {
7692
}
7793

7894
kubeCfg := api.NewConfig()
79-
err := util.PopulateKubeConfig(kubeCfgSetup, kubeCfg)
95+
err = util.PopulateKubeConfig(kubeCfgSetup, kubeCfg)
8096
if err != nil {
8197
return errors.Wrap(err, "populating kubeconfig")
8298
}
@@ -94,6 +110,11 @@ func SetupCerts(cmd command.Runner, k8s config.KubernetesConfig) error {
94110
return err
95111
}
96112
}
113+
114+
// configure CA certificates
115+
if err := configureCACerts(cmd, caCerts); err != nil {
116+
return errors.Wrapf(err, "error configuring CA certificates during provisioning %v", err)
117+
}
97118
return nil
98119
}
99120

@@ -197,3 +218,102 @@ func generateCerts(k8s config.KubernetesConfig) error {
197218

198219
return nil
199220
}
221+
222+
func collectCACerts() (map[string]string, error) {
223+
localPath := constants.GetMinipath()
224+
225+
isValidPem := func(hostpath string) (bool, error) {
226+
fileBytes, err := ioutil.ReadFile(hostpath)
227+
if err != nil {
228+
return false, err
229+
}
230+
231+
for {
232+
block, rest := pem.Decode(fileBytes)
233+
if block == nil {
234+
break
235+
}
236+
237+
if block.Type == "CERTIFICATE" {
238+
// certificate found
239+
return true, nil
240+
}
241+
fileBytes = rest
242+
}
243+
244+
return false, nil
245+
}
246+
247+
certFiles := map[string]string{}
248+
249+
certsDir := filepath.Join(localPath, "certs")
250+
err := filepath.Walk(certsDir, func(hostpath string, info os.FileInfo, err error) error {
251+
if info != nil && !info.IsDir() {
252+
ext := strings.ToLower(filepath.Ext(hostpath))
253+
if ext == ".crt" || ext == ".pem" {
254+
validPem, err := isValidPem(hostpath)
255+
if err != nil {
256+
return err
257+
}
258+
if validPem {
259+
filename := filepath.Base(hostpath)
260+
dst := fmt.Sprintf("%s.%s", strings.TrimSuffix(filename, ext), "pem")
261+
certFiles[hostpath] = path.Join(constants.CACertificatesDir, dst)
262+
}
263+
}
264+
}
265+
return nil
266+
})
267+
if err != nil {
268+
return nil, errors.Wrapf(err, "provisioning: traversal certificates dir %s", certsDir)
269+
}
270+
271+
for _, excluded := range []string{"ca.pem", "cert.pem"} {
272+
certFiles[filepath.Join(certsDir, excluded)] = ""
273+
}
274+
275+
// populates minikube CA
276+
certFiles[filepath.Join(localPath, "ca.crt")] = path.Join(constants.CACertificatesDir, "minikubeCA.pem")
277+
278+
filtered := map[string]string{}
279+
for k, v := range certFiles {
280+
if v != "" {
281+
filtered[k] = v
282+
}
283+
}
284+
return filtered, nil
285+
}
286+
287+
func configureCACerts(cmd command.Runner, caCerts map[string]string) error {
288+
getSubjectHash := func(hostpath string) (string, error) {
289+
out, err := cmd.CombinedOutput(fmt.Sprintf("openssl x509 -hash -noout -in '%s'", hostpath))
290+
if err != nil {
291+
return "", err
292+
}
293+
294+
stringHash := strings.ReplaceAll(out, "\n", "")
295+
return stringHash, nil
296+
}
297+
298+
for _, caCertFile := range caCerts {
299+
dstFilename := path.Base(caCertFile)
300+
certStorePath := path.Join(constants.SSLCertStoreDir, dstFilename)
301+
if err := cmd.Run(fmt.Sprintf("sudo test -f '%s'", certStorePath)); err != nil {
302+
if err := cmd.Run(fmt.Sprintf("sudo ln -s '%s' '%s'", caCertFile, certStorePath)); err != nil {
303+
return errors.Wrapf(err, "error making symbol link for certificate %s", caCertFile)
304+
}
305+
}
306+
subjectHash, err := getSubjectHash(caCertFile)
307+
if err != nil {
308+
return errors.Wrapf(err, "error calculating subject hash for certificate %s", caCertFile)
309+
}
310+
subjectHashLink := path.Join(constants.SSLCertStoreDir, fmt.Sprintf("%s.0", subjectHash))
311+
if err := cmd.Run(fmt.Sprintf("sudo test -f '%s'", subjectHashLink)); err != nil {
312+
if err := cmd.Run(fmt.Sprintf("sudo ln -s '%s' '%s'", certStorePath, subjectHashLink)); err != nil {
313+
return errors.Wrapf(err, "error making subject hash symbol link for certificate %s", caCertFile)
314+
}
315+
}
316+
}
317+
318+
return nil
319+
}

pkg/minikube/bootstrapper/certs_test.go

+29
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ limitations under the License.
1717
package bootstrapper
1818

1919
import (
20+
"fmt"
2021
"os"
22+
"path"
2123
"path/filepath"
2224
"testing"
2325

@@ -39,10 +41,37 @@ func TestSetupCerts(t *testing.T) {
3941
ServiceCIDR: util.DefaultServiceCIDR,
4042
}
4143

44+
if err := os.Mkdir(filepath.Join(tempDir, "certs"), 0777); err != nil {
45+
t.Fatalf("error create certificate directory: %v", err)
46+
}
47+
48+
if err := util.GenerateCACert(
49+
filepath.Join(tempDir, "certs", "mycert.pem"),
50+
filepath.Join(tempDir, "certs", "mykey.pem"),
51+
"Test Certificate",
52+
); err != nil {
53+
t.Fatalf("error generating certificate: %v", err)
54+
}
55+
56+
cmdMap := map[string]string{}
57+
certFilenames := map[string]string{"ca.crt": "minikubeCA.pem", "mycert.pem": "mycert.pem"}
58+
for _, dst := range certFilenames {
59+
certFile := path.Join(constants.CACertificatesDir, dst)
60+
certStorePath := path.Join(constants.SSLCertStoreDir, dst)
61+
certNameHash := "abcdef"
62+
remoteCertHashLink := path.Join(constants.SSLCertStoreDir, fmt.Sprintf("%s.0", certNameHash))
63+
cmdMap[fmt.Sprintf("sudo ln -s '%s' '%s'", certFile, certStorePath)] = "1"
64+
cmdMap[fmt.Sprintf("openssl x509 -hash -noout -in '%s'", certFile)] = certNameHash
65+
cmdMap[fmt.Sprintf("sudo ln -s '%s' '%s'", certStorePath, remoteCertHashLink)] = "1"
66+
}
67+
f.SetCommandToOutput(cmdMap)
68+
4269
var filesToBeTransferred []string
4370
for _, cert := range certs {
4471
filesToBeTransferred = append(filesToBeTransferred, filepath.Join(constants.GetMinipath(), cert))
4572
}
73+
filesToBeTransferred = append(filesToBeTransferred, filepath.Join(constants.GetMinipath(), "ca.crt"))
74+
filesToBeTransferred = append(filesToBeTransferred, filepath.Join(constants.GetMinipath(), "certs", "mycert.pem"))
4675

4776
if err := SetupCerts(f, k8s); err != nil {
4877
t.Fatalf("Error starting cluster: %v", err)

pkg/minikube/constants/constants.go

+5
Original file line numberDiff line numberDiff line change
@@ -426,3 +426,8 @@ const (
426426
// DriverDocumentation the documentation of the KVM driver
427427
DriverDocumentation = "https://github.com/kubernetes/minikube/blob/master/docs/drivers.md"
428428
)
429+
430+
const (
431+
CACertificatesDir = "/usr/share/ca-certificates"
432+
SSLCertStoreDir = "/etc/ssl/certs"
433+
)

0 commit comments

Comments
 (0)