// 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 }
// 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 }
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 }
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 }
// 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 }
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 }
// 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 }
// 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) }
// 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 }
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") }
// 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() }