Esempio n. 1
0
func getRemoteCertificate(address string) (*x509.Certificate, error) {
	// Setup a permissive TLS config
	tlsConfig, err := shared.GetTLSConfig("", "", nil)
	if err != nil {
		return nil, err
	}

	tlsConfig.InsecureSkipVerify = true
	tr := &http.Transport{
		TLSClientConfig: tlsConfig,
		Dial:            shared.RFC3493Dialer,
		Proxy:           shared.ProxyFromEnvironment,
	}

	// Connect
	client := &http.Client{Transport: tr}
	resp, err := client.Get(address)
	if err != nil {
		return nil, err
	}

	// Retrieve the certificate
	if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
		return nil, fmt.Errorf("Unable to read remote TLS certificate")
	}

	return resp.TLS.PeerCertificates[0], nil
}
Esempio n. 2
0
func (d *Daemon) httpGetSync(url string, certificate string) (*lxd.Response, error) {
	var err error

	var cert *x509.Certificate
	if certificate != "" {
		certBlock, _ := pem.Decode([]byte(certificate))
		if certBlock == nil {
			return nil, fmt.Errorf("Invalid certificate")
		}

		cert, err = x509.ParseCertificate(certBlock.Bytes)
		if err != nil {
			return nil, err
		}
	}

	tlsConfig, err := shared.GetTLSConfig("", "", "", cert)
	if err != nil {
		return nil, err
	}

	tr := &http.Transport{
		TLSClientConfig:   tlsConfig,
		Dial:              shared.RFC3493Dialer,
		Proxy:             d.proxy,
		DisableKeepAlives: true,
	}

	myhttp := http.Client{
		Transport: tr,
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", shared.UserAgent)

	r, err := myhttp.Do(req)
	if err != nil {
		return nil, err
	}

	resp, err := lxd.ParseResponse(r)
	if err != nil {
		return nil, err
	}

	if resp.Type != lxd.Sync {
		return nil, fmt.Errorf("unexpected non-sync response")
	}

	return resp, nil
}
Esempio n. 3
0
func (d *Daemon) httpGetFile(url string, certificate string) (*http.Response, error) {
	var err error

	var cert *x509.Certificate
	if certificate != "" {
		certBlock, _ := pem.Decode([]byte(certificate))
		if certBlock == nil {
			return nil, fmt.Errorf("Invalid certificate")
		}

		cert, err = x509.ParseCertificate(certBlock.Bytes)
		if err != nil {
			return nil, err
		}
	}

	tlsConfig, err := shared.GetTLSConfig("", "", "", cert)
	if err != nil {
		return nil, err
	}

	tr := &http.Transport{
		TLSClientConfig:   tlsConfig,
		Dial:              shared.RFC3493Dialer,
		Proxy:             d.proxy,
		DisableKeepAlives: true,
	}
	myhttp := http.Client{
		Transport: tr,
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", shared.UserAgent)

	raw, err := myhttp.Do(req)
	if err != nil {
		return nil, err
	}

	if raw.StatusCode != 200 {
		_, err := lxd.HoistResponse(raw, lxd.Error)
		if err != nil {
			return nil, err
		}

		return nil, fmt.Errorf("non-200 status with no error response?")
	}

	return raw, nil
}
Esempio n. 4
0
func (d *Daemon) UpdateHTTPsPort(oldAddress string, newAddress string) error {
	var sockets []net.Listener

	if oldAddress != "" {
		_, _, err := net.SplitHostPort(oldAddress)
		if err != nil {
			oldAddress = fmt.Sprintf("%s:%s", oldAddress, shared.DefaultPort)
		}

		for _, socket := range d.Sockets {
			if socket.Addr().String() == oldAddress {
				socket.Close()
			} else {
				sockets = append(sockets, socket)
			}
		}
	} else {
		sockets = d.Sockets
	}

	if newAddress != "" {
		_, _, err := net.SplitHostPort(newAddress)
		if err != nil {
			newAddress = fmt.Sprintf("%s:%s", newAddress, shared.DefaultPort)
		}

		tlsConfig, err := shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return err
		}

		tcpl, err := tls.Listen("tcp", newAddress, tlsConfig)
		if err != nil {
			return fmt.Errorf("cannot listen on https socket: %v", err)
		}

		d.tomb.Go(func() error { return http.Serve(tcpl, d.mux) })
		sockets = append(sockets, tcpl)
	}

	d.Sockets = sockets
	return nil
}
Esempio n. 5
0
func (d *Daemon) httpGetSync(url string) (*lxd.Response, error) {
	var err error
	if d.tlsconfig == nil {
		d.tlsconfig, err = shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return nil, err
		}
	}
	tr := &http.Transport{
		TLSClientConfig: d.tlsconfig,
		Dial:            shared.RFC3493Dialer,
		Proxy:           http.ProxyFromEnvironment,
	}
	myhttp := http.Client{
		Transport: tr,
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", shared.UserAgent)

	r, err := myhttp.Do(req)
	if err != nil {
		return nil, err
	}

	resp, err := lxd.ParseResponse(r)
	if err != nil {
		return nil, err
	}

	if resp.Type != lxd.Sync {
		return nil, fmt.Errorf("unexpected non-sync response")
	}

	return resp, nil
}
Esempio n. 6
0
func (d *Daemon) httpGetFile(url string) (*http.Response, error) {
	var err error
	if d.tlsconfig == nil {
		d.tlsconfig, err = shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return nil, err
		}
	}
	tr := &http.Transport{
		TLSClientConfig: d.tlsconfig,
		Dial:            shared.RFC3493Dialer,
		Proxy:           http.ProxyFromEnvironment,
	}
	myhttp := http.Client{
		Transport: tr,
	}

	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", shared.UserAgent)

	raw, err := myhttp.Do(req)
	if err != nil {
		return nil, err
	}

	if raw.StatusCode != 200 {
		_, err := lxd.HoistResponse(raw, lxd.Error)
		if err != nil {
			return nil, err
		}

		return nil, fmt.Errorf("non-200 status with no error response?")
	}

	return raw, nil
}
Esempio n. 7
0
File: client.go Progetto: djibi2/lxd
// NewClient returns a new LXD client.
func NewClient(config *Config, remote string) (*Client, error) {
	c := Client{
		Config: *config,
		Http:   http.Client{},
	}

	c.Name = remote

	if remote == "" {
		return nil, fmt.Errorf(i18n.G("A remote name must be provided."))
	}

	if r, ok := config.Remotes[remote]; ok {
		if r.Addr[0:5] == "unix:" {
			if r.Addr == "unix://" {
				r.Addr = fmt.Sprintf("unix:%s", shared.VarPath("unix.socket"))
			}

			c.BaseURL = "http://unix.socket"
			c.BaseWSURL = "ws://unix.socket"
			c.Transport = "unix"
			uDial := func(networ, addr string) (net.Conn, error) {
				var err error
				var raddr *net.UnixAddr
				if r.Addr[7:] == "unix://" {
					raddr, err = net.ResolveUnixAddr("unix", r.Addr[7:])
				} else {
					raddr, err = net.ResolveUnixAddr("unix", r.Addr[5:])
				}
				if err != nil {
					return nil, err
				}
				return net.DialUnix("unix", nil, raddr)
			}
			c.Http.Transport = &http.Transport{Dial: uDial}
			c.websocketDialer.NetDial = uDial
			c.Remote = &r
		} else {
			certf, keyf, err := readMyCert(config.ConfigDir)
			if err != nil {
				return nil, err
			}

			tlsconfig, err := shared.GetTLSConfig(certf, keyf)
			if err != nil {
				return nil, err
			}

			tr := &http.Transport{
				TLSClientConfig: tlsconfig,
				Dial:            shared.RFC3493Dialer,
				Proxy:           http.ProxyFromEnvironment,
			}

			c.websocketDialer = websocket.Dialer{
				NetDial:         shared.RFC3493Dialer,
				TLSClientConfig: tlsconfig,
			}

			c.certf = certf
			c.keyf = keyf

			if r.Addr[0:8] == "https://" {
				c.BaseURL = "https://" + r.Addr[8:]
				c.BaseWSURL = "wss://" + r.Addr[8:]
			} else {
				c.BaseURL = "https://" + r.Addr
				c.BaseWSURL = "wss://" + r.Addr
			}
			c.Transport = "https"
			c.Http.Transport = tr
			c.loadServerCert()
			c.Remote = &r
		}
	} else {
		return nil, fmt.Errorf(i18n.G("unknown remote name: %q"), remote)
	}

	return &c, nil
}
Esempio n. 8
0
File: images.go Progetto: djibi2/lxd
func imgPostURLInfo(d *Daemon, req imagePostReq, op *operation) error {
	var err error

	if req.Source["url"] == "" {
		return fmt.Errorf("Missing URL")
	}

	// Resolve the image URL
	if d.tlsconfig == nil {
		d.tlsconfig, err = shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return err
		}
	}

	tr := &http.Transport{
		TLSClientConfig: d.tlsconfig,
		Dial:            shared.RFC3493Dialer,
		Proxy:           http.ProxyFromEnvironment,
	}

	myhttp := http.Client{
		Transport: tr,
	}

	head, err := http.NewRequest("HEAD", req.Source["url"], nil)
	if err != nil {
		return err
	}

	architecturesStr := []string{}
	for _, arch := range d.architectures {
		architecturesStr = append(architecturesStr, fmt.Sprintf("%d", arch))
	}

	head.Header.Set("User-Agent", shared.UserAgent)
	head.Header.Set("LXD-Server-Architectures", strings.Join(architecturesStr, ", "))
	head.Header.Set("LXD-Server-Version", shared.Version)

	raw, err := myhttp.Do(head)
	if err != nil {
		return err
	}

	hash := raw.Header.Get("LXD-Image-Hash")
	if hash == "" {
		return fmt.Errorf("Missing LXD-Image-Hash header")
	}

	url := raw.Header.Get("LXD-Image-URL")
	if url == "" {
		return fmt.Errorf("Missing LXD-Image-URL header")
	}

	// Import the image
	err = d.ImageDownload(op,
		url, hash, "", false, true)

	if err != nil {
		return err
	}

	info, err := dbImageGet(d.db, hash, false, false)
	if err != nil {
		return err
	}

	if req.Public {
		err = dbImageSetPublic(d.db, info.Id, req.Public)
		if err != nil {
			return err
		}
	}

	metadata := make(map[string]string)
	metadata["fingerprint"] = info.Fingerprint
	metadata["size"] = strconv.FormatInt(info.Size, 10)
	op.UpdateMetadata(metadata)

	return nil
}
Esempio n. 9
0
func createFromMigration(d *Daemon, req *containerPostReq) Response {
	if req.Source.Mode != "pull" {
		return NotImplemented
	}

	run := func(op *operation) error {
		args := containerArgs{
			Architecture: req.Architecture,
			BaseImage:    req.Source.BaseImage,
			Config:       req.Config,
			Ctype:        cTypeRegular,
			Devices:      req.Devices,
			Ephemeral:    req.Ephemeral,
			Name:         req.Name,
			Profiles:     req.Profiles,
		}

		var c container
		_, err := dbImageGet(d.db, req.Source.BaseImage, false, true)

		/* Only create a container from an image if we're going to
		 * rsync over the top of it. In the case of a better file
		 * transfer mechanism, let's just use that.
		 *
		 * TODO: we could invent some negotiation here, where if the
		 * source and sink both have the same image, we can clone from
		 * it, but we have to know before sending the snapshot that
		 * we're sending the whole thing or just a delta from the
		 * image, so one extra negotiation round trip is needed. An
		 * alternative is to move actual container object to a later
		 * point and just negotiate it over the migration control
		 * socket. Anyway, it'll happen later :)
		 */
		if err == nil && d.Storage.MigrationType() == MigrationFSType_RSYNC {
			c, err = containerCreateFromImage(d, args, req.Source.BaseImage)
			if err != nil {
				return err
			}
		} else {
			c, err = containerCreateAsEmpty(d, args)
			if err != nil {
				return err
			}
		}

		config, err := shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			c.Delete()
			return err
		}

		migrationArgs := MigrationSinkArgs{
			Url: req.Source.Operation,
			Dialer: websocket.Dialer{
				TLSClientConfig: config,
				NetDial:         shared.RFC3493Dialer},
			Container: c,
			Secrets:   req.Source.Websockets,
		}

		sink, err := NewMigrationSink(&migrationArgs)
		if err != nil {
			c.Delete()
			return err
		}

		// Start the storage for this container (LVM mount/umount)
		c.StorageStart()

		// And finaly run the migration.
		err = sink()
		if err != nil {
			c.StorageStop()
			shared.Log.Error("Error during migration sink", "err", err)
			c.Delete()
			return fmt.Errorf("Error transferring container data: %s", err)
		}

		defer c.StorageStop()

		err = c.TemplateApply("copy")
		if err != nil {
			return err
		}

		return nil
	}

	resources := map[string][]string{}
	resources["containers"] = []string{req.Name}

	op, err := operationCreate(operationClassTask, resources, nil, run, nil, nil)
	if err != nil {
		return InternalError(err)
	}

	return OperationResponse(op)
}
Esempio n. 10
0
func createFromMigration(d *Daemon, req *containerPostReq) Response {
	if req.Source.Mode != "pull" {
		return NotImplemented
	}

	run := func() shared.OperationResult {
		createArgs := containerLXDArgs{
			Ctype:     cTypeRegular,
			Config:    req.Config,
			Profiles:  req.Profiles,
			Ephemeral: req.Ephemeral,
			BaseImage: req.Source.BaseImage,
		}

		var c container
		if _, err := dbImageGet(d.db, req.Source.BaseImage, false, true); err == nil {
			c, err = containerLXDCreateFromImage(
				d, req.Name, createArgs, req.Source.BaseImage)

			if err != nil {
				return shared.OperationError(err)
			}
		} else {
			c, err = containerLXDCreateAsEmpty(d, req.Name, createArgs)
			if err != nil {
				return shared.OperationError(err)
			}
		}

		config, err := shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			c.Delete()
			return shared.OperationError(err)
		}

		args := MigrationSinkArgs{
			Url: req.Source.Operation,
			Dialer: websocket.Dialer{
				TLSClientConfig: config,
				NetDial:         shared.RFC3493Dialer},
			Container: c,
			Secrets:   req.Source.Websockets,
		}

		sink, err := NewMigrationSink(&args)
		if err != nil {
			c.Delete()
			return shared.OperationError(err)
		}

		// Start the storage for this container (LVM mount/umount)
		c.StorageStart()

		// And finaly run the migration.
		err = sink()
		if err != nil {
			c.StorageStop()
			c.Delete()
			return shared.OperationError(fmt.Errorf("Error transferring container data: %s", err))
		}

		defer c.StorageStop()

		err = c.TemplateApply("copy")
		if err != nil {
			return shared.OperationError(err)
		}

		return shared.OperationError(nil)
	}

	resources := make(map[string][]string)
	resources["containers"] = []string{req.Name}

	return &asyncResponse{run: run, resources: resources}
}
Esempio n. 11
0
File: images.go Progetto: vahe/lxd
func imgPostURLInfo(d *Daemon, req imagePostReq, op *operation) error {
	var err error

	if req.Source["url"] == "" {
		return fmt.Errorf("Missing URL")
	}

	// Resolve the image URL
	tlsConfig, err := shared.GetTLSConfig("", "", "", nil)
	if err != nil {
		return err
	}

	tr := &http.Transport{
		TLSClientConfig: tlsConfig,
		Dial:            shared.RFC3493Dialer,
		Proxy:           d.proxy,
	}

	myhttp := http.Client{
		Transport: tr,
	}

	head, err := http.NewRequest("HEAD", req.Source["url"], nil)
	if err != nil {
		return err
	}

	architecturesStr := []string{}
	for _, arch := range d.architectures {
		architecturesStr = append(architecturesStr, fmt.Sprintf("%d", arch))
	}

	head.Header.Set("User-Agent", shared.UserAgent)
	head.Header.Set("LXD-Server-Architectures", strings.Join(architecturesStr, ", "))
	head.Header.Set("LXD-Server-Version", shared.Version)

	raw, err := myhttp.Do(head)
	if err != nil {
		return err
	}

	hash := raw.Header.Get("LXD-Image-Hash")
	if hash == "" {
		return fmt.Errorf("Missing LXD-Image-Hash header")
	}

	url := raw.Header.Get("LXD-Image-URL")
	if url == "" {
		return fmt.Errorf("Missing LXD-Image-URL header")
	}

	// Import the image
	hash, err = d.ImageDownload(op, url, "direct", "", "", hash, false, req.AutoUpdate)
	if err != nil {
		return err
	}

	id, info, err := dbImageGet(d.db, hash, false, false)
	if err != nil {
		return err
	}

	// Allow overriding or adding properties
	for k, v := range req.Properties {
		info.Properties[k] = v
	}

	if req.Public || req.AutoUpdate || req.Filename != "" || len(req.Properties) > 0 {
		err = dbImageUpdate(d.db, id, req.Filename, info.Size, req.Public, req.AutoUpdate, info.Architecture, info.CreationDate, info.ExpiryDate, info.Properties)
		if err != nil {
			return err
		}
	}

	metadata := make(map[string]string)
	metadata["fingerprint"] = info.Fingerprint
	metadata["size"] = strconv.FormatInt(info.Size, 10)
	op.UpdateMetadata(metadata)

	return nil
}
Esempio n. 12
0
func (d *Daemon) UpdateHTTPsPort(oldAddress string, newAddress string) error {
	var sockets []Socket

	if oldAddress == newAddress {
		return nil
	}

	if oldAddress != "" {
		oldHost, oldPort, err := net.SplitHostPort(oldAddress)
		if err != nil {
			oldHost = oldAddress
			oldPort = shared.DefaultPort
		}

		// Strip brackets around IPv6 once we've gotten rid of the port
		oldHost = strings.TrimLeft(oldHost, "[")
		oldHost = strings.TrimRight(oldHost, "]")

		for _, socket := range d.Sockets {
			host, port, err := net.SplitHostPort(socket.Socket.Addr().String())
			if err != nil {
				host = socket.Socket.Addr().String()
				port = shared.DefaultPort
			}

			// Strip brackets around IPv6 once we've gotten rid of the port
			host = strings.TrimLeft(host, "[")
			host = strings.TrimRight(host, "]")

			if !shared.PathExists(host) && IpsEqual(net.ParseIP(host), net.ParseIP(oldHost)) && port == oldPort {
				socket.Socket.Close()
			} else {
				sockets = append(sockets, socket)
			}
		}
	} else {
		sockets = d.Sockets
	}

	if newAddress != "" {
		_, _, err := net.SplitHostPort(newAddress)
		if err != nil {
			ip := net.ParseIP(newAddress)
			if ip != nil && ip.To4() == nil {
				newAddress = fmt.Sprintf("[%s]:%s", newAddress, shared.DefaultPort)
			} else {
				newAddress = fmt.Sprintf("%s:%s", newAddress, shared.DefaultPort)
			}
		}

		tlsConfig, err := shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return err
		}

		tcpl, err := tls.Listen("tcp", newAddress, tlsConfig)
		if err != nil {
			return fmt.Errorf("cannot listen on https socket: %v", err)
		}

		d.tomb.Go(func() error { return http.Serve(tcpl, d.mux) })
		sockets = append(sockets, Socket{Socket: tcpl, CloseOnExit: true})
	}

	d.Sockets = sockets
	return nil
}
Esempio n. 13
0
// NewClient returns a new LXD client.
func NewClient(config *Config, remote string) (*Client, error) {
	c := Client{
		config: *config,
		http:   http.Client{},
	}

	c.name = remote

	// TODO: Here, we don't support configurable local remotes, we only
	// support the default local LXD at /var/lib/lxd/unix.socket.
	if remote == "" {
		c.BaseURL = "http://unix.socket"
		c.BaseWSURL = "ws://unix.socket"
		c.Transport = "unix"
		c.http.Transport = &unixTransport
		c.websocketDialer.NetDial = unixDial
	} else if r, ok := config.Remotes[remote]; ok {
		if r.Addr[0:5] == "unix:" {
			c.BaseURL = "http://unix.socket"
			c.BaseWSURL = "ws://unix.socket"
			c.Transport = "unix"
			uDial := func(networ, addr string) (net.Conn, error) {
				var err error
				var raddr *net.UnixAddr
				if r.Addr[7:] == "unix://" {
					raddr, err = net.ResolveUnixAddr("unix", r.Addr[7:])
				} else {
					raddr, err = net.ResolveUnixAddr("unix", r.Addr[5:])
				}
				if err != nil {
					return nil, err
				}
				return net.DialUnix("unix", nil, raddr)
			}
			c.http.Transport = &http.Transport{Dial: uDial}
			c.websocketDialer.NetDial = uDial
			c.Remote = &r
		} else {
			certf, keyf, err := readMyCert()
			if err != nil {
				return nil, err
			}

			tlsconfig, err := shared.GetTLSConfig(certf, keyf)
			if err != nil {
				return nil, err
			}

			tr := &http.Transport{
				TLSClientConfig: tlsconfig,
				Dial:            shared.RFC3493Dialer,
				Proxy:           http.ProxyFromEnvironment,
			}

			c.websocketDialer = websocket.Dialer{
				NetDial:         shared.RFC3493Dialer,
				TLSClientConfig: tlsconfig,
			}

			c.certf = certf
			c.keyf = keyf

			if r.Addr[0:8] == "https://" {
				c.BaseURL = "https://" + r.Addr[8:]
				c.BaseWSURL = "wss://" + r.Addr[8:]
			} else {
				c.BaseURL = "https://" + r.Addr
				c.BaseWSURL = "wss://" + r.Addr
			}
			c.Transport = "https"
			c.http.Transport = tr
			c.loadServerCert()
			c.Remote = &r
		}
	} else {
		return nil, fmt.Errorf(gettext.Gettext("unknown remote name: %q"), remote)
	}

	if err := c.Finger(); err != nil {
		return nil, err
	}

	return &c, nil
}
Esempio n. 14
0
func (d *Daemon) Init() error {
	/* Setup logging */
	if shared.Log == nil {
		shared.SetLogger("", "", true, true)
	}

	if !d.IsMock {
		shared.Log.Info("LXD is starting",
			log.Ctx{"path": shared.VarPath("")})
	} else {
		shared.Log.Info("Mock LXD is starting",
			log.Ctx{"path": shared.VarPath("")})
	}

	/* Detect user namespaces */
	runningInUserns = shared.RunningInUserNS()

	/* Detect apparmor support */
	if aaEnabled && os.Getenv("LXD_SECURITY_APPARMOR") == "false" {
		aaEnabled = false
		shared.Log.Warn("Per-container AppArmor profiles have been manually disabled")
	}

	if aaEnabled && !shared.IsDir("/sys/kernel/security/apparmor") {
		aaEnabled = false
		shared.Log.Warn("Per-container AppArmor profiles disabled because of lack of kernel support")
	}

	if aaEnabled && !haveMacAdmin() {
		shared.Log.Warn("Per-container AppArmor profiles are disabled because mac_admin capability is missing.")
		aaEnabled = false
	}

	_, err := exec.LookPath("apparmor_parser")
	if aaEnabled && err != nil {
		aaEnabled = false
		shared.Log.Warn("Per-container AppArmor profiles disabled because 'apparmor_parser' couldn't be found")
	}

	if aaEnabled && runningInUserns {
		aaEnabled = false
		shared.Log.Warn("Per-container AppArmor profiles disabled because LXD is running inside a user namespace")
	}

	/* Get the list of supported architectures */
	var architectures = []int{}

	uname := syscall.Utsname{}
	if err := syscall.Uname(&uname); err != nil {
		return err
	}

	architectureName := ""
	for _, c := range uname.Machine {
		if c == 0 {
			break
		}
		architectureName += string(byte(c))
	}

	architecture, err := shared.ArchitectureId(architectureName)
	if err != nil {
		return err
	}
	architectures = append(architectures, architecture)

	personalities, err := shared.ArchitecturePersonalities(architecture)
	if err != nil {
		return err
	}
	for _, personality := range personalities {
		architectures = append(architectures, personality)
	}
	d.architectures = architectures

	/* Create required paths */
	d.lxcpath = shared.VarPath("containers")
	err = os.MkdirAll(d.lxcpath, 0755)
	if err != nil {
		return err
	}

	// Create default directories
	if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil {
		return err
	}

	/* Detect the filesystem */
	d.BackingFs, err = filesystemDetect(d.lxcpath)
	if err != nil {
		shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err})
	}

	/* Read the uid/gid allocation */
	d.IdmapSet, err = shared.DefaultIdmapSet()
	if err != nil {
		shared.Log.Warn("Error reading idmap", log.Ctx{"err": err.Error()})
		shared.Log.Warn("Only privileged containers will be able to run")
	} else {
		shared.Log.Info("Default uid/gid map:")
		for _, lxcmap := range d.IdmapSet.ToLxcString() {
			shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n"))
		}
	}

	/* Initialize the database */
	err = initializeDbObject(d, shared.VarPath("lxd.db"))
	if err != nil {
		return err
	}

	/* Prune images */
	d.pruneChan = make(chan bool)
	go func() {
		for {
			expiryStr, err := dbImageExpiryGet(d.db)
			var expiry int
			if err != nil {
				expiry = 10
			} else {
				expiry, err = strconv.Atoi(expiryStr)
				if err != nil {
					expiry = 10
				}
				if expiry <= 0 {
					expiry = 1
				}
			}
			timer := time.NewTimer(time.Duration(expiry) * 24 * time.Hour)
			timeChan := timer.C
			select {
			case <-timeChan:
				d.pruneExpiredImages()
			case <-d.pruneChan:
				d.pruneExpiredImages()
				timer.Stop()
			}
		}
	}()

	/* Setup /dev/lxd */
	d.devlxd, err = createAndBindDevLxd()
	if err != nil {
		return err
	}

	if err := setupSharedMounts(); err != nil {
		return err
	}

	var tlsConfig *tls.Config
	if !d.IsMock {
		err = d.SetupStorageDriver()
		if err != nil {
			return fmt.Errorf("Failed to setup storage: %s", err)
		}

		/* Restart containers */
		containersRestart(d)
		containersWatch(d)

		/* Setup the TLS authentication */
		certf, keyf, err := readMyCert()
		if err != nil {
			return err
		}
		d.certf = certf
		d.keyf = keyf
		readSavedClientCAList(d)

		tlsConfig, err = shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return err
		}
	}

	/* Setup the web server */
	d.mux = mux.NewRouter()

	d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for _, c := range api10 {
		d.createCmd("1.0", c)
	}

	d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		shared.Log.Debug("Sending top level 404", log.Ctx{"url": r.URL})
		w.Header().Set("Content-Type", "application/json")
		NotFound.Render(w)
	})

	listeners, err := activation.Listeners(false)
	if err != nil {
		return err
	}

	var sockets []Socket

	if len(listeners) > 0 {
		shared.Log.Info("LXD is socket activated")

		for _, listener := range listeners {
			if shared.PathExists(listener.Addr().String()) {
				sockets = append(sockets, Socket{Socket: listener, CloseOnExit: false})
			} else {
				tlsListener := tls.NewListener(listener, tlsConfig)
				sockets = append(sockets, Socket{Socket: tlsListener, CloseOnExit: false})
			}
		}
	} else {
		shared.Log.Info("LXD isn't socket activated")

		localSocketPath := shared.VarPath("unix.socket")

		// If the socket exists, let's try to connect to it and see if there's
		// a lxd running.
		if shared.PathExists(localSocketPath) {
			c := &lxd.Config{Remotes: map[string]lxd.RemoteConfig{}}
			_, err := lxd.NewClient(c, "")
			if err != nil {
				shared.Log.Debug("Detected stale unix socket, deleting")
				// Connecting failed, so let's delete the socket and
				// listen on it ourselves.
				err = os.Remove(localSocketPath)
				if err != nil {
					return err
				}
			}
		}

		unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath)
		if err != nil {
			return fmt.Errorf("cannot resolve unix socket address: %v", err)
		}

		unixl, err := net.ListenUnix("unix", unixAddr)
		if err != nil {
			return fmt.Errorf("cannot listen on unix socket: %v", err)
		}

		if err := os.Chmod(localSocketPath, 0660); err != nil {
			return err
		}

		gid, err := shared.GroupId(*group)
		if err != nil {
			return err
		}

		if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil {
			return err
		}

		sockets = append(sockets, Socket{Socket: unixl, CloseOnExit: true})
	}

	listenAddr, err := d.ConfigValueGet("core.https_address")
	if err != nil {
		return err
	}

	if listenAddr != "" {
		_, _, err := net.SplitHostPort(listenAddr)
		if err != nil {
			listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort)
		}

		tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig)
		if err != nil {
			shared.Log.Error("cannot listen on https socket, skipping...", log.Ctx{"err": err})
		} else {
			sockets = append(sockets, Socket{Socket: tcpl, CloseOnExit: true})
		}
	}

	if !d.IsMock {
		d.Sockets = sockets
	} else {
		d.Sockets = []Socket{}
	}

	d.tomb.Go(func() error {
		shared.Log.Info("REST API daemon:")
		for _, socket := range d.Sockets {
			shared.Log.Info(" - binding socket", log.Ctx{"socket": socket.Socket.Addr()})
			current_socket := socket
			d.tomb.Go(func() error { return http.Serve(current_socket.Socket, d.mux) })
		}

		d.tomb.Go(func() error {
			server := devLxdServer(d)
			return server.Serve(d.devlxd)
		})
		return nil
	})

	return nil
}
Esempio n. 15
0
File: daemon.go Progetto: Ramzec/lxd
// StartDaemon starts the shared daemon with the provided configuration.
func StartDaemon(listenAddr string) (*Daemon, error) {
	d := &Daemon{}

	d.lxcpath = shared.VarPath("lxc")
	err := os.MkdirAll(shared.VarPath("/"), 0755)
	if err != nil {
		return nil, err
	}
	err = os.MkdirAll(d.lxcpath, 0755)
	if err != nil {
		return nil, err
	}

	d.BackingFs, err = shared.GetFilesystem(d.lxcpath)
	if err != nil {
		shared.Debugf("Error detecting backing fs: %s\n", err)
	}

	certf, keyf, err := readMyCert()
	if err != nil {
		return nil, err
	}
	d.certf = certf
	d.keyf = keyf

	err = initDb(d)
	if err != nil {
		return nil, err
	}

	readSavedClientCAList(d)

	d.mux = mux.NewRouter()

	d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for _, c := range api10 {
		d.createCmd("1.0", c)
	}

	d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		shared.Debugf("sending top level 404: %s", r.URL)
		w.Header().Set("Content-Type", "application/json")
		NotFound.Render(w)
	})

	d.IdmapSet, err = shared.DefaultIdmapSet()
	if err != nil {
		shared.Logf("error reading idmap: %s", err.Error())
		shared.Logf("operations requiring idmap will not be available")
	} else {
		shared.Debugf("Default uid/gid map:")
		for _, lxcmap := range d.IdmapSet.ToLxcString() {
			shared.Debugf(" - " + lxcmap)
		}
	}

	tlsConfig, err := shared.GetTLSConfig(d.certf, d.keyf)
	if err != nil {
		return nil, err
	}

	listeners, err := activation.Listeners(false)
	if err != nil {
		return nil, err
	}

	var localSockets []net.Listener
	var remoteSockets []net.Listener

	if len(listeners) > 0 {
		shared.Debugf("LXD is socket activated.\n")

		for _, listener := range listeners {
			if _, err := os.Stat(listener.Addr().String()); err == nil {
				localSockets = append(localSockets, listener)
			} else {
				tlsListener := tls.NewListener(listener, tlsConfig)
				remoteSockets = append(remoteSockets, tlsListener)
			}
		}
	} else {
		shared.Debugf("LXD isn't socket activated.\n")

		localSocketPath := shared.VarPath("unix.socket")

		// If the socket exists, let's try to connect to it and see if there's
		// a lxd running.
		if _, err := os.Stat(localSocketPath); err == nil {
			c := &lxd.Config{Remotes: map[string]lxd.RemoteConfig{}}
			_, err := lxd.NewClient(c, "")
			if err != nil {
				shared.Debugf("Detected old but dead unix socket, deleting it...")
				// Connecting failed, so let's delete the socket and
				// listen on it ourselves.
				err = os.Remove(localSocketPath)
				if err != nil {
					return nil, err
				}
			}
		}

		unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath)
		if err != nil {
			return nil, fmt.Errorf("cannot resolve unix socket address: %v", err)
		}

		unixl, err := net.ListenUnix("unix", unixAddr)
		if err != nil {
			return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
		}

		if err := os.Chmod(localSocketPath, 0660); err != nil {
			return nil, err
		}

		gid, err := shared.GroupId(*group)
		if err != nil {
			return nil, err
		}

		if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil {
			return nil, err
		}

		localSockets = append(localSockets, unixl)

		if listenAddr != "" {
			tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig)
			if err != nil {
				return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
			}

			remoteSockets = append(remoteSockets, tcpl)
		}
	}

	d.localSockets = localSockets
	d.remoteSockets = remoteSockets
	d.devlxd, err = createAndBindDevLxd()
	if err != nil {
		return nil, err
	}

	containersRestart(d)
	containersWatch(d)

	d.tomb.Go(func() error {
		for _, socket := range d.localSockets {
			shared.Debugf(" - binding local socket: %s\n", socket.Addr())
			d.tomb.Go(func() error { return http.Serve(socket, d.mux) })
		}
		for _, socket := range d.remoteSockets {
			shared.Debugf(" - binding remote socket: %s\n", socket.Addr())
			d.tomb.Go(func() error { return http.Serve(socket, d.mux) })
		}

		d.tomb.Go(func() error {
			server := devLxdServer(d)
			return server.Serve(d.devlxd)
		})
		return nil
	})

	return d, nil
}
Esempio n. 16
0
// StartDaemon starts the shared daemon with the provided configuration.
func StartDaemon() (*Daemon, error) {
	d := &Daemon{}

	/* Setup logging */
	if shared.Log == nil {
		shared.SetLogger("", "", true, true)
	}

	shared.Log.Info("LXD is starting.")

	/* Get the list of supported architectures */
	var architectures = []int{}

	uname := syscall.Utsname{}
	if err := syscall.Uname(&uname); err != nil {
		return nil, err
	}

	architectureName := ""
	for _, c := range uname.Machine {
		if c == 0 {
			break
		}
		architectureName += string(byte(c))
	}

	architecture, err := shared.ArchitectureId(architectureName)
	if err != nil {
		return nil, err
	}
	architectures = append(architectures, architecture)

	personalities, err := shared.ArchitecturePersonalities(architecture)
	if err != nil {
		return nil, err
	}
	for _, personality := range personalities {
		architectures = append(architectures, personality)
	}
	d.architectures = architectures

	/* Create required paths */
	d.lxcpath = shared.VarPath("containers")
	err = os.MkdirAll(d.lxcpath, 0755)
	if err != nil {
		return nil, err
	}

	// Create default directories
	if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil {
		return nil, err
	}
	if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil {
		return nil, err
	}
	if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil {
		return nil, err
	}

	/* Detect the filesystem */
	d.BackingFs, err = filesystemDetect(d.lxcpath)
	if err != nil {
		shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err})
	}

	/* Read the uid/gid allocation */
	d.IdmapSet, err = shared.DefaultIdmapSet()
	if err != nil {
		shared.Log.Warn("error reading idmap", log.Ctx{"err": err.Error()})
		shared.Log.Warn("operations requiring idmap will not be available")
	} else {
		shared.Log.Info("Default uid/gid map:")
		for _, lxcmap := range d.IdmapSet.ToLxcString() {
			shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n"))
		}
	}

	/* Initialize the database */
	err = initDb(d)
	if err != nil {
		return nil, err
	}

	/* Setup the TLS authentication */
	certf, keyf, err := readMyCert()
	if err != nil {
		return nil, err
	}
	d.certf = certf
	d.keyf = keyf
	readSavedClientCAList(d)

	tlsConfig, err := shared.GetTLSConfig(d.certf, d.keyf)
	if err != nil {
		return nil, err
	}

	/* Setup /dev/lxd */
	d.devlxd, err = createAndBindDevLxd()
	if err != nil {
		return nil, err
	}

	if err := setupSharedMounts(); err != nil {
		return nil, err
	}

	/* Restart containers */
	containersRestart(d)
	containersWatch(d)

	err = d.SetupStorageDriver()
	if err != nil {
		return nil, fmt.Errorf("Failed to setup storage: %s", err)
	}

	/* Setup the web server */
	d.mux = mux.NewRouter()

	d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for _, c := range api10 {
		d.createCmd("1.0", c)
	}

	d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		shared.Log.Debug("sending top level 404", log.Ctx{"url": r.URL})
		w.Header().Set("Content-Type", "application/json")
		NotFound.Render(w)
	})

	listeners, err := activation.Listeners(false)
	if err != nil {
		return nil, err
	}

	var sockets []net.Listener

	if len(listeners) > 0 {
		shared.Log.Info("LXD is socket activated.")

		for _, listener := range listeners {
			if shared.PathExists(listener.Addr().String()) {
				sockets = append(sockets, listener)
			} else {
				tlsListener := tls.NewListener(listener, tlsConfig)
				sockets = append(sockets, tlsListener)
			}
		}
	} else {
		shared.Log.Info("LXD isn't socket activated.")

		localSocketPath := shared.VarPath("unix.socket")

		// If the socket exists, let's try to connect to it and see if there's
		// a lxd running.
		if shared.PathExists(localSocketPath) {
			c := &lxd.Config{Remotes: map[string]lxd.RemoteConfig{}}
			_, err := lxd.NewClient(c, "")
			if err != nil {
				shared.Log.Debug("Detected old but dead unix socket, deleting it...")
				// Connecting failed, so let's delete the socket and
				// listen on it ourselves.
				err = os.Remove(localSocketPath)
				if err != nil {
					return nil, err
				}
			}
		}

		unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath)
		if err != nil {
			return nil, fmt.Errorf("cannot resolve unix socket address: %v", err)
		}

		unixl, err := net.ListenUnix("unix", unixAddr)
		if err != nil {
			return nil, fmt.Errorf("cannot listen on unix socket: %v", err)
		}

		if err := os.Chmod(localSocketPath, 0660); err != nil {
			return nil, err
		}

		gid, err := shared.GroupId(*group)
		if err != nil {
			return nil, err
		}

		if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil {
			return nil, err
		}

		sockets = append(sockets, unixl)
	}

	listenAddr, err := d.ConfigValueGet("core.https_address")
	if err != nil {
		return nil, err
	}

	if listenAddr != "" {
		_, _, err := net.SplitHostPort(listenAddr)
		if err != nil {
			listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort)
		}

		tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig)
		if err != nil {
			return nil, fmt.Errorf("cannot listen on https socket: %v", err)
		}

		sockets = append(sockets, tcpl)
	}

	d.Sockets = sockets

	d.pruneChan = make(chan bool)
	go func() {
		for {
			expiryStr, err := dbGetImageExpiry(d)
			var expiry int
			if err != nil {
				expiry = 10
			} else {
				expiry, err = strconv.Atoi(expiryStr)
				if err != nil {
					expiry = 10
				}
				if expiry <= 0 {
					expiry = 1
				}
			}
			timer := time.NewTimer(time.Duration(expiry) * 24 * time.Hour)
			timeChan := timer.C
			select {
			case <-timeChan:
				d.pruneExpiredImages()
			case <-d.pruneChan:
				d.pruneExpiredImages()
				timer.Stop()
			}
		}
	}()

	d.tomb.Go(func() error {
		for _, socket := range d.Sockets {
			shared.Log.Info(" - binding socket", log.Ctx{"socket": socket.Addr()})
			current_socket := socket
			d.tomb.Go(func() error { return http.Serve(current_socket, d.mux) })
		}

		d.tomb.Go(func() error {
			server := devLxdServer(d)
			return server.Serve(d.devlxd)
		})
		return nil
	})

	return d, nil
}
Esempio n. 17
0
func createFromMigration(d *Daemon, req *containerPostReq) Response {
	if req.Source.Mode != "pull" {
		return NotImplemented
	}

	createArgs := DbCreateContainerArgs{
		d:         d,
		name:      req.Name,
		ctype:     cTypeRegular,
		config:    req.Config,
		profiles:  req.Profiles,
		ephem:     req.Ephemeral,
		baseImage: req.Source.BaseImage,
	}

	_, err := dbCreateContainer(createArgs)
	if err != nil {
		return SmartError(err)
	}

	c, err := newLxdContainer(req.Name, d)
	if err != nil {
		removeContainer(d, req.Name)
		return SmartError(err)
	}

	// rsync complaisn if the parent directory for the rootfs sync doesn't
	// exist
	dpath := shared.VarPath("lxc", req.Name)
	if err := os.MkdirAll(dpath, 0700); err != nil {
		removeContainer(d, req.Name)
		return InternalError(err)
	}

	if err := extractShiftIfExists(d, c, req.Source.BaseImage, req.Name); err != nil {
		removeContainer(d, req.Name)
		return InternalError(err)
	}

	config, err := shared.GetTLSConfig(d.certf, d.keyf)
	if err != nil {
		removeContainer(d, req.Name)
		return InternalError(err)
	}

	args := migration.MigrationSinkArgs{
		Url: req.Source.Operation,
		Dialer: websocket.Dialer{
			TLSClientConfig: config,
			NetDial:         shared.RFC3493Dialer},
		Container: c.c,
		Secrets:   req.Source.Websockets,
		IdMapSet:  c.idmapset,
	}

	sink, err := migration.NewMigrationSink(&args)
	if err != nil {
		removeContainer(d, req.Name)
		return BadRequest(err)
	}

	run := func() shared.OperationResult {
		err := sink()
		if err != nil {
			removeContainer(d, req.Name)
			return shared.OperationError(err)
		}

		c, err := newLxdContainer(req.Name, d)
		if err != nil {
			return shared.OperationError(err)
		}

		err = templateApply(c, "copy")
		if err != nil {
			return shared.OperationError(err)
		}

		return shared.OperationError(nil)
	}

	resources := make(map[string][]string)
	resources["containers"] = []string{req.Name}

	return &asyncResponse{run: run, resources: resources}
}
Esempio n. 18
0
func (d *Daemon) Init() error {
	d.shutdownChan = make(chan bool)

	/* Set the executable path */
	absPath, err := os.Readlink("/proc/self/exe")
	if err != nil {
		return err
	}
	d.execPath = absPath

	/* Set the LVM environment */
	err = os.Setenv("LVM_SUPPRESS_FD_WARNINGS", "1")
	if err != nil {
		return err
	}

	/* Setup logging if that wasn't done before */
	if shared.Log == nil {
		shared.Log, err = logging.GetLogger("", "", true, true, nil)
		if err != nil {
			return err
		}
	}

	if !d.IsMock {
		shared.Log.Info("LXD is starting",
			log.Ctx{"path": shared.VarPath("")})
	} else {
		shared.Log.Info("Mock LXD is starting",
			log.Ctx{"path": shared.VarPath("")})
	}

	/* Detect user namespaces */
	runningInUserns = shared.RunningInUserNS()

	/* Detect AppArmor support */
	if aaAvailable && os.Getenv("LXD_SECURITY_APPARMOR") == "false" {
		aaAvailable = false
		aaAdmin = false
		shared.Log.Warn("AppArmor support has been manually disabled")
	}

	if aaAvailable && !shared.IsDir("/sys/kernel/security/apparmor") {
		aaAvailable = false
		aaAdmin = false
		shared.Log.Warn("AppArmor support has been disabled because of lack of kernel support")
	}

	_, err = exec.LookPath("apparmor_parser")
	if aaAvailable && err != nil {
		aaAvailable = false
		aaAdmin = false
		shared.Log.Warn("AppArmor support has been disabled because 'apparmor_parser' couldn't be found")
	}

	/* Detect AppArmor admin support */
	if aaAdmin && !haveMacAdmin() {
		aaAdmin = false
		shared.Log.Warn("Per-container AppArmor profiles are disabled because the mac_admin capability is missing.")
	}

	if aaAdmin && runningInUserns {
		aaAdmin = false
		shared.Log.Warn("Per-container AppArmor profiles are disabled because LXD is running in an unprivileged container.")
	}

	/* Detect AppArmor confinment */
	if !aaConfined {
		profile := aaProfile()
		if profile != "unconfined" && profile != "" {
			aaConfined = true
			shared.Log.Warn("Per-container AppArmor profiles are disabled because LXD is already protected by AppArmor.")
		}
	}

	/* Detect CGroup support */
	cgCpuController = shared.PathExists("/sys/fs/cgroup/cpu/")
	if !cgCpuController {
		shared.Log.Warn("Couldn't find the CGroup CPU controller, CPU time limits will be ignored.")
	}

	cgCpusetController = shared.PathExists("/sys/fs/cgroup/cpuset/")
	if !cgCpusetController {
		shared.Log.Warn("Couldn't find the CGroup CPUset controller, CPU pinning will be ignored.")
	}

	cgMemoryController = shared.PathExists("/sys/fs/cgroup/memory/")
	if !cgMemoryController {
		shared.Log.Warn("Couldn't find the CGroup memory controller, memory limits will be ignored.")
	}

	cgSwapAccounting = shared.PathExists("/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes")
	if !cgSwapAccounting {
		shared.Log.Warn("CGroup memory swap accounting is disabled, swap limits will be ignored.")
	}

	/* Get the list of supported architectures */
	var architectures = []int{}

	uname := syscall.Utsname{}
	if err := syscall.Uname(&uname); err != nil {
		return err
	}

	architectureName := ""
	for _, c := range uname.Machine {
		if c == 0 {
			break
		}
		architectureName += string(byte(c))
	}

	architecture, err := shared.ArchitectureId(architectureName)
	if err != nil {
		return err
	}
	architectures = append(architectures, architecture)

	personalities, err := shared.ArchitecturePersonalities(architecture)
	if err != nil {
		return err
	}
	for _, personality := range personalities {
		architectures = append(architectures, personality)
	}
	d.architectures = architectures

	/* Set container path */
	d.lxcpath = shared.VarPath("containers")

	/* Make sure all our directories are available */
	if err := os.MkdirAll(shared.VarPath("containers"), 0711); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("devices"), 0711); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("devlxd"), 0755); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("images"), 0700); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("security"), 0700); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("shmounts"), 0711); err != nil {
		return err
	}
	if err := os.MkdirAll(shared.VarPath("snapshots"), 0700); err != nil {
		return err
	}

	/* Detect the filesystem */
	d.BackingFs, err = filesystemDetect(d.lxcpath)
	if err != nil {
		shared.Log.Error("Error detecting backing fs", log.Ctx{"err": err})
	}

	/* Read the uid/gid allocation */
	d.IdmapSet, err = shared.DefaultIdmapSet()
	if err != nil {
		shared.Log.Warn("Error reading idmap", log.Ctx{"err": err.Error()})
		shared.Log.Warn("Only privileged containers will be able to run")
	} else {
		shared.Log.Info("Default uid/gid map:")
		for _, lxcmap := range d.IdmapSet.ToLxcString() {
			shared.Log.Info(strings.TrimRight(" - "+lxcmap, "\n"))
		}
	}

	/* Initialize the database */
	err = initializeDbObject(d, shared.VarPath("lxd.db"))
	if err != nil {
		return err
	}

	/* Prune images */
	d.pruneChan = make(chan bool)
	go func() {
		d.pruneExpiredImages()
		for {
			timer := time.NewTimer(24 * time.Hour)
			timeChan := timer.C
			select {
			case <-timeChan:
				/* run once per day */
				d.pruneExpiredImages()
			case <-d.pruneChan:
				/* run when image.remote_cache_expiry is changed */
				d.pruneExpiredImages()
				timer.Stop()
			}
		}
	}()

	/* Setup /dev/lxd */
	d.devlxd, err = createAndBindDevLxd()
	if err != nil {
		return err
	}

	if err := setupSharedMounts(); err != nil {
		return err
	}

	var tlsConfig *tls.Config
	if !d.IsMock {
		err = d.SetupStorageDriver()
		if err != nil {
			return fmt.Errorf("Failed to setup storage: %s", err)
		}

		/* Restart containers */
		go func() {
			containersRestart(d)
		}()

		/* Start the scheduler */
		go deviceTaskScheduler(d)

		/* Setup the TLS authentication */
		certf, keyf, err := readMyCert()
		if err != nil {
			return err
		}
		d.certf = certf
		d.keyf = keyf
		readSavedClientCAList(d)

		tlsConfig, err = shared.GetTLSConfig(d.certf, d.keyf)
		if err != nil {
			return err
		}
	}

	/* Setup the web server */
	d.mux = mux.NewRouter()

	d.mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")
		SyncResponse(true, []string{"/1.0"}).Render(w)
	})

	for _, c := range api10 {
		d.createCmd("1.0", c)
	}

	for _, c := range apiInternal {
		d.createCmd("internal", c)
	}

	d.mux.NotFoundHandler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		shared.Log.Debug("Sending top level 404", log.Ctx{"url": r.URL})
		w.Header().Set("Content-Type", "application/json")
		NotFound.Render(w)
	})

	listeners, err := activation.Listeners(false)
	if err != nil {
		return err
	}

	var sockets []Socket

	if len(listeners) > 0 {
		shared.Log.Info("LXD is socket activated")

		for _, listener := range listeners {
			if shared.PathExists(listener.Addr().String()) {
				sockets = append(sockets, Socket{Socket: listener, CloseOnExit: false})
			} else {
				tlsListener := tls.NewListener(listener, tlsConfig)
				sockets = append(sockets, Socket{Socket: tlsListener, CloseOnExit: false})
			}
		}
	} else {
		shared.Log.Info("LXD isn't socket activated")

		localSocketPath := shared.VarPath("unix.socket")

		// If the socket exists, let's try to connect to it and see if there's
		// a lxd running.
		if shared.PathExists(localSocketPath) {
			c, err := lxd.NewClient(&lxd.DefaultConfig, "local")
			if err != nil {
				return err
			}

			err = c.Finger()
			if err != nil {
				shared.Log.Debug("Detected stale unix socket, deleting")
				// Connecting failed, so let's delete the socket and
				// listen on it ourselves.
				err = os.Remove(localSocketPath)
				if err != nil {
					return err
				}
			} else {
				return fmt.Errorf("LXD is already running.")
			}
		}

		unixAddr, err := net.ResolveUnixAddr("unix", localSocketPath)
		if err != nil {
			return fmt.Errorf("cannot resolve unix socket address: %v", err)
		}

		unixl, err := net.ListenUnix("unix", unixAddr)
		if err != nil {
			return fmt.Errorf("cannot listen on unix socket: %v", err)
		}

		if err := os.Chmod(localSocketPath, 0660); err != nil {
			return err
		}

		var gid int
		if d.group != "" {
			gid, err = shared.GroupId(d.group)
			if err != nil {
				return err
			}
		} else {
			gid = os.Getgid()
		}

		if err := os.Chown(localSocketPath, os.Getuid(), gid); err != nil {
			return err
		}

		sockets = append(sockets, Socket{Socket: unixl, CloseOnExit: true})
	}

	listenAddr, err := d.ConfigValueGet("core.https_address")
	if err != nil {
		return err
	}

	if listenAddr != "" {
		_, _, err := net.SplitHostPort(listenAddr)
		if err != nil {
			listenAddr = fmt.Sprintf("%s:%s", listenAddr, shared.DefaultPort)
		}

		tcpl, err := tls.Listen("tcp", listenAddr, tlsConfig)
		if err != nil {
			shared.Log.Error("cannot listen on https socket, skipping...", log.Ctx{"err": err})
		} else {
			sockets = append(sockets, Socket{Socket: tcpl, CloseOnExit: true})
		}
	}

	if !d.IsMock {
		d.Sockets = sockets
	} else {
		d.Sockets = []Socket{}
	}

	d.tomb.Go(func() error {
		shared.Log.Info("REST API daemon:")
		for _, socket := range d.Sockets {
			shared.Log.Info(" - binding socket", log.Ctx{"socket": socket.Socket.Addr()})
			current_socket := socket
			d.tomb.Go(func() error { return http.Serve(current_socket.Socket, d.mux) })
		}

		d.tomb.Go(func() error {
			server := devLxdServer(d)
			return server.Serve(d.devlxd)
		})
		return nil
	})

	return nil
}
Esempio n. 19
0
func createFromMigration(d *Daemon, req *containerPostReq) Response {
	if req.Source.Mode != "pull" && req.Source.Mode != "push" {
		return NotImplemented
	}

	architecture, err := shared.ArchitectureId(req.Architecture)
	if err != nil {
		architecture = 0
	}

	args := containerArgs{
		Architecture: architecture,
		BaseImage:    req.Source.BaseImage,
		Config:       req.Config,
		Ctype:        cTypeRegular,
		Devices:      req.Devices,
		Ephemeral:    req.Ephemeral,
		Name:         req.Name,
		Profiles:     req.Profiles,
	}

	var c container
	_, _, err = dbImageGet(d.db, req.Source.BaseImage, false, true)

	/* Only create a container from an image if we're going to
	 * rsync over the top of it. In the case of a better file
	 * transfer mechanism, let's just use that.
	 *
	 * TODO: we could invent some negotiation here, where if the
	 * source and sink both have the same image, we can clone from
	 * it, but we have to know before sending the snapshot that
	 * we're sending the whole thing or just a delta from the
	 * image, so one extra negotiation round trip is needed. An
	 * alternative is to move actual container object to a later
	 * point and just negotiate it over the migration control
	 * socket. Anyway, it'll happen later :)
	 */
	if err == nil && d.Storage.MigrationType() == MigrationFSType_RSYNC {
		c, err = containerCreateFromImage(d, args, req.Source.BaseImage)
		if err != nil {
			return InternalError(err)
		}
	} else {
		c, err = containerCreateAsEmpty(d, args)
		if err != nil {
			return InternalError(err)
		}
	}

	var cert *x509.Certificate
	if req.Source.Certificate != "" {
		certBlock, _ := pem.Decode([]byte(req.Source.Certificate))
		if certBlock == nil {
			return InternalError(fmt.Errorf("Invalid certificate"))
		}

		cert, err = x509.ParseCertificate(certBlock.Bytes)
		if err != nil {
			return InternalError(err)
		}
	}

	config, err := shared.GetTLSConfig("", "", "", cert)
	if err != nil {
		c.Delete()
		return InternalError(err)
	}

	push := false
	if req.Source.Mode == "push" {
		push = true
	}

	migrationArgs := MigrationSinkArgs{
		Url: req.Source.Operation,
		Dialer: websocket.Dialer{
			TLSClientConfig: config,
			NetDial:         shared.RFC3493Dialer},
		Container: c,
		Secrets:   req.Source.Websockets,
		Push:      push,
		Live:      req.Source.Live,
	}

	sink, err := NewMigrationSink(&migrationArgs)
	if err != nil {
		c.Delete()
		return InternalError(err)
	}

	run := func(op *operation) error {
		// And finaly run the migration.
		err = sink.Do(op)
		if err != nil {
			shared.LogError("Error during migration sink", log.Ctx{"err": err})
			c.Delete()
			return fmt.Errorf("Error transferring container data: %s", err)
		}

		err = c.TemplateApply("copy")
		if err != nil {
			return err
		}

		return nil
	}

	resources := map[string][]string{}
	resources["containers"] = []string{req.Name}

	var op *operation
	if push {
		op, err = operationCreate(operationClassWebsocket, resources, sink.Metadata(), run, nil, sink.Connect)
		if err != nil {
			return InternalError(err)
		}
	} else {
		op, err = operationCreate(operationClassTask, resources, nil, run, nil, nil)
		if err != nil {
			return InternalError(err)
		}
	}

	return OperationResponse(op)
}