// Create a public domain with a CachedGuard. // TODO(cjpatton) create a net.Conn here. defer Close() somehow. Add new // constructor from a net.Conn that doesn't save the domain to disk. // Refactor Request's in ca.go to use already existing connection. func (d *Domain) CreatePublicCachedDomain(network, addr string, ttl int64) (*Domain, error) { newDomain := &Domain{ Config: d.Config, } configDir, configName := path.Split(d.ConfigPath) // '/path/to/', 'file' // Load public key from domain. keyPath := path.Join(configDir, d.Config.DomainInfo.GetPolicyKeysPath()) keys, err := NewOnDiskPBEKeys(Signing, PublicCachedDomainPassword, keyPath, NewX509Name(d.Config.X509Info)) if err != nil { return nil, err } newDomain.Keys = keys // Set up a CachedGuard. newDomain.Guard = NewCachedGuard(newDomain.Keys.VerifyingKey, Datalog /*TODO(cjpatton) hardcoded*/, network, addr, ttl) newDomain.Config.DomainInfo.GuardNetwork = proto.String(network) newDomain.Config.DomainInfo.GuardAddress = proto.String(addr) newDomain.Config.DomainInfo.GuardTtl = proto.Int64(ttl) // Create domain directory ending with ".pub". configDir = strings.TrimRight(configDir, "/") + ".pub" err = util.MkdirAll(configDir, 0777) if err != nil { return nil, err } newDomain.ConfigPath = path.Join(configDir, configName) newDomain.Keys.dir = path.Join(configDir, d.Config.DomainInfo.GetPolicyKeysPath()) // Save public key. Copy certificate from the old to new directory. // TODO(tmroeder) this is a bit hacky, but the best we can do short // of refactoring the NewOnDiskPBEKey() code. In particular, there is // currently no way to *just* save the keys. err = util.MkdirAll(newDomain.Keys.dir, 0777) if err != nil { return nil, err } for name, _ := range d.Keys.X509Paths() { inFile, err := os.Open(d.Keys.X509Path(name)) if err != nil { return nil, err } defer inFile.Close() outFile, err := os.Create(newDomain.Keys.X509Path(name)) if err != nil { return nil, err } defer outFile.Close() _, err = io.Copy(outFile, inFile) if err != nil { return nil, err } } // Save domain. err = newDomain.Save() return newDomain, err }
// CreateDomain initializes a new Domain, writing its configuration files to // a directory. This creates the directory if needed, creates a policy key pair // (encrypted with the given password when stored on disk), and initializes a // default guard of the appropriate type if needed. Any parameters left empty in // cfg will be set to reasonable default values. func CreateDomain(cfg DomainConfig, configPath string, password []byte) (*Domain, error) { cfg.SetDefaults() configDir := path.Dir(configPath) err := util.MkdirAll(configDir, 0777) if err != nil { return nil, err } keypath := path.Join(configDir, cfg.DomainInfo.GetPolicyKeysPath()) // This creates a keyset if it doesn't exist, and it reads the keyset // otherwise. keys, err := NewOnDiskPBEKeys(Signing, password, keypath, NewX509Name(cfg.X509Info)) if err != nil { return nil, err } var guard Guard switch cfg.DomainInfo.GetGuardType() { case "ACLs": if cfg.AclGuardInfo == nil { return nil, fmt.Errorf("must supply ACL info for the ACL guard") } aclsPath := cfg.AclGuardInfo.GetSignedAclsPath() agi := ACLGuardDetails{ SignedAclsPath: proto.String(path.Join(configDir, aclsPath)), } guard = NewSignedACLGuard(keys.VerifyingKey, agi) case "Datalog": if cfg.DatalogGuardInfo == nil { return nil, fmt.Errorf("must supply Datalog info for the Datalog guard") } rulesPath := cfg.DatalogGuardInfo.GetSignedRulesPath() dgi := DatalogGuardDetails{ SignedRulesPath: proto.String(path.Join(configDir, rulesPath)), } guard, err = NewDatalogGuardFromConfig(keys.VerifyingKey, dgi) if err != nil { return nil, err } case "AllowAll": guard = LiberalGuard case "DenyAll": guard = ConservativeGuard default: return nil, newError("unrecognized guard type: %s", cfg.DomainInfo.GetGuardType()) } d := &Domain{cfg, configPath, keys, guard} err = d.Save() if err != nil { return nil, err } return d, nil }
// NewSecret creates and encrypts a new secret value of the given length, or it // reads and decrypts the value and checks that it's the right length. It // creates the file and its parent directories if these directories do not // exist. func (k *Keys) NewSecret(file string, length int) ([]byte, error) { if _, err := os.Stat(file); err != nil { // Create the parent directories and the file. if err := util.MkdirAll(path.Dir(file), 0700); err != nil { return nil, err } secret := make([]byte, length) if _, err := rand.Read(secret); err != nil { return nil, err } enc, err := k.CryptingKey.Encrypt(secret) if err != nil { return nil, err } if err := ioutil.WriteFile(file, enc, 0700); err != nil { return nil, err } return secret, nil } enc, err := ioutil.ReadFile(file) if err != nil { return nil, err } dec, err := k.CryptingKey.Decrypt(enc) if err != nil { return nil, err } if len(dec) != length { ZeroBytes(dec) return nil, newError("The decrypted value had length %d, but it should have had length %d", len(dec), length) } return dec, nil }
func main() { // tao -help ==> show main help // tao ==> show main help // tao [commonopts] cmd [otheropts] ==> some_cmd [commonopts] [otheropts] boolopts := []string{"quiet", "verbose", "help"} valopts := []string{"tao_domain"} flag.VisitAll(func(f *flag.Flag) { type BoolFlag interface { IsBoolFlag() bool } if b, ok := f.Value.(BoolFlag); ok && b.IsBoolFlag() { boolopts = append(boolopts, f.Name) } else { valopts = append(valopts, f.Name) } }) var args []string cmd := "help" for i := 1; i < len(os.Args); i++ { arg := os.Args[i] if arg == "help" || arg == "-?" { args = append(args, "-help") continue } if arg == "--" { args = append(args, os.Args[i:]...) break } if arg == "" || arg[0] != '-' { cmd = arg args = append(args, os.Args[i+1:]...) break } if e := strings.Index(arg, "="); e != -1 { // -name=val name := flagName(arg[0:e]) if !match(name, boolopts...) && !match(name, valopts...) { options.Usage("Unrecognized option: %s", arg) } args = append(args, arg) } else if match(flagName(arg), boolopts...) { // -bool args = append(args, arg) } else if match(flagName(arg), valopts...) { // -name val if i+1 >= len(os.Args) { options.Usage("flag needs an argument: %s", arg) } args = append(args, arg, os.Args[i+1]) i++ } else { options.Usage("Unrecognized option: %s", arg) } } // Add a default --tao_domain // config := os.Getenv("TAO_DOMAIN") // if config != "" { // arg := fmt.Sprintf("--tao_domain='%s'", config) // args = append([]string{arg}, args...) // } // Add a default --log_dir logdir := os.TempDir() + "/tao_log" if err := util.MkdirAll(logdir, 0777); err != nil { options.FailIf(err, "Can't create log directory: %s", logdir) } arg := fmt.Sprintf("--log_dir=%s", logdir) args = append([]string{arg}, args...) // Maybe add --alsologtostderr=true too? switch cmd { case "help": help() case "domain": subcmd(cmd, "tao_admin", args) case "host": subcmd(cmd, "linux_host", args) case "run", "list", "stop", "kill": subcmd(cmd, "tao_launch", args) default: options.Usage("Unrecognized tao command: %s", cmd) } }
// Start starts a QEMU/KVM CoreOS container using the command line. func (kcc *KvmCoreOSHostContainer) startVM() error { // Create a temporary directory for the config drive. td, err := ioutil.TempDir("", "coreos") kcc.Tempdir = td if err != nil { return err } // Create a temporary directory for the linux_host image. Note that the // args were validated in Start before this call. kcc.LHPath = kcc.spec.Args[1] // Expand the host file into the directory. linuxHostFile, err := os.Open(kcc.spec.Path) if err != nil { return err } zipReader, err := gzip.NewReader(linuxHostFile) if err != nil { return err } defer zipReader.Close() unzippedImage, err := ioutil.ReadAll(zipReader) if err != nil { return err } unzippedReader := bytes.NewReader(unzippedImage) tarReader := tar.NewReader(unzippedReader) for { hdr, err := tarReader.Next() if err == io.EOF { break } if err != nil { return err } fi := hdr.FileInfo() outputName := path.Join(kcc.LHPath, hdr.Name) if fi.IsDir() { if err := os.Mkdir(outputName, fi.Mode()); err != nil { return err } } else { outputFile, err := os.OpenFile(outputName, os.O_CREATE|os.O_TRUNC|os.O_RDWR, fi.Mode()) if err != nil { return err } if _, err := io.Copy(outputFile, tarReader); err != nil { outputFile.Close() return err } outputFile.Close() } } latestDir := path.Join(td, "openstack/latest") if err := util.MkdirAll(latestDir, 0700); err != nil { return err } cfg := kcc.Cfg userData := path.Join(latestDir, "user_data") if err := ioutil.WriteFile(userData, []byte(cfg.SSHKeysCfg), 0700); err != nil { return err } // Copy the rules into the mirrored filesystem for use by the Linux host // on CoreOS. if cfg.RulesPath != "" { rules, err := ioutil.ReadFile(cfg.RulesPath) if err != nil { return err } rulesFile := path.Join(kcc.LHPath, path.Base(cfg.RulesPath)) if err := ioutil.WriteFile(rulesFile, []byte(rules), 0700); err != nil { return err } } qemuProg := "qemu-system-x86_64" qemuArgs := []string{"-name", cfg.Name, "-m", strconv.Itoa(cfg.Memory), "-machine", "accel=kvm:tcg", // Networking. "-net", "nic,vlan=0,model=virtio", "-net", "user,vlan=0,hostfwd=tcp::" + kcc.spec.Args[2] + "-:22,hostname=" + cfg.Name, // Tao communications through virtio-serial. With this // configuration, QEMU waits for a server on cfg.SocketPath, // then connects to it. "-chardev", "socket,path=" + cfg.SocketPath + ",id=port0-char", "-device", "virtio-serial", "-device", "virtserialport,id=port1,name=tao,chardev=port0-char", // The CoreOS image to boot from. "-drive", "if=virtio,file=" + cfg.ImageFile, // A Plan9P filesystem for SSH configuration (and our rules). "-fsdev", "local,id=conf,security_model=none,readonly,path=" + td, "-device", "virtio-9p-pci,fsdev=conf,mount_tag=config-2", // Another Plan9P filesystem for the linux_host files. "-fsdev", "local,id=tao,security_model=none,path=" + kcc.LHPath, "-device", "virtio-9p-pci,fsdev=tao,mount_tag=tao", // Machine config. "-cpu", "host", "-smp", "4", "-nographic"} // for now, we add -nographic explicitly. // TODO(tmroeder): append args later. //qemuArgs = append(qemuArgs, kcc.spec.Args...) glog.Info("Launching qemu/coreos") kcc.QCmd = exec.Command(qemuProg, qemuArgs...) // Don't connect QEMU/KVM to any of the current input/output channels, // since we'll connect over SSH. //kcc.QCmd.Stdin = os.Stdin //kcc.QCmd.Stdout = os.Stdout //kcc.QCmd.Stderr = os.Stderr // TODO(kwalsh) set up env, dir, and uid/gid. return kcc.QCmd.Start() }