Example #1
0
// Save serializes, seals, and writes a key set to disk. It calls t.Seal().
func (k *Keys) Save(t Tao) error {
	// Marshal key set.
	cks, err := MarshalKeyset(k)
	if err != nil {
		return err
	}
	cks.Delegation = k.Delegation

	// TODO(tmroeder): defer zeroKeyset(cks)

	m, err := proto.Marshal(cks)
	if err != nil {
		return err
	}
	defer ZeroBytes(m)

	data, err := t.Seal(m, k.policy)
	if err != nil {
		return err
	}

	if err = util.WritePath(k.SealedKeysetPath(), data, 0700, 0600); err != nil {
		return err
	}

	return nil
}
Example #2
0
// NewSignedOnDiskPBEKeys creates the same type of keys as NewOnDiskPBEKeys but
// signs a certificate for the signer with the provided Keys, which must have
// both a SigningKey and a Certificate.
func NewSignedOnDiskPBEKeys(keyTypes KeyType, password []byte, path string, name *pkix.Name, serial int, signer *Keys) (*Keys, error) {
	if signer == nil || name == nil {
		return nil, newError("must supply a signer and a name")
	}

	if signer.Cert == nil || signer.SigningKey == nil {
		return nil, newError("the signing key must have a SigningKey and a Cert")
	}

	if keyTypes & ^Signing != 0 {
		return nil, newError("can't sign a key that has no signer")
	}

	k, err := NewOnDiskPBEKeys(keyTypes, password, path, nil)
	if err != nil {
		return nil, err
	}

	// If there's already a cert, then this means that there was already a
	// keyset on disk, so don't create a new signed certificate.
	if k.Cert == nil {
		k.Cert, err = signer.SigningKey.CreateSignedX509(signer.Cert, serial, k.VerifyingKey, name)
		if err != nil {
			return nil, err
		}

		if err = util.WritePath(k.X509Path(), k.Cert.Raw, 0777, 0666); err != nil {
			return nil, err
		}
	}

	return k, nil
}
Example #3
0
func (k *Keys) SaveCerts() error {
	for name, cert := range k.Cert {
		if err := util.WritePath(k.X509Path(name), cert.Raw, 0777, 0666); err != nil {
			return err
		}
	}
	return nil
}
Example #4
0
func (k *Keys) newCert(name *pkix.Name) (err error) {
	k.Cert, err = k.SigningKey.CreateSelfSignedX509(name)
	if err != nil {
		return err
	}
	if err = util.WritePath(k.X509Path(), k.Cert.Raw, 0777, 0666); err != nil {
		return err
	}
	return nil
}
Example #5
0
// Save writes all persistent policy data to disk, signed by key.
func (g *DatalogGuard) Save(signer *Signer) error {
	sdb, err := g.GetSignedDatalogRules(signer)
	if err != nil {
		return err
	}
	serialized, err := proto.Marshal(sdb)
	if err != nil {
		return err
	}
	if err := util.WritePath(g.Config.GetSignedRulesPath(), serialized, 0777, 0666); err != nil {
		return err
	}
	return nil
}
Example #6
0
func publish(doc []byte) (url string, err error) {
	docurl := *options.String["docurl"]
	docdir := *options.String["docdir"]

	h := sha256.Sum256(doc)
	p := path.Join(docdir, fmt.Sprintf("%x.txt", h))
	err = util.WritePath(p, doc, 0777, 0666)
	if err != nil {
		return
	}

	if !strings.HasSuffix(docurl, "/") {
		docurl += "/"
	}
	url = fmt.Sprintf("%s%x.txt", docurl, h)
	return
}
Example #7
0
// SaveKeyset serializes and saves a Keys object to disk in plaintext.
func SaveKeyset(k *Keys, dir string) error {
	k.dir = dir
	cks, err := MarshalKeyset(k)
	if err != nil {
		return err
	}
	cks.Delegation = k.Delegation

	m, err := proto.Marshal(cks)
	if err != nil {
		return err
	}

	if err = util.WritePath(k.PlaintextKeysetPath(), m, 0700, 0600); err != nil {
		return err
	}

	return nil
}
Example #8
0
// Publish signs and caches a manifest about a child for later use by Derive.
func (m Manifest) Publish(h Host, childSubprin auth.SubPrin) error {
	self := h.HostName()
	child := self.MakeSubprincipal(childSubprin)
	stmt := auth.Says{
		Speaker: &self,
		Message: m.Formula(&child),
	}
	a, err := h.Say(stmt)
	if err != nil {
		return err
	}
	buf, err := proto.Marshal(a)
	if err != nil {
		return err
	}
	hash := fmt.Sprintf("%02x", sha256.Sum256([]byte(child.String())))
	b := make([]byte, 10)
	rand.Read(b) // ignore errors
	f := path.Join(Directory, fmt.Sprintf("%s/%02x", hash, b))
	return util.WritePath(f, buf, 0755, 0644)
}
Example #9
0
// NewOnDiskPBEKeys creates a new Keys structure with the specified key types
// store under PBE on disk. If keys are generated and name is not nil, then a
// self-signed x509 certificate will be generated and saved as well.
func NewOnDiskPBEKeys(keyTypes KeyType, password []byte, path string, name *pkix.Name) (*Keys, error) {
	if keyTypes == 0 || (keyTypes & ^Signing & ^Crypting & ^Deriving != 0) {
		return nil, newError("bad key type")
	}

	if path == "" {
		return nil, newError("bad init call: no path for keys")
	}

	k := &Keys{
		keyTypes: keyTypes,
		dir:      path,
	}

	if len(password) == 0 {
		// This means there's no secret information: just load a public
		// verifying key.
		if k.keyTypes & ^Signing != 0 {
			return nil, newError("without a password, only a verifying key can be loaded")
		}

		err := k.loadCert()
		if err != nil {
			return nil, err
		}
		if k.Cert == nil {
			return nil, newError("no password and can't load cert: %s", k.X509Path())
		}

		if k.VerifyingKey, err = FromX509(k.Cert); err != nil {
			return nil, err
		}
	} else {
		// There are two different types of keysets: in one there's
		// just a Signer, so we use an encrypted PEM format. In the
		// other, there are multiple keys, so we use a custom protobuf
		// format.
		if k.keyTypes & ^Signing != 0 {
			// Check to see if there are already keys.
			f, err := os.Open(k.PBEKeysetPath())
			if err == nil {
				defer f.Close()
				ks, err := ioutil.ReadAll(f)
				if err != nil {
					return nil, err
				}

				data, err := PBEDecrypt(ks, password)
				if err != nil {
					return nil, err
				}
				defer ZeroBytes(data)

				var cks CryptoKeyset
				if err = proto.Unmarshal(data, &cks); err != nil {
					return nil, err
				}

				// TODO(tmroeder): defer zeroKeyset(&cks)

				ktemp, err := UnmarshalKeyset(&cks)
				if err != nil {
					return nil, err
				}

				// Note that this loads the certificate if it's
				// present, and it returns nil otherwise.
				err = k.loadCert()
				if err != nil {
					return nil, err
				}

				k.SigningKey = ktemp.SigningKey
				k.VerifyingKey = ktemp.VerifyingKey
				k.CryptingKey = ktemp.CryptingKey
				k.DerivingKey = ktemp.DerivingKey
			} else {
				// Create and store a new set of keys.
				k, err = NewTemporaryKeys(keyTypes)
				if err != nil {
					return nil, err
				}

				k.dir = path

				cks, err := MarshalKeyset(k)
				if err != nil {
					return nil, err
				}

				// TODO(tmroeder): defer zeroKeyset(cks)

				m, err := proto.Marshal(cks)
				if err != nil {
					return nil, err
				}
				defer ZeroBytes(m)

				enc, err := PBEEncrypt(m, password)
				if err != nil {
					return nil, err
				}

				if err = util.WritePath(k.PBEKeysetPath(), enc, 0777, 0600); err != nil {
					return nil, err
				}

				if k.SigningKey != nil && name != nil {
					err = k.newCert(name)
					if err != nil {
						return nil, err
					}
				}
			}
		} else {
			// There's just a signer, so do PEM encryption of the encoded key.
			f, err := os.Open(k.PBESignerPath())
			if err == nil {
				defer f.Close()
				// Read the signer.
				ss, err := ioutil.ReadAll(f)
				if err != nil {
					return nil, err
				}

				pb, rest := pem.Decode(ss)
				if pb == nil || len(rest) > 0 {
					return nil, newError("decoding failure")
				}

				p, err := x509.DecryptPEMBlock(pb, password)
				if err != nil {
					return nil, err
				}
				defer ZeroBytes(p)

				err = k.loadCert()
				if err != nil {
					return nil, err
				}

				if k.SigningKey, err = UnmarshalSignerDER(p); err != nil {
					return nil, err
				}
				k.VerifyingKey = k.SigningKey.GetVerifier()
			} else {
				// Create a fresh key and store it to the PBESignerPath.
				if k.SigningKey, err = GenerateSigner(); err != nil {
					return nil, err
				}

				k.VerifyingKey = k.SigningKey.GetVerifier()
				p, err := MarshalSignerDER(k.SigningKey)
				if err != nil {
					return nil, err
				}
				defer ZeroBytes(p)

				pb, err := x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", p, password, x509.PEMCipherAES128)
				if err != nil {
					return nil, err
				}

				pbes, err := util.CreatePath(k.PBESignerPath(), 0777, 0600)
				if err != nil {
					return nil, err
				}
				defer pbes.Close()

				if err = pem.Encode(pbes, pb); err != nil {
					return nil, err
				}

				if k.SigningKey != nil && name != nil {
					err = k.newCert(name)
					if err != nil {
						return nil, err
					}
				}
			}
		}
	}

	return k, nil
}
Example #10
0
func main() {
	verbose.Set(true)
	options.Parse()

	profiling.ProfilePath = *options.String["profile"]

	if !verbose.Enabled {
		taoca.ConfirmNames = false
	}

	if *options.String["config"] != "" && !*options.Bool["init"] {
		err := options.Load(*options.String["config"])
		options.FailIf(err, "Can't load configuration")
	}

	fmt.Println("https/tls Certificate Authority")

	manualMode = *options.Bool["manual"]
	learnMode = *options.Bool["learn"]

	if !manualMode && tao.Parent() == nil {
		options.Fail(nil, "can't continue: automatic mode, but no host Tao available")
	}

	if *options.Bool["root"] == (*options.String["subsidiary"] != "") {
		options.Usage("must supply exactly one of -root or -subsidiary options")
	}

	host := *options.String["host"]
	port := *options.String["port"]
	addr := net.JoinHostPort(host, port)

	// TODO(kwalsh) extend tao name with operating mode and policy

	cpath := *options.String["config"]
	kdir := *options.String["keys"]
	if kdir == "" && cpath != "" {
		kdir = path.Dir(cpath)
	} else if kdir == "" {
		options.Fail(nil, "Option -keys or -config is required")
	}
	ppath := path.Join(kdir, "policy")

	var err error

	if *options.Bool["init"] {
		if cpath != "" {
			err := options.Save(cpath, "HTTPS/TLS certificate authority configuration", "persistent")
			options.FailIf(err, "Can't save configuration")
		}
		fmt.Println("" +
			"Initializing fresh HTTP/TLS CA signing key. Provide the following information,\n" +
			"to be include in the CA's own x509 certificate. Leave the response blank to\n" +
			"accept the default value.\n" +
			"\n" +
			"Configuration file: " + cpath + "\n" +
			"Keys directory: " + kdir + "\n")

		var caName *pkix.Name
		if taoca.ConfirmNames {
			if *options.Bool["root"] {
				caName = taoca.ConfirmName(caRootName)
			} else {
				caName = taoca.ConfirmName(caSubsidiaryName)
			}
		} else {
			if *options.Bool["root"] {
				caName = caRootName
			} else {
				caName = caSubsidiaryName
			}
		}

		if manualMode {
			pwd := options.Password("Choose an HTTPS/TLS CA signing key password", "pass")
			caKeys, err = tao.InitOnDiskPBEKeys(tao.Signing, pwd, kdir, caName)
			tao.ZeroBytes(pwd)
		} else {
			caKeys, err = tao.InitOnDiskTaoSealedKeys(tao.Signing, caName, tao.Parent(), kdir, tao.SealPolicyDefault)
		}
		options.FailIf(err, "Can't initialize fresh HTTPS/TLS CA signing key")
		if *options.Bool["root"] {
			fmt.Printf(""+
				"Note: To install this CA's key in the Chrome browser, go to\n"+
				"  'Settings', 'Show advanced settings...', 'Manage Certificates...', 'Authorities'\n"+
				"  then import the following file:\n"+
				"     %s\n"+
				"  Select 'Trust this certificate for identifying websites' and/or other\n"+
				"  options, then click 'OK'\n", caKeys.X509Path("default"))
		} else {
			csr := taoca.NewCertificateSigningRequest(caKeys.VerifyingKey, caName)
			*csr.IsCa = true
			srv := *options.String["subsidiary"]
			taoca.DefaultServerName = srv
			taoca.SubmitAndInstall(caKeys, csr)
		}

		if !manualMode {
			f, err := os.Open(ppath)
			if err == nil {
				f.Close()
				fmt.Printf("Using existing certificate-granting policy: %s\n", ppath)
			} else {
				fmt.Printf("Creating default certificate-granting policy: %s\n", ppath)
				fmt.Printf("Edit that file to define the certificate-granting policy.\n")
				err := util.WritePath(ppath, []byte(policy.Default), 0755, 0755)
				options.FailIf(err, "Can't save policy rules")
			}
		}
	} else {
		if manualMode {
			pwd := options.Password("HTTPS/TLS CA signing key password", "pass")
			caKeys, err = tao.LoadOnDiskPBEKeys(tao.Signing, pwd, kdir)
			tao.ZeroBytes(pwd)
		} else {
			caKeys, err = tao.LoadOnDiskTaoSealedKeys(tao.Signing, tao.Parent(), kdir, tao.SealPolicyDefault)
		}
		options.FailIf(err, "Can't load HTTP/TLS CA signing key")
	}

	netlog.Log("https_ca: start")
	netlog.Log("https_ca: manual? %v", manualMode)

	if !manualMode {
		guard, err = policy.Load(ppath)
		options.FailIf(err, "Can't load certificate-granting policy")
	}

	var prin auth.Prin
	if tao.Parent() != nil {
		prin, err = tao.Parent().GetTaoName()
		options.FailIf(err, "Can't get tao name")
	} else {
		rendezvous.DefaultServer.Connect(caKeys)
		prin = caKeys.SigningKey.ToPrincipal()
	}

	name := *options.String["name"]
	if name != "" {
		err = rendezvous.Register(rendezvous.Binding{
			Name:      proto.String(name),
			Host:      proto.String(host),
			Port:      proto.String(port),
			Protocol:  proto.String("protoc/rpc/https_ca"),
			Principal: proto.String(prin.String()),
		})
		options.FailIf(err, "Can't register with rendezvous service")
	}

	statsdelay := *options.String["stats"]
	var srv *tao.Server
	if statsdelay != "" {
		go profiling.ShowStats(&stats, statsdelay, "sign certificates")
		srv = tao.NewOpenServer(tao.ConnHandlerFunc(doResponseWithStats))
	} else {
		srv = tao.NewOpenServer(tao.ConnHandlerFunc(doResponseWithoutStats))
	}

	srv.Keys = caKeys
	fmt.Printf("Listening at %s using Tao-authenticated channels\n", addr)
	err = srv.ListenAndServe(addr)
	options.FailIf(err, "server died")

	fmt.Println("Server Done")
	netlog.Log("https_ca: done")
}
Example #11
0
// InitOnDiskPBEKeys creates a new Keys structure with the specified key types
// stored under PBE on disk. If name is not nil, then a self-signed x509
// certificate will be generated and saved as well.
func InitOnDiskPBEKeys(keyTypes KeyType, password []byte, path string, name *pkix.Name) (*Keys, error) {
	if keyTypes == 0 || (keyTypes & ^Signing & ^Crypting & ^Deriving != 0) {
		return nil, newError("bad key type")
	}

	if path == "" {
		return nil, newError("bad init call: no path for keys")
	}

	if len(password) == 0 {
		return nil, newError("password may not be empty")
	}

	var k *Keys
	var err error

	if keyTypes & ^Signing != 0 {
		// There are are multiple keys, so use a custom protobuf format.
		k, err = NewTemporaryNamedKeys(keyTypes, name)
		if err != nil {
			return nil, err
		}
		k.dir = path

		cks, err := MarshalKeyset(k)
		if err != nil {
			return nil, err
		}

		// TODO(tmroeder): defer zeroKeyset(cks)

		m, err := proto.Marshal(cks)
		if err != nil {
			return nil, err
		}
		defer ZeroBytes(m)

		enc, err := PBEEncrypt(m, password)
		if err != nil {
			return nil, err
		}

		if err = util.WritePath(k.PBEKeysetPath(), enc, 0777, 0600); err != nil {
			return nil, err
		}
	} else {
		k = &Keys{
			keyTypes:        keyTypes,
			dir:             path,
			CertificatePool: NewCertificatePool(),
		}
		// Just a signer, so create fresh key and store it to PBESignerPath.
		if k.SigningKey, err = GenerateSigner(); err != nil {
			return nil, err
		}

		k.VerifyingKey = k.SigningKey.GetVerifier()
		p, err := MarshalSignerDER(k.SigningKey)
		if err != nil {
			return nil, err
		}
		defer ZeroBytes(p)

		pb, err := x509.EncryptPEMBlock(rand.Reader, "EC PRIVATE KEY", p, password, x509.PEMCipherAES128)
		if err != nil {
			return nil, err
		}

		pbes, err := util.CreatePath(k.PBESignerPath(), 0777, 0600)
		if err != nil {
			return nil, err
		}
		defer pbes.Close()

		if err = pem.Encode(pbes, pb); err != nil {
			return nil, err
		}

		if name == nil {
			name = DefaultEphemeralX509Name
		}
		template := k.SigningKey.X509Template(name)
		template.IsCA = true
		cert, err := k.SigningKey.CreateSelfSignedX509(template)
		if err != nil {
			return nil, err
		}
		k.Cert["self"] = cert
		k.Cert["default"] = cert
	}

	if err = k.SaveCerts(); err != nil {
		return nil, err
	}

	return k, nil
}
// Start starts a QEMU/KVM CoreOS container using the command line.
func (kcc *KvmCoreOSContainer) startVM() error {
	// CoreOS gets cloud-config data from openstack/latest/user_data on the
	// config-2 drive. Create a temporary directory for this file. The file
	// format is:
	//
	// #cloud-config
	// ssh_authorized_keys:
	// - ssh-rsa AAAAB3N...
	// - ssh-dss AAAAB3N...
	// hostname: mycontainer
	// coreos:
	//   units:
	//     - name: media-tao.mount
	//       command: start
	//       content: |
	//         [Mount]
	//         What=shared
	//         Where=/media/shared
	//         Type=9p
	//         Options=trans=virtio,version=9p2000.L
	//     - name: docker-redis.service
	//       command: start
	//       content: |
	//         [Service]
	//         ExecStart=bash -c 'echo "$0" "$1" <stdin >stdout 2>stderr' "hello "world"
	//         ExecStopPost=/usr/bin/shutdown -P now
	//
	// Note: The unusual "$0" "$1" ... notation is an attempt to avoid quoting
	// issues for Args.
	// TODO(kwalsh) ExecStopPost does not have the desired effect. Why not? For
	// now, put the shutdown into ExecStart.

	s := "#cloud-config\n"
	s += "hostname: " + kcc.Hostname + "\n"

	s += "ssh_authorized_keys:\n"
	s += "- " + kcc.Factory.PublicKey + "\n"
	for _, k := range kcc.SSHAuthorizedKeys {
		s += "- " + k + "\n"
	}

	s += "coreos:\n"
	s += "  units:\n"

	for i, from := range kcc.MountFrom {
		to := kcc.MountTo[i]
		// TODO(kwalsh) Security check: make sure from is owned by UID.
		s += fmt.Sprintf(""+
			"    - name: %s.mount\n"+
			"      command: start\n"+
			"      content: |\n"+
			"        [Mount]\n"+
			"        What=%s\n"+
			"        Where=%s\n"+
			"        Options=trans=virtio,version=9p2000.L\n",
			systemdEscape(to),
			systemdEscape(from), to)
	}

	s += fmt.Sprintf("" +
		"    - name: tao-perms.service\n" +
		"      command: start\n" +
		"      content: |\n" +
		"        [Service]\n" +
		"        Type=oneshot\n" +
		"        ExecStart=/bin/chmod a+rw /dev/virtio-ports/tao\n" +
		"        ExecStart=/bin/chmod a+rw /dev/virtio-ports/stdio\n")

	if !kcc.SSHCommand {
		// Use bash -c '...' so that we can redirect stdio to our socket
		// TODO(kwalsh) perhaps we can use systemd's "TTYPath=" option instead?
		cmd := kcc.spec.Path
		args := ""
		for i, arg := range kcc.spec.Args {
			cmd += fmt.Sprintf(" \"$%d\"", i)
			args += " " + systemdQuote(arg)
		}
		cmd += " <>/dev/virtio-ports/stdio 1>&0 2>&0"
		ctype := "file"
		cspec := "tao::RPC+tao::FileMessageChannel(/dev/virtio-ports/tao)"
		s += fmt.Sprintf(""+
			"    - name: tao-launch.service\n"+
			"      command: start\n"+
			"      content: |\n"+
			"        [Service]\n"+
			"        Type=oneshot\n"+
			"        Requires=tao-perms.service\n"+
			"        After=tao-perms.service\n"+
			"        Environment='%s=%s' '%s=%s'\n"+
			"        User=core\n"+
			"        ExecStart=/bin/bash -c '%s'%s\n"+
			"        ExecStopPost=/bin/bash -c '/bin/sudo /sbin/shutdown -P now'\n",
			HostChannelTypeEnvVar, ctype,
			HostSpecEnvVar, cspec,
			cmd, args)
		// TODO(kwalsh) shutting down the system in this way is apparently very
		// slow, with a 90 second delay. Not sure why... the last line of the
		// log before the 90 second pause is:
		// [   93.152540] random: nonblocking pool is initialized
	}

	cloudConfig := path.Join(kcc.CoreOSConfigPath, "openstack/latest/user_data")
	if err := util.WritePath(cloudConfig, []byte(s), 0400, 0644); err != nil {
		return err
	}

	userNet := "user,vlan=0"
	if kcc.Hostname != "" {
		userNet += ",hostname=" + kcc.Hostname
	}
	// TODO(kwalsh) remove hack
	if kcc.SSHCommand || true {
		userNet += fmt.Sprintf(",hostfwd=tcp::%d-:22", kcc.SSHForwardingPort)
	}

	// Most all of these options can be overridden by options in ContainerArgs.
	qemuProg := "qemu-system-x86_64"
	qemuArgs := []string{"-name", kcc.VMName,
		// Machine config.
		"-m", strconv.Itoa(kcc.Memory),
		"-machine", "accel=kvm:tcg",
		"-cpu", "host",
		"-smp", "4",
		"-nographic",
		// Networking.
		"-net", "nic,vlan=0,model=virtio",
		"-net", userNet,
		// Tao communications through virtio-serial. With this
		// configuration, QEMU waits for a server on kcc.SocketPath,
		// then connects to it.
		"-device", "virtio-serial",
		"-chardev", "socket,path=" + kcc.SocketPath + ",id=port0-char",
		"-device", "virtserialport,id=port1,name=tao,chardev=port0-char",
		// The CoreOS image to boot from.
		"-drive", "if=virtio,file=" + kcc.PrivateImage,
		// A Plan9P filesystem for cloud-config.
		"-fsdev", "local,id=conf,security_model=none,readonly,path=" + kcc.CoreOSConfigPath,
		"-device", "virtio-9p-pci,fsdev=conf,mount_tag=config-2",
	}
	if !kcc.SSHCommand {
		qemuArgs = append(qemuArgs,
			"-chardev", "socket,path="+kcc.StdioPath+",id=port1-char",
			"-device", "virtserialport,id=port2,name=stdio,chardev=port1-char")
	}
	// TODO(kwalsh) check above virtserialport usage. Do we need two of them to
	// handle stdout,stderr properly?

	for _, from := range kcc.MountFrom {
		tag := systemdEscape(from)
		// Another Plan9P filesystem for the user mount
		// TODO(kwalsh) should this really be security_model=none ?
		qemuArgs = append(qemuArgs,
			"-fsdev", "local,id=tao,security_model=none,path="+from,
			"-device", "virtio-9p-pci,fsdev="+tag+",mount_tag="+tag)
	}
	// TODO(kwalsh) sanity check user-supplied args, many can violate security
	qemuArgs = append(qemuArgs, kcc.spec.ContainerArgs...)

	// Log qemu output to file
	qout, err := os.Create(kcc.LogPath)
	if err != nil {
		return err
	}

	glog.Infof("Launching qemu/coreos prog=%s args=%q", qemuProg, qemuArgs)
	kcc.QCmd = exec.Command(qemuProg, qemuArgs...)
	kcc.QCmd.Stdout = qout
	kcc.QCmd.Stderr = qout

	return kcc.QCmd.Start()
}