[lxc-devel] [distrobuilder/master] Add source verification

monstermunchkin on Github lxc-bot at linuxcontainers.org
Thu Feb 15 16:21:08 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 308 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180215/b7285865/attachment.bin>
-------------- next part --------------
From 0a3a7f8d8575aba5e4f66b359659cb67cb583786 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 14 Feb 2018 17:59:39 +0100
Subject: [PATCH 1/5] shared: Add file verification

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 shared/net.go                 |  37 +++++++++++++------
 shared/util.go                |  33 +++++++++++++++++
 shared/utils_test.go          |  81 ++++++++++++++++++++++++++++++++++++++++++
 testdata/testfile             |   1 +
 testdata/testfile-invalid.asc |  20 +++++++++++
 testdata/testfile.asc         |  20 +++++++++++
 testdata/testfile.gpg         | Bin 0 -> 627 bytes
 testdata/testfile.sig         | Bin 0 -> 566 bytes
 8 files changed, 181 insertions(+), 11 deletions(-)
 create mode 100644 shared/utils_test.go
 create mode 100644 testdata/testfile
 create mode 100644 testdata/testfile-invalid.asc
 create mode 100644 testdata/testfile.asc
 create mode 100644 testdata/testfile.gpg
 create mode 100644 testdata/testfile.sig

diff --git a/shared/net.go b/shared/net.go
index 63c927f..84dcb78 100644
--- a/shared/net.go
+++ b/shared/net.go
@@ -82,19 +82,35 @@ func Download(file, checksum string) error {
 	return nil
 }
 
+// downloadChecksum downloads or opens URL, and matches fname against the
+// sha256sums inside of the downloaded or opened file.
 func downloadChecksum(URL string, fname string) (string, error) {
-	var client http.Client
+	var (
+		client   http.Client
+		tempFile *os.File
+		err      error
+	)
 
-	tempFile, err := ioutil.TempFile(os.TempDir(), "sha256.")
-	if err != nil {
-		return "", err
-	}
-	defer os.Remove(tempFile.Name())
+	// do not re-download checksum file if it's already present
+	fi, err := os.Stat(filepath.Join(os.TempDir(), URL))
+	if err == nil && !fi.IsDir() {
+		tempFile, err = os.Open(filepath.Join(os.TempDir(), URL))
+		if err != nil {
+			return "", err
+		}
+		defer os.Remove(tempFile.Name())
+	} else {
+		tempFile, err = ioutil.TempFile(os.TempDir(), "sha256.")
+		if err != nil {
+			return "", err
+		}
+		defer os.Remove(tempFile.Name())
 
-	_, err = lxd.DownloadFileSha256(&client, "", nil, nil, "", URL, "", tempFile)
-	// ignore hash mismatch
-	if err != nil && !strings.HasPrefix(err.Error(), "Hash mismatch") {
-		return "", err
+		_, err = lxd.DownloadFileSha256(&client, "", nil, nil, "", URL, "", tempFile)
+		// ignore hash mismatch
+		if err != nil && !strings.HasPrefix(err.Error(), "Hash mismatch") {
+			return "", err
+		}
 	}
 
 	tempFile.Seek(0, 0)
@@ -102,7 +118,6 @@ func downloadChecksum(URL string, fname string) (string, error) {
 	scanner := bufio.NewScanner(tempFile)
 	for scanner.Scan() {
 		s := strings.Split(scanner.Text(), " ")
-		//if len(s) == 2 && s[1] == fmt.Sprintf("*%s", filepath.Base(fname)) {
 		matched, _ := regexp.MatchString(fmt.Sprintf(".*%s", filepath.Base(fname)), s[len(s)-1])
 		if matched {
 			return s[0], nil
diff --git a/shared/util.go b/shared/util.go
index 25511a1..2d6e0d6 100644
--- a/shared/util.go
+++ b/shared/util.go
@@ -4,6 +4,9 @@ import (
 	"io"
 	"os"
 	"os/exec"
+	"path/filepath"
+
+	lxd "github.com/lxc/lxd/shared"
 )
 
 // Copy copies a file.
@@ -42,3 +45,33 @@ func RunCommand(name string, arg ...string) error {
 
 	return cmd.Run()
 }
+
+// VerifyFile verifies a file using gpg.
+func VerifyFile(signedFile, signatureFile string, keys []string) bool {
+	gpgDir := filepath.Join(os.TempDir(), "distrobuilder.gpg")
+
+	err := os.MkdirAll(gpgDir, 0700)
+	if err != nil {
+		return false
+	}
+	defer os.RemoveAll(gpgDir)
+
+	_, err = lxd.RunCommand("gpg", append([]string{"--homedir", gpgDir, "--recv-keys"}, keys...)...)
+	if err != nil {
+		return false
+	}
+
+	if signatureFile != "" {
+		_, err = lxd.RunCommand("gpg", "--homedir", gpgDir, "--verify", signatureFile, signedFile)
+		if err != nil {
+			return false
+		}
+	} else {
+		_, err = lxd.RunCommand("gpg", "--homedir", gpgDir, "--verify", signedFile)
+		if err != nil {
+			return false
+		}
+	}
+
+	return true
+}
diff --git a/shared/utils_test.go b/shared/utils_test.go
new file mode 100644
index 0000000..f65718c
--- /dev/null
+++ b/shared/utils_test.go
@@ -0,0 +1,81 @@
+package shared
+
+import (
+	"fmt"
+	"log"
+	"os"
+	"path/filepath"
+	"testing"
+)
+
+func TestVerifyFile(t *testing.T) {
+	wd, err := os.Getwd()
+	if err != nil {
+		fmt.Errorf("Failed to retrieve working directory: %s", err)
+	}
+	testdataDir := filepath.Join(wd, "..", "testdata")
+
+	keys := []string{"0x5DE8949A899C8D99"}
+
+	tests := []struct {
+		name          string
+		signedFile    string
+		signatureFile string
+		keys          []string
+		shouldFail    bool
+	}{
+		{
+			"testfile with detached signature",
+			filepath.Join(testdataDir, "testfile"),
+			filepath.Join(testdataDir, "testfile.sig"),
+			keys,
+			false,
+		},
+		{
+			"testfile with cleartext signature",
+			filepath.Join(testdataDir, "testfile.asc"),
+			"",
+			keys,
+			false,
+		},
+		{
+			"testfile with invalid cleartext signature",
+			filepath.Join(testdataDir, "testfile-invalid.asc"),
+			"",
+			keys,
+			true,
+		},
+		{
+			"testfile with normal signature",
+			filepath.Join(testdataDir, "testfile.gpg"),
+			"",
+			keys,
+			false,
+		},
+		{
+			"no keys",
+			filepath.Join(testdataDir, "testfile"),
+			filepath.Join(testdataDir, "testfile.sig"),
+			[]string{},
+			true,
+		},
+		{
+			"invalid key",
+			filepath.Join(testdataDir, "testfile.asc"),
+			"",
+			[]string{"0x46181433FBB75451"},
+			true,
+		},
+	}
+
+	for i, tt := range tests {
+		log.Printf("Running test #%d: %s", i, tt.name)
+		valid := VerifyFile(tt.signedFile, tt.signatureFile, tt.keys)
+		if !tt.shouldFail && !valid {
+			t.Fatalf("Failed to verify: %s", tt.name)
+		}
+		if tt.shouldFail && valid {
+			t.Fatalf("Expected to fail: %s", tt.name)
+		}
+	}
+}
diff --git a/testdata/testfile b/testdata/testfile
new file mode 100644
index 0000000..833b514
--- /dev/null
+++ b/testdata/testfile
@@ -0,0 +1 @@
+I need to be verified.
diff --git a/testdata/testfile-invalid.asc b/testdata/testfile-invalid.asc
new file mode 100644
index 0000000..4d50081
--- /dev/null
+++ b/testdata/testfile-invalid.asc
@@ -0,0 +1,20 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+I need to be verified.
+-----BEGIN PGP SIGNATURE-----
+
+kQIzBAEBCgAdFiEEYfHyDZhnHyXKGYh8mTQI0RN7fVEFAlqFpu8ACgkQmTQI0RN7
+fVGj3w/7BCzAkG995rA/7ba371SW/5uifLKxEn/izWzuJsEO40BN0rzV53XsIqew
+TMhudZo2r1lF7L0KkVChCl/E//aGB5srHRmQlogJqjdyw4qCuVmTe/QMadjo67fS
+wSqH40p5KCQeLZ33xF60vbMwf7ZwtSesFnCsQyvhu85+FDpuexGKKKDxSmO4WjHV
+lL5nDZ0vtSghw3yobGWiYBQ/6MqGLkL6yK0LAY50slywTgAb5WtSE2YCTTLeJOEi
+PEWMWbWoRYmN9ijUowo9YP6cKj4Fz0LtbWMBuHDgvO7Zl/qrb57NxRgBM0cCzAnR
+zEwjRjcfK7GGk+NyfAGbeabgJT/ATI/51sB3MBJgbd+FcSt4zMUL2qfwFDtrTqK3
+7NaKOUh7fVnsGeKY/4DSz0+hJy4qR9JuawDCuiS8CJHzp9LKxKmQDhFfpmFYWOOr
+Nqc4PifAc0OQ3n1iJGMZ0I5CSP79hRLu7FTyOEhARAz1VMR9lOmEAT+M7RcbENs6
+U06mI5h5tyKyBt0cUKQSKtYGKydR2+ZGVkkjEpodcU9RpRzvBFQMU23XdtVPNnya
+sf3ddNIbkaWkF17oxy7PW4ZFnWbA8wATEnnWi3dPIGhRRdS2qJioXFziW/idSkUB
+AagCicMVQ1XDX/Hg5HrwUBrGBk1JZ3TTwzZ/kpePgry1XSLuGxI=
+=vSiP
+-----END PGP SIGNATURE-----
diff --git a/testdata/testfile.asc b/testdata/testfile.asc
new file mode 100644
index 0000000..c26d48d
--- /dev/null
+++ b/testdata/testfile.asc
@@ -0,0 +1,20 @@
+-----BEGIN PGP SIGNED MESSAGE-----
+Hash: SHA512
+
+I need to be verified.
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEYfHyDZhnHyXKGYh8mTQI0RN7fVEFAlqFpu8ACgkQmTQI0RN7
+fVGj3w/7BCzAkG995rA/7ba371SW/5uifLKxEn/izWzuJsEO40BN0rzV53XsIqew
+TMhudZo2r1lF7L0KkVChCl/E//aGB5srHRmQlogJqjdyw4qCuVmTe/QMadjo67fS
+wSqH40p5KCQeLZ33xF60vbMwf7ZwtSesFnCsQyvhu85+FDpuexGKKKDxSmO4WjHV
+lL5nDZ0vtSghw3yobGWiYBQ/6MqGLkL6yK0LAY50slywTgAb5WtSE2YCTTLeJOEi
+PEWMWbWoRYmN9ijUowo9YP6cKj4Fz0LtbWMBuHDgvO7Zl/qrb57NxRgBM0cCzAnR
+zEwjRjcfK7GGk+NyfAGbeabgJT/ATI/51sB3MBJgbd+FcSt4zMUL2qfwFDtrTqK3
+7NaKOUh7fVnsGeKY/4DSz0+hJy4qR9JuawDCuiS8CJHzp9LKxKmQDhFfpmFYWOOr
+Nqc4PifAc0OQ3n1iJGMZ0I5CSP79hRLu7FTyOEhARAz1VMR9lOmEAT+M7RcbENs6
+U06mI5h5tyKyBt0cUKQSKtYGKydR2+ZGVkkjEpodcU9RpRzvBFQMU23XdtVPNnya
+sf3ddNIbkaWkF17oxy7PW4ZFnWbA8wATEnnWi3dPIGhRRdS2qJioXFziW/idSkUB
+AagCicMVQ1XDX/Hg5HrwUBrGBk1JZ3TTwzZ/kpePgry1XSLuGxI=
+=vSiP
+-----END PGP SIGNATURE-----
diff --git a/testdata/testfile.gpg b/testdata/testfile.gpg
new file mode 100644
index 0000000000000000000000000000000000000000..9a403046452838801db20450756700fbf5460693
GIT binary patch
literal 627
zcmV-(0*w8m0Re0Rll_nl0{{vEnKTH|6MKD80jwor2y|t0bY^L6Wm<)zs7WAhWo2X_
zbZ;PHWgvECa%pC1Wn?Z2i2^eO009aB9Tp)3Ve#?}m}eg)${C1!nKTH|6MKD81p-=y
zqNo502 at shy2+<RJeNlmd5B at X9v?W9eGY8sgh65yIjtEoIXU$G>KO7Ao)5A#<I{+d1
z=m}umHiGm!&!mf_Z&f)5Lj#OS&OKC1_|>nzWCVs=#`u5B^@IvrKm;@hDEGUAgC$!C
z at b=T#pUMYzRQ281#SK~6xUHGh#(|Cw5l$;tN$zLnjHuTWzE*Rz9=neyN#rn{o1aSG
zbE6J&SC8??9awI%<a|vHmlL0|es*t3VMCuo1=QF_oLJhK_hs+EjMl9RhQe+-|HjZ&
zlQDWxhw5gBL8_$ZheT|y^z*FitIXBM!mK+&Pcmz3 at 77{%X9X8SS}CS!Pk-G{8TE<~
zs>9&<+nG`xDcbdG-<A=KUXTlEV~1?4VP=bL at -_m8v<Lwv at R4h<bs8l*0S~qnYE>=n
zvNPN*2H0$1ux)-V95fU4Ar1feLQx|6r)>t_O#YNAK4dbn6YQf#!3(ewC;ZV%mZ><O
zz=cX^Qv5{X=}i1E7?~ehiBru&q(O(s_K(&{=I5hl=r+h`sUvk19rIDju>k`w{}T}m
zFf05LKSjmntpO)_dTkyI`4h~NnKsDabvAI`UXe8DFKU at VZ79>`&NqAd;-&;ov at Xx2
z>sGZyM>)fSG0v8o3a=>R-W5o6rR at C@;jtCs7tP+aq8+y}85pw$fLyLAO`MdnzKw2w
NT&7-O0$DG{%h2^B9TETl

literal 0
HcmV?d00001

diff --git a/testdata/testfile.sig b/testdata/testfile.sig
new file mode 100644
index 0000000000000000000000000000000000000000..eec00698179e16641d70a2c1c8eba383affb398b
GIT binary patch
literal 566
zcmV-60?GY}0y6{v0SW*e79j*-@$wCrXCEcX8HjwDGzifXdwo#_0$PQzeE<px5ScUx
z(Gz=pQ4;wO0FWp*KTz8UX)>1VR-!q57Iq`-e)5f7$C=il+8WJrc<ydH#z02s0cn at H
z?wJ0f^f$h1bxC_zisOK;S?~?RpIXfTo{4Gy2qA~JkzEEWSj)$f at fy}<^t+~icRFBD
zOwHi(Re+ya-mP3=mE8GuLI5QjAC@}WBUBJS4j~uB!S!jsBcn6tZJ*%gxmAimYv1|B
zpcfT=B!TgZMJr|Z-$YQ6-xw6wR?#APxdX at iru`il5?`n2tPq!IFbGwK$r^H1#}=X*
zvjf9Ri<LGa0#7$l3)P8Odf$+}CylJJ4bCn?8TB8qzzIkOc7RtcRJTTqtH4(t$<<RG
z#J9-3^$E~N=n^k3{7xfOO1#2k)|jjDRy?j7L-#={K?j8i*0+Q$YEDxu1q<fr8q`j~
z;WZbVSD{zp2Q at wR=Y`?v&|ERheMYoHU;9xx_D3L%;6f|IY;7x at Dm+!sQ2lzxrBa5E
z5aceYX8Fw4r?$@NiwO!?<$z7$kY;2TCbBV;9+Fy8gz8SGPSf+>da%0)&?{<jdbqm8
z-Ng(Ua(u>SN9!@HOw9M-j17WvkFwQJXtWqXXg;dfA?u~-F at GKLIS|)M)>~6OAhTxa
z#H(~)%zD^%(>0$^uD~k5 at WC02xkxe4(~)RM2vHphF~BeJ*@n1lvyD^OdrWyNH&v}1
E2D20jvH$=8

literal 0
HcmV?d00001


From 83df26aa162b7461e4a6c38f8165182131a69e88 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 14 Feb 2018 17:56:44 +0100
Subject: [PATCH 2/5] sources: Verify Ubuntu

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 sources/ubuntu-http.go | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/sources/ubuntu-http.go b/sources/ubuntu-http.go
index 479882b..1a01cf4 100644
--- a/sources/ubuntu-http.go
+++ b/sources/ubuntu-http.go
@@ -26,6 +26,8 @@ func NewUbuntuHTTP() *UbuntuHTTP {
 
 // Run downloads the tarball and unpacks it.
 func (s *UbuntuHTTP) Run(URL, release, variant, arch, cacheDir string) error {
+	baseURL := fmt.Sprintf("%s/releases/%s/release/", URL, release)
+
 	if strings.ContainsAny(release, "0123456789") {
 		s.fname = fmt.Sprintf("ubuntu-base-%s-base-%s.tar.gz", release, arch)
 	} else {
@@ -36,9 +38,20 @@ func (s *UbuntuHTTP) Run(URL, release, variant, arch, cacheDir string) error {
 		}
 	}
 
+	shared.Download(baseURL+"SHA256SUMS.gpg", "")
+	shared.Download(baseURL+"SHA256SUMS", "")
+
+	valid := shared.VerifyFile(
+		filepath.Join(os.TempDir(), "SHA256SUMS"),
+		filepath.Join(os.TempDir(), "SHA256SUMS.gpg"),
+		[]string{"0x46181433FBB75451", "0xD94AA3F0EFE21092"})
+	if !valid {
+		return fmt.Errorf("Failed to validate tarball")
+	}
+
 	err := shared.Download(
-		URL+path.Join("/", "releases", release, "release", s.fname),
-		URL+path.Join("/", "releases", release, "release", "SHA256SUMS"))
+		baseURL+s.fname,
+		baseURL+"SHA256SUMS")
 	if err != nil {
 		return fmt.Errorf("Error downloading Ubuntu image: %s", err)
 	}

From 9bbd4438c0c8515e6ddb935f303201cb3e2aef23 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 14 Feb 2018 18:25:39 +0100
Subject: [PATCH 3/5] sources: Verify Alpine Linux

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 sources/alpine-http.go | 24 +++++++++++++++---------
 1 file changed, 15 insertions(+), 9 deletions(-)

diff --git a/sources/alpine-http.go b/sources/alpine-http.go
index f29c652..3f52ca2 100644
--- a/sources/alpine-http.go
+++ b/sources/alpine-http.go
@@ -1,9 +1,9 @@
 package sources
 
 import (
+	"errors"
 	"fmt"
 	"os"
-	"path"
 	"path/filepath"
 	"strings"
 
@@ -11,7 +11,7 @@ import (
 	lxd "github.com/lxc/lxd/shared"
 )
 
-// AlpineLinuxHTTP represents the debootstrap downloader.
+// AlpineLinuxHTTP represents the Alpine Linux downloader.
 type AlpineLinuxHTTP struct{}
 
 // NewAlpineLinuxHTTP creates a new AlpineLinuxHTTP instance.
@@ -19,20 +19,26 @@ func NewAlpineLinuxHTTP() *AlpineLinuxHTTP {
 	return &AlpineLinuxHTTP{}
 }
 
-// Run runs debootstrap.
+// Run downloads an Alpine Linux mini root filesystem.
 func (s *AlpineLinuxHTTP) Run(URL, release, variant, arch, cacheDir string) error {
 	fname := fmt.Sprintf("alpine-minirootfs-%s-%s.tar.gz", release, arch)
+	tarball := fmt.Sprintf("%s/v%s/releases/%s/%s", URL,
+		strings.Join(strings.Split(release, ".")[0:2], "."), arch, fname)
 
-	// Download
-	parts := strings.Split("3.7.0", ".")
-	strings.Join(parts[0:2], ".")
-	err := shared.Download(URL+path.Join("/",
-		fmt.Sprintf("v%s", strings.Join(strings.Split(release, ".")[0:2], ".")),
-		"releases", arch, fname), "")
+	err := shared.Download(tarball, tarball+".sha256")
 	if err != nil {
 		return err
 	}
 
+	shared.Download(tarball+".asc", "")
+	valid := shared.VerifyFile(
+		filepath.Join(os.TempDir(), fname),
+		filepath.Join(os.TempDir(), fname+".asc"),
+		[]string{"0482D84022F52DF1C4E7CD43293ACD0907D9495A"})
+	if !valid {
+		return errors.New("Failed to verify tarball")
+	}
+
 	err = os.MkdirAll(filepath.Join(cacheDir, "rootfs"), 0755)
 	if err != nil {
 		return err

From f95711470029bee114ad6035f3cd86e4db632d59 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Wed, 14 Feb 2018 19:13:26 +0100
Subject: [PATCH 4/5] sources: Verify Arch Linux

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 sources/archlinux-http.go | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/sources/archlinux-http.go b/sources/archlinux-http.go
index 7f18434..847152d 100644
--- a/sources/archlinux-http.go
+++ b/sources/archlinux-http.go
@@ -1,16 +1,16 @@
 package sources
 
 import (
+	"errors"
 	"fmt"
 	"os"
-	"path"
 	"path/filepath"
 
 	"github.com/lxc/distrobuilder/shared"
 	lxd "github.com/lxc/lxd/shared"
 )
 
-// ArchLinuxHTTP represents the debootstrap downloader.
+// ArchLinuxHTTP represents the Arch Linux downloader.
 type ArchLinuxHTTP struct{}
 
 // NewArchLinuxHTTP creates a new ArchLinuxHTTP instance.
@@ -18,16 +18,26 @@ func NewArchLinuxHTTP() *ArchLinuxHTTP {
 	return &ArchLinuxHTTP{}
 }
 
-// Run runs debootstrap.
+// Run downloads an Arch Linux tarball.
 func (s *ArchLinuxHTTP) Run(URL, release, variant, arch, cacheDir string) error {
 	fname := fmt.Sprintf("archlinux-bootstrap-%s-x86_64.tar.gz", release)
+	tarball := fmt.Sprintf("%s/%s/%s", URL, release, fname)
 
-	// Download
-	err := shared.Download(URL+path.Join("/", release, fname), "")
+	err := shared.Download(tarball, "")
 	if err != nil {
 		return err
 	}
 
+	shared.Download(tarball+".sig", "")
+
+	valid := shared.VerifyFile(
+		filepath.Join(os.TempDir(), fname),
+		filepath.Join(os.TempDir(), fname+".sig"),
+		[]string{"4AA4767BBC9C4B1D18AE28B77F2D434B9741E8AC"})
+	if !valid {
+		return errors.New("Failed to verify tarball")
+	}
+
 	err = os.MkdirAll(cacheDir, 0755)
 	if err != nil {
 		return err

From 9321eb3f9af71cdfb55c0997ab25b9ea908d4175 Mon Sep 17 00:00:00 2001
From: Thomas Hipp <thomas.hipp at canonical.com>
Date: Thu, 15 Feb 2018 10:23:44 +0100
Subject: [PATCH 5/5] sources: Verify CentOS

Signed-off-by: Thomas Hipp <thomas.hipp at canonical.com>
---
 sources/centos-http.go | 13 ++++++++++---
 1 file changed, 10 insertions(+), 3 deletions(-)

diff --git a/sources/centos-http.go b/sources/centos-http.go
index 48a42bb..474549c 100644
--- a/sources/centos-http.go
+++ b/sources/centos-http.go
@@ -1,6 +1,7 @@
 package sources
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"net/http"
@@ -28,15 +29,21 @@ func NewCentOSHTTP() *CentOSHTTP {
 // Run downloads the tarball and unpacks it.
 func (s *CentOSHTTP) Run(URL, release, variant, arch, cacheDir string) error {
 	s.cacheDir = cacheDir
+	baseURL := fmt.Sprintf("%s/%s/isos/%s/", URL, strings.Split(release, ".")[0], arch)
 
 	s.fname = getRelease(URL, release, variant, arch)
 	if s.fname == "" {
 		return fmt.Errorf("Couldn't get name of iso")
 	}
 
-	err := shared.Download(
-		URL+path.Join("/", strings.Split(release, ".")[0], "isos", arch, s.fname),
-		URL+path.Join("/", strings.Split(release, ".")[0], "isos", arch, "sha256sum.txt"))
+	shared.Download(baseURL+"sha256sum.txt.asc", "")
+	valid := shared.VerifyFile(filepath.Join(os.TempDir(), "sha256sum.txt.asc"), "",
+		[]string{"24C6A8A7F4A80EB5"})
+	if !valid {
+		return errors.New("Failed to verify tarball")
+	}
+
+	err := shared.Download(baseURL+s.fname, "sha256sum.txt.asc")
 	if err != nil {
 		return fmt.Errorf("Error downloading CentOS image: %s", err)
 	}


More information about the lxc-devel mailing list