mirror of
https://github.com/clearlinux/rkt.git
synced 2026-09-06 05:41:50 +00:00
rkt: move local aci handling into fetchImage()
Also introduces ascFile signature file override and wires up per-app --signature overrides. $ rkt run imgs/pauser.aci error opening signature file: open /home/coreos/rkt/imgs/pauser.aci.asc: no such file or directory $ $ rkt --insecure-skip-verify run imgs/pauser.aci ^]^]Container rootfs terminated by signal KILL. $ $ gpg2 --armor --detach-sign imgs/pauser.aci $ rkt run imgs/pauser.aci rkt: signature verified: Vito Caputo (CoreOS stuff) ^]^]Container rootfs terminated by signal KILL. $ Non-local aci handling behavior is unchanged other than local-file --signature overrides apply to remote aci verification as well.
This commit is contained in:
+120
-60
@@ -21,6 +21,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -71,7 +72,7 @@ func runFetch(args []string) (exit int) {
|
||||
ks := getKeystore()
|
||||
|
||||
for _, img := range args {
|
||||
hash, err := fetchImage(img, ds, ks, true)
|
||||
hash, err := fetchImage(img, "" /* TODO(vc): wire to --signature */, ds, ks, true)
|
||||
if err != nil {
|
||||
stderr("%v", err)
|
||||
return 1
|
||||
@@ -85,9 +86,39 @@ func runFetch(args []string) (exit int) {
|
||||
|
||||
// fetchImage will take an image as either a URL or a name string and import it
|
||||
// into the store if found. If discover is true meta-discovery is enabled.
|
||||
func fetchImage(img string, ds *cas.Store, ks *keystore.Keystore, discover bool) (string, error) {
|
||||
// If asc is not "", it must exist as a local file and will be used as the
|
||||
// signature file for verification, unless verification is disabled.
|
||||
func fetchImage(img string, asc string, ds *cas.Store, ks *keystore.Keystore, discover bool) (string, error) {
|
||||
var (
|
||||
ascFile *os.File
|
||||
err error
|
||||
)
|
||||
if asc != "" && ks != nil {
|
||||
ascFile, err = os.Open(asc)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to open signature file: %v", err)
|
||||
}
|
||||
defer ascFile.Close()
|
||||
}
|
||||
|
||||
u, err := url.Parse(img)
|
||||
if err == nil && discover && u.Scheme == "" {
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("not a valid image reference (%s)", img)
|
||||
}
|
||||
|
||||
// if img refers to a local file, ensure the scheme is file:// and make the url path absolute
|
||||
_, err = os.Stat(u.Path)
|
||||
if err == nil {
|
||||
u.Path, err = filepath.Abs(u.Path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to get abs path: %v", err)
|
||||
}
|
||||
u.Scheme = "file"
|
||||
} else if !os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("unable to access %q: %v", img, err)
|
||||
}
|
||||
|
||||
if discover && u.Scheme == "" {
|
||||
if app := newDiscoveryApp(img); app != nil {
|
||||
stdout("rkt: searching for app image %s", img)
|
||||
ep, attempts, err := discovery.DiscoverEndpoints(*app, true)
|
||||
@@ -111,45 +142,53 @@ func fetchImage(img string, ds *cas.Store, ks *keystore.Keystore, discover bool)
|
||||
if _, ok := app.Labels["version"]; !ok {
|
||||
latest = true
|
||||
}
|
||||
return fetchImageFromEndpoints(ep, ds, ks, latest)
|
||||
return fetchImageFromEndpoints(ep, ascFile, ds, ks, latest)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("not a valid URL (%s)", img)
|
||||
}
|
||||
|
||||
switch u.Scheme {
|
||||
case "http", "https", "docker":
|
||||
case "http", "https", "docker", "file":
|
||||
default:
|
||||
return "", fmt.Errorf("rkt only supports http, https or docker URLs (%s)", img)
|
||||
}
|
||||
return fetchImageFromURL(u.String(), u.Scheme, ds, ks, false)
|
||||
return fetchImageFromURL(u.String(), u.Scheme, ascFile, ds, ks, false)
|
||||
}
|
||||
|
||||
func fetchImageFromEndpoints(ep *discovery.Endpoints, ds *cas.Store, ks *keystore.Keystore, latest bool) (string, error) {
|
||||
return downloadImage(ep.ACIEndpoints[0].ACI, ep.ACIEndpoints[0].ASC, "", ds, ks, latest)
|
||||
func fetchImageFromEndpoints(ep *discovery.Endpoints, ascFile *os.File, ds *cas.Store, ks *keystore.Keystore, latest bool) (string, error) {
|
||||
return fetchImageFrom(ep.ACIEndpoints[0].ACI, ep.ACIEndpoints[0].ASC, "", ascFile, ds, ks, latest)
|
||||
}
|
||||
|
||||
func fetchImageFromURL(imgurl string, scheme string, ds *cas.Store, ks *keystore.Keystore, latest bool) (string, error) {
|
||||
return downloadImage(imgurl, ascURLFromImgURL(imgurl), scheme, ds, ks, latest)
|
||||
func fetchImageFromURL(imgurl string, scheme string, ascFile *os.File, ds *cas.Store, ks *keystore.Keystore, latest bool) (string, error) {
|
||||
return fetchImageFrom(imgurl, ascURLFromImgURL(imgurl), scheme, ascFile, ds, ks, latest)
|
||||
}
|
||||
|
||||
func downloadImage(aciURL string, ascURL string, scheme string, ds *cas.Store, ks *keystore.Keystore, latest bool) (string, error) {
|
||||
stdout("rkt: fetching image from %s", aciURL)
|
||||
func fetchImageFrom(aciURL string, ascURL string, scheme string, ascFile *os.File, ds *cas.Store, ks *keystore.Keystore, latest bool) (string, error) {
|
||||
if scheme != "file" || globalFlags.Debug {
|
||||
stdout("rkt: fetching image from %s", aciURL)
|
||||
}
|
||||
|
||||
if globalFlags.InsecureSkipVerify {
|
||||
stdout("rkt: warning: signature verification has been disabled")
|
||||
if ks != nil {
|
||||
stdout("rkt: warning: signature verification has been disabled")
|
||||
}
|
||||
} else if scheme == "docker" {
|
||||
return "", fmt.Errorf("signature verification for docker images is not supported (try --insecure-skip-verify)")
|
||||
}
|
||||
var key string
|
||||
rem, ok, err := ds.GetRemote(aciURL)
|
||||
if err != nil {
|
||||
if err == nil {
|
||||
key = rem.BlobKey
|
||||
} else {
|
||||
return "", err
|
||||
}
|
||||
if !ok {
|
||||
entity, aciFile, err := download(aciURL, ascURL, ds, ks)
|
||||
entity, aciFile, err := fetch(aciURL, ascURL, ascFile, ds, ks)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Remove(aciFile.Name())
|
||||
if scheme != "file" {
|
||||
defer os.Remove(aciFile.Name())
|
||||
}
|
||||
|
||||
if entity != nil && !globalFlags.InsecureSkipVerify {
|
||||
fmt.Println("rkt: signature verified: ")
|
||||
@@ -157,26 +196,29 @@ func downloadImage(aciURL string, ascURL string, scheme string, ds *cas.Store, k
|
||||
stdout(" %s", v.Name)
|
||||
}
|
||||
}
|
||||
key, err := ds.WriteACI(aciFile, latest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
rem = cas.NewRemote(aciURL, ascURL)
|
||||
rem.BlobKey = key
|
||||
err = ds.WriteRemote(rem)
|
||||
key, err = ds.WriteACI(aciFile, latest)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if scheme != "file" {
|
||||
rem = cas.NewRemote(aciURL, ascURL)
|
||||
rem.BlobKey = key
|
||||
err = ds.WriteRemote(rem)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
return rem.BlobKey, nil
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// download downloads and verifies the remote ACI from the given aciURL.
|
||||
// If Keystore is nil signature verification will be skipped.
|
||||
// download returns the signer, an *os.File representing the ACI, and an error if any.
|
||||
// err will be nil if the ACI downloads successfully and the ACI is verified.
|
||||
func download(aciURL string, ascURL string, ds *cas.Store, ks *keystore.Keystore) (*openpgp.Entity, *os.File, error) {
|
||||
// fetch opens/downloads and verifies the remote ACI.
|
||||
// If ascFile is not nil, it will be used as the signature file and ascURL will be ignored.
|
||||
// If Keystore is nil signature verification will be skipped, regardless of ascFile.
|
||||
// fetch returns the signer, an *os.File representing the ACI, and an error if any.
|
||||
// err will be nil if the ACI fetches successfully and the ACI is verified.
|
||||
func fetch(aciURL string, ascURL string, ascFile *os.File, ds *cas.Store, ks *keystore.Keystore) (*openpgp.Entity, *os.File, error) {
|
||||
var entity *openpgp.Entity
|
||||
u, err := url.Parse(aciURL)
|
||||
if err != nil {
|
||||
@@ -203,51 +245,69 @@ func download(aciURL string, ascURL string, ds *cas.Store, ks *keystore.Keystore
|
||||
return nil, aciFile, nil
|
||||
}
|
||||
|
||||
var sigTempFile *os.File
|
||||
if ks != nil {
|
||||
stdout("Downloading signature from %v\n", ascURL)
|
||||
sigTempFile, err = ds.TmpFile()
|
||||
if ks != nil && ascFile == nil {
|
||||
u, err := url.Parse(ascURL)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error setting up temporary file: %v", err)
|
||||
return nil, nil, fmt.Errorf("error parsing ASC url: %v", err)
|
||||
}
|
||||
defer sigTempFile.Close()
|
||||
defer os.Remove(sigTempFile.Name())
|
||||
err = downloadSignatureFile(ascURL, sigTempFile)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error downloading the signature file: %v", err)
|
||||
if u.Scheme == "file" {
|
||||
ascFile, err = os.Open(u.Path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error opening signature file: %v", err)
|
||||
}
|
||||
} else {
|
||||
stdout("Downloading signature from %v\n", ascURL)
|
||||
ascFile, err = ds.TmpFile()
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error setting up temporary file: %v", err)
|
||||
}
|
||||
if err = downloadSignatureFile(ascURL, ascFile); err != nil {
|
||||
return nil, nil, fmt.Errorf("error downloading the signature file: %v", err)
|
||||
}
|
||||
defer os.Remove(ascFile.Name())
|
||||
}
|
||||
defer ascFile.Close()
|
||||
}
|
||||
|
||||
acif, err := ds.TmpFile()
|
||||
if err != nil {
|
||||
return nil, acif, fmt.Errorf("error setting up temporary file: %v", err)
|
||||
}
|
||||
err = downloadACI(aciURL, acif)
|
||||
if err != nil {
|
||||
return nil, acif, fmt.Errorf("error downloading ACI: %v", err)
|
||||
var aciFile *os.File
|
||||
if u.Scheme == "file" {
|
||||
aciFile, err = os.Open(u.Path)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("error opening ACI file: %v", err)
|
||||
}
|
||||
} else {
|
||||
aciFile, err = ds.TmpFile()
|
||||
if err != nil {
|
||||
return nil, aciFile, fmt.Errorf("error setting up temporary file: %v", err)
|
||||
}
|
||||
defer os.Remove(aciFile.Name())
|
||||
|
||||
if err = downloadACI(aciURL, aciFile); err != nil {
|
||||
return nil, nil, fmt.Errorf("error downloading ACI: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ks != nil {
|
||||
manifest, err := aci.ManifestFromImage(acif)
|
||||
manifest, err := aci.ManifestFromImage(aciFile)
|
||||
if err != nil {
|
||||
return nil, acif, err
|
||||
return nil, aciFile, err
|
||||
}
|
||||
|
||||
if _, err := acif.Seek(0, 0); err != nil {
|
||||
return nil, acif, fmt.Errorf("error seeking ACI file: %v", err)
|
||||
if _, err := aciFile.Seek(0, 0); err != nil {
|
||||
return nil, aciFile, fmt.Errorf("error seeking ACI file: %v", err)
|
||||
}
|
||||
if _, err := sigTempFile.Seek(0, 0); err != nil {
|
||||
return nil, acif, fmt.Errorf("error seeking signature file: %v", err)
|
||||
if _, err := ascFile.Seek(0, 0); err != nil {
|
||||
return nil, aciFile, fmt.Errorf("error seeking signature file: %v", err)
|
||||
}
|
||||
if entity, err = ks.CheckSignature(manifest.Name.String(), acif, sigTempFile); err != nil {
|
||||
return nil, acif, err
|
||||
if entity, err = ks.CheckSignature(manifest.Name.String(), aciFile, ascFile); err != nil {
|
||||
return nil, aciFile, err
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := acif.Seek(0, 0); err != nil {
|
||||
return nil, acif, err
|
||||
if _, err := aciFile.Seek(0, 0); err != nil {
|
||||
return nil, aciFile, fmt.Errorf("error seeking ACI file: %v", err)
|
||||
}
|
||||
return entity, acif, nil
|
||||
return entity, aciFile, nil
|
||||
}
|
||||
|
||||
type writeSyncer interface {
|
||||
|
||||
+3
-3
@@ -183,9 +183,9 @@ func TestDownloading(t *testing.T) {
|
||||
if tt.hit == true && !ok {
|
||||
t.Fatalf("expected a hit got a miss")
|
||||
}
|
||||
_, aciFile, err := download(tt.ACIURL, tt.SigURL, ds, nil)
|
||||
_, aciFile, err := fetch(tt.ACIURL, tt.SigURL, nil, ds, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("error downloading aci: %v", err)
|
||||
t.Fatalf("error fetching aci: %v", err)
|
||||
}
|
||||
defer os.Remove(aciFile.Name())
|
||||
|
||||
@@ -260,7 +260,7 @@ func TestFetchImage(t *testing.T) {
|
||||
}
|
||||
}))
|
||||
defer ts.Close()
|
||||
_, err = fetchImage(fmt.Sprintf("%s/app.aci", ts.URL), ds, ks, true)
|
||||
_, err = fetchImage(fmt.Sprintf("%s/app.aci", ts.URL), "", ds, ks, true)
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
+4
-21
@@ -18,7 +18,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/coreos/rkt/Godeps/_workspace/src/github.com/appc/spec/schema/types"
|
||||
"github.com/coreos/rkt/cas"
|
||||
@@ -28,7 +27,7 @@ import (
|
||||
// findImages uses findImage to attain a list of image hashes using discovery if necessary
|
||||
func (al *rktApps) findImages(ds *cas.Store, ks *keystore.Keystore) error {
|
||||
for _, app := range al.apps {
|
||||
h, err := findImage(app.image, ds, ks, true)
|
||||
h, err := findImage(app.image, app.asc, ds, ks, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -40,7 +39,7 @@ func (al *rktApps) findImages(ds *cas.Store, ks *keystore.Keystore) error {
|
||||
|
||||
// findImage will recognize a ACI hash and use that, import a local file, use
|
||||
// discovery or download an ACI directly.
|
||||
func findImage(img string, ds *cas.Store, ks *keystore.Keystore, discover bool) (*types.Hash, error) {
|
||||
func findImage(img string, asc string, ds *cas.Store, ks *keystore.Keystore, discover bool) (*types.Hash, error) {
|
||||
// check if it is a valid hash, if so let it pass through
|
||||
h, err := types.NewHash(img)
|
||||
if err == nil {
|
||||
@@ -56,24 +55,8 @@ func findImage(img string, ds *cas.Store, ks *keystore.Keystore, discover bool)
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// import the local file if it exists
|
||||
file, err := os.Open(img)
|
||||
if err == nil {
|
||||
key, err := ds.WriteACI(file, false)
|
||||
file.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("%s: %v", img, err)
|
||||
}
|
||||
h, err := types.NewHash(key)
|
||||
if err != nil {
|
||||
// should never happen
|
||||
panic(err)
|
||||
}
|
||||
return h, nil
|
||||
}
|
||||
|
||||
// try fetching remotely
|
||||
key, err := fetchImage(img, ds, ks, discover)
|
||||
// try fetching the image, potentially remotely
|
||||
key, err := fetchImage(img, asc, ds, ks, discover)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+2
-3
@@ -91,14 +91,13 @@ func runPrepare(args []string) (exit int) {
|
||||
return 1
|
||||
}
|
||||
|
||||
s1img, err := findImage(flagStage1Image, ds, nil, false)
|
||||
s1img, err := findImage(flagStage1Image, "", ds, nil, false)
|
||||
if err != nil {
|
||||
stderr("prepare: finding stage1 image %q: %v", flagStage1Image, err)
|
||||
return 1
|
||||
}
|
||||
|
||||
ks := getKeystore()
|
||||
if err := Apps.findImages(ds, ks); err != nil {
|
||||
if err := Apps.findImages(ds, getKeystore()); err != nil {
|
||||
stderr("%v", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
+2
-3
@@ -114,14 +114,13 @@ func runRun(args []string) (exit int) {
|
||||
return 1
|
||||
}
|
||||
|
||||
s1img, err := findImage(flagStage1Image, ds, nil, false)
|
||||
s1img, err := findImage(flagStage1Image, "", ds, nil, false)
|
||||
if err != nil {
|
||||
stderr("Error finding stage1 image %q: %v", flagStage1Image, err)
|
||||
return 1
|
||||
}
|
||||
|
||||
ks := getKeystore()
|
||||
if err := Apps.findImages(ds, ks); err != nil {
|
||||
if err := Apps.findImages(ds, getKeystore()); err != nil {
|
||||
stderr("%v", err)
|
||||
return 1
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user