[lxc-devel] [lxd/master] Bugfixes

stgraber on Github lxc-bot at linuxcontainers.org
Wed Mar 16 21:59:25 UTC 2016


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 301 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20160316/2c69bda6/attachment.bin>
-------------- next part --------------
From 4c2678bf2e68c90dcde7d3fdf5b37e24f461aff8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 16 Mar 2016 15:52:33 -0400
Subject: [PATCH 1/4] More strictly parse remote URLs
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #1763

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxc/remote.go | 24 ++++++++++++---------
 po/lxd.pot    | 69 +++++++++++++++++++++++++++++++++++------------------------
 2 files changed, 55 insertions(+), 38 deletions(-)

diff --git a/lxc/remote.go b/lxc/remote.go
index 0c9cbca..6f54717 100644
--- a/lxc/remote.go
+++ b/lxc/remote.go
@@ -79,7 +79,7 @@ func getRemoteCertificate(address string) (*x509.Certificate, error) {
 
 	// Retrieve the certificate
 	if resp.TLS == nil || len(resp.TLS.PeerCertificates) == 0 {
-		return nil, fmt.Errorf("Unable to read remote TLS certificate")
+		return nil, fmt.Errorf(i18n.G("Unable to read remote TLS certificate"))
 	}
 
 	return resp.TLS.PeerCertificates[0], nil
@@ -95,24 +95,28 @@ func (c *remoteCmd) addServer(config *lxd.Config, server string, addr string, ac
 		config.Remotes = make(map[string]lxd.RemoteConfig)
 	}
 
-	// Fast track simplestreams
-	if protocol == "simplestreams" {
-		config.Remotes[server] = lxd.RemoteConfig{Addr: addr, Public: true, Protocol: protocol}
-		return nil
-	}
-
 	/* Complex remote URL parsing */
 	remoteURL, err := url.Parse(addr)
 	if err != nil {
 		return err
 	}
 
+	// Fast track simplestreams
+	if protocol == "simplestreams" {
+		if remoteURL.Scheme != "https" {
+			return fmt.Errorf(i18n.G("Only https URLs are supported for simplestreams"))
+		}
+
+		config.Remotes[server] = lxd.RemoteConfig{Addr: addr, Public: true, Protocol: protocol}
+		return nil
+	}
+
 	if remoteURL.Scheme != "" {
 		if remoteURL.Scheme != "unix" && remoteURL.Scheme != "https" {
-			rScheme = "https"
-		} else {
-			rScheme = remoteURL.Scheme
+			return fmt.Errorf(i18n.G("Invalid URL scheme \"%s\" in \"%s\""), remoteURL.Scheme, addr)
 		}
+
+		rScheme = remoteURL.Scheme
 	} else if addr[0] == '/' {
 		rScheme = "unix"
 	} else {
diff --git a/po/lxd.pot b/po/lxd.pot
index 66686ae..c2ec8cf 100644
--- a/po/lxd.pot
+++ b/po/lxd.pot
@@ -7,7 +7,7 @@
 msgid   ""
 msgstr  "Project-Id-Version: lxd\n"
         "Report-Msgid-Bugs-To: lxc-devel at lists.linuxcontainers.org\n"
-        "POT-Creation-Date: 2016-03-02 19:53-0500\n"
+        "POT-Creation-Date: 2016-03-16 15:52-0400\n"
         "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
         "Last-Translator: FULL NAME <EMAIL at ADDRESS>\n"
         "Language-Team: LANGUAGE <LL at li.org>\n"
@@ -90,11 +90,11 @@ msgstr  ""
 msgid   "ARCHITECTURE"
 msgstr  ""
 
-#: lxc/remote.go:52
+#: lxc/remote.go:53
 msgid   "Accept certificate"
 msgstr  ""
 
-#: lxc/remote.go:216
+#: lxc/remote.go:250
 #, c-format
 msgid   "Admin password for %s: "
 msgstr  ""
@@ -143,7 +143,7 @@ msgstr  ""
 msgid   "Cannot provide container name to list"
 msgstr  ""
 
-#: lxc/remote.go:166
+#: lxc/remote.go:200
 #, c-format
 msgid   "Certificate fingerprint: %x"
 msgstr  ""
@@ -155,7 +155,7 @@ msgid   "Changes state of one or more containers to %s.\n"
         "lxc %s <name> [<name>...]"
 msgstr  ""
 
-#: lxc/remote.go:239
+#: lxc/remote.go:273
 msgid   "Client certificate stored at server: "
 msgstr  ""
 
@@ -205,7 +205,7 @@ msgstr  ""
 msgid   "Copying the image: %s"
 msgstr  ""
 
-#: lxc/remote.go:181
+#: lxc/remote.go:215
 msgid   "Could not create server cert dir"
 msgstr  ""
 
@@ -251,12 +251,12 @@ msgid   "Delete containers or container snapshots.\n"
         "Destroy containers or snapshots with any attached data (configuration, snapshots, ...)."
 msgstr  ""
 
-#: lxc/config.go:606
+#: lxc/config.go:610
 #, c-format
 msgid   "Device %s added to %s"
 msgstr  ""
 
-#: lxc/config.go:634
+#: lxc/config.go:640
 #, c-format
 msgid   "Device %s removed from %s"
 msgstr  ""
@@ -384,6 +384,11 @@ msgid   "Initialize a container from a particular image.\n"
         "lxc init ubuntu u1"
 msgstr  ""
 
+#: lxc/remote.go:116
+#, c-format
+msgid   "Invalid URL scheme \"%s\" in \"%s\""
+msgstr  ""
+
 #: lxc/init.go:30 lxc/init.go:35
 msgid   "Invalid configuration key"
 msgstr  ""
@@ -552,7 +557,7 @@ msgid   "Manage files on a container.\n"
         "<source> in the case of pull, <target> in the case of push and <file> in the case of edit are <container name>/<path>"
 msgstr  ""
 
-#: lxc/remote.go:38
+#: lxc/remote.go:39
 msgid   "Manage remote LXD servers.\n"
         "\n"
         "lxc remote add <name> <url> [--accept-certificate] [--password=PASSWORD]\n"
@@ -660,11 +665,11 @@ msgstr  ""
 msgid   "Must supply container name for: "
 msgstr  ""
 
-#: lxc/list.go:338 lxc/remote.go:323
+#: lxc/list.go:338 lxc/remote.go:357
 msgid   "NAME"
 msgstr  ""
 
-#: lxc/remote.go:297 lxc/remote.go:302
+#: lxc/remote.go:331 lxc/remote.go:336
 msgid   "NO"
 msgstr  ""
 
@@ -685,6 +690,10 @@ msgstr  ""
 msgid   "No fingerprint specified."
 msgstr  ""
 
+#: lxc/remote.go:107
+msgid   "Only https URLs are supported for simplestreams"
+msgstr  ""
+
 #: lxc/image.go:397
 msgid   "Only https:// is supported for remote image import."
 msgstr  ""
@@ -714,11 +723,11 @@ msgstr  ""
 msgid   "PROFILES"
 msgstr  ""
 
-#: lxc/remote.go:325
+#: lxc/remote.go:359
 msgid   "PROTOCOL"
 msgstr  ""
 
-#: lxc/image.go:592 lxc/remote.go:326
+#: lxc/image.go:592 lxc/remote.go:360
 msgid   "PUBLIC"
 msgstr  ""
 
@@ -804,7 +813,7 @@ msgstr  ""
 msgid   "Properties:"
 msgstr  ""
 
-#: lxc/remote.go:55
+#: lxc/remote.go:56
 msgid   "Public image server"
 msgstr  ""
 
@@ -819,7 +828,7 @@ msgid   "Publish containers as images.\n"
         "lxc publish [remote:]container [remote:] [--alias=ALIAS]... [prop-key=prop-value]..."
 msgstr  ""
 
-#: lxc/remote.go:53
+#: lxc/remote.go:54
 msgid   "Remote admin password"
 msgstr  ""
 
@@ -849,19 +858,19 @@ msgstr  ""
 msgid   "STATE"
 msgstr  ""
 
-#: lxc/remote.go:327
+#: lxc/remote.go:361
 msgid   "STATIC"
 msgstr  ""
 
-#: lxc/remote.go:174
+#: lxc/remote.go:208
 msgid   "Server certificate NACKed by user"
 msgstr  ""
 
-#: lxc/remote.go:236
+#: lxc/remote.go:270
 msgid   "Server doesn't trust us after adding our cert"
 msgstr  ""
 
-#: lxc/remote.go:54
+#: lxc/remote.go:55
 msgid   "Server protocol (lxd or simplestreams)"
 msgstr  ""
 
@@ -979,10 +988,14 @@ msgstr  ""
 msgid   "UPLOAD DATE"
 msgstr  ""
 
-#: lxc/remote.go:324
+#: lxc/remote.go:358
 msgid   "URL"
 msgstr  ""
 
+#: lxc/remote.go:82
+msgid   "Unable to read remote TLS certificate"
+msgstr  ""
+
 #: lxc/image.go:323
 #, c-format
 msgid   "Uploaded: %s"
@@ -1013,7 +1026,7 @@ msgstr  ""
 msgid   "Whether to show the expanded configuration"
 msgstr  ""
 
-#: lxc/remote.go:299 lxc/remote.go:304
+#: lxc/remote.go:333 lxc/remote.go:338
 msgid   "YES"
 msgstr  ""
 
@@ -1033,11 +1046,11 @@ msgstr  ""
 msgid   "can't copy to the same container name"
 msgstr  ""
 
-#: lxc/remote.go:287
+#: lxc/remote.go:321
 msgid   "can't remove the default remote"
 msgstr  ""
 
-#: lxc/remote.go:313
+#: lxc/remote.go:347
 msgid   "default"
 msgstr  ""
 
@@ -1075,7 +1088,7 @@ msgstr  ""
 msgid   "not all the profiles from the source exist on the target"
 msgstr  ""
 
-#: lxc/remote.go:167
+#: lxc/remote.go:201
 msgid   "ok (y/n)?"
 msgstr  ""
 
@@ -1084,22 +1097,22 @@ msgstr  ""
 msgid   "processing aliases failed %s\n"
 msgstr  ""
 
-#: lxc/remote.go:349
+#: lxc/remote.go:383
 #, c-format
 msgid   "remote %s already exists"
 msgstr  ""
 
-#: lxc/remote.go:279 lxc/remote.go:341 lxc/remote.go:376 lxc/remote.go:392
+#: lxc/remote.go:313 lxc/remote.go:375 lxc/remote.go:410 lxc/remote.go:426
 #, c-format
 msgid   "remote %s doesn't exist"
 msgstr  ""
 
-#: lxc/remote.go:262
+#: lxc/remote.go:296
 #, c-format
 msgid   "remote %s exists as <%s>"
 msgstr  ""
 
-#: lxc/remote.go:283 lxc/remote.go:345 lxc/remote.go:380
+#: lxc/remote.go:317 lxc/remote.go:379 lxc/remote.go:414
 #, c-format
 msgid   "remote %s is static and cannot be modified"
 msgstr  ""

From 4c19e958f613d01f83bad42eaaa9296d4abb86f9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 16 Mar 2016 17:17:39 -0400
Subject: [PATCH 2/4] Fix devlxd access outside of an exec session
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #1751

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/devlxd.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lxd/devlxd.go b/lxd/devlxd.go
index ecb28e8..ce8b00a 100644
--- a/lxd/devlxd.go
+++ b/lxd/devlxd.go
@@ -297,7 +297,7 @@ func findContainerForPid(pid int32, d *Daemon) (container, error) {
 		if strings.HasPrefix(string(cmdline), "[lxc monitor]") {
 			// container names can't have spaces
 			parts := strings.Split(string(cmdline), " ")
-			name := parts[len(parts)-1]
+			name := strings.TrimSuffix(parts[len(parts)-1], "\x00")
 
 			return containerLoadByName(d, name)
 		}

From 689d722e17808bed8899fc6ebaa22e013044b7ed Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 16 Mar 2016 17:42:13 -0400
Subject: [PATCH 3/4] Restrict /dev/lxd to uid 0 in the container
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #1751

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/devlxd.go | 42 +++++++++++++++++++++++++++++++-----------
 1 file changed, 31 insertions(+), 11 deletions(-)

diff --git a/lxd/devlxd.go b/lxd/devlxd.go
index ce8b00a..8991a67 100644
--- a/lxd/devlxd.go
+++ b/lxd/devlxd.go
@@ -85,18 +85,32 @@ var handlers = []devLxdHandler{
 func hoistReq(f func(container, *http.Request) *devLxdResponse, d *Daemon) func(http.ResponseWriter, *http.Request) {
 	return func(w http.ResponseWriter, r *http.Request) {
 		conn := extractUnderlyingConn(w)
-		pid, ok := pidMapper.m[conn]
+		cred, ok := pidMapper.m[conn]
 		if !ok {
 			http.Error(w, pidNotInContainerErr.Error(), 500)
 			return
 		}
 
-		c, err := findContainerForPid(pid, d)
+		c, err := findContainerForPid(cred.pid, d)
 		if err != nil {
 			http.Error(w, err.Error(), 500)
 			return
 		}
 
+		// Access control
+		rootUid := int64(0)
+
+		idmapset, err := c.LastIdmapSet()
+		if err == nil && idmapset != nil {
+			uid, _ := idmapset.ShiftIntoNs(0, 0)
+			rootUid = int64(uid)
+		}
+
+		if rootUid != cred.uid {
+			http.Error(w, "Access denied for non-root user", 401)
+			return
+		}
+
 		resp := f(c, r)
 		if resp.code != http.StatusOK {
 			http.Error(w, fmt.Sprintf("%s", resp.content), resp.code)
@@ -185,21 +199,27 @@ func devLxdServer(d *Daemon) *http.Server {
  * from our http handlers, since there appears to be no way to pass information
  * around here.
  */
-var pidMapper = ConnPidMapper{m: map[*net.UnixConn]int32{}}
+var pidMapper = ConnPidMapper{m: map[*net.UnixConn]*ucred{}}
+
+type ucred struct {
+	pid int32
+	uid int64
+	gid int64
+}
 
 type ConnPidMapper struct {
-	m map[*net.UnixConn]int32
+	m map[*net.UnixConn]*ucred
 }
 
 func (m *ConnPidMapper) ConnStateHandler(conn net.Conn, state http.ConnState) {
 	unixConn := conn.(*net.UnixConn)
 	switch state {
 	case http.StateNew:
-		pid, err := getPid(unixConn)
+		cred, err := getCred(unixConn)
 		if err != nil {
-			shared.Debugf("Error getting pid for conn %s", err)
+			shared.Debugf("Error getting ucred for conn %s", err)
 		} else {
-			m.m[unixConn] = pid
+			m.m[unixConn] = cred
 		}
 	case http.StateActive:
 		return
@@ -234,15 +254,15 @@ func extractUnderlyingFd(unixConnPtr *net.UnixConn) int {
 	return int(fd.Int())
 }
 
-func getPid(conn *net.UnixConn) (int32, error) {
+func getCred(conn *net.UnixConn) (*ucred, error) {
 	fd := extractUnderlyingFd(conn)
 
-	_, _, pid, err := getUcred(fd)
+	uid, gid, pid, err := getUcred(fd)
 	if err != nil {
-		return 0, err
+		return nil, err
 	}
 
-	return pid, nil
+	return &ucred{pid, int64(uid), int64(gid)}, nil
 }
 
 /*

From dea6b959e9a8173af63a8c974aaf1097b5eb6bfb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Wed, 16 Mar 2016 17:58:40 -0400
Subject: [PATCH 4/4] Return better errors for public and simplestream remotes
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #1762

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 client.go     | 210 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 lxc/remote.go |   4 +-
 2 files changed, 201 insertions(+), 13 deletions(-)

diff --git a/client.go b/client.go
index 7decf89..9d4716f 100644
--- a/client.go
+++ b/client.go
@@ -493,18 +493,11 @@ func (c *Client) url(elem ...string) string {
 }
 
 func (c *Client) GetServerConfig() (*Response, error) {
-	return c.baseGet(c.url(shared.APIVersion))
-}
-
-func (c *Client) Finger() error {
-	shared.Debugf("Fingering the daemon")
-	_, err := c.GetServerConfig()
-	if err != nil {
-		return err
+	if c.Remote.Protocol == "simplestreams" {
+		return nil, fmt.Errorf("This function isn't supported by simplestreams remote.")
 	}
 
-	shared.Debugf("Pong received")
-	return nil
+	return c.baseGet(c.url(shared.APIVersion))
 }
 
 func (c *Client) AmTrusted() bool {
@@ -550,6 +543,10 @@ func (c *Client) IsPublic() bool {
 }
 
 func (c *Client) ListContainers() ([]shared.ContainerInfo, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	resp, err := c.get("containers?recursion=1")
 	if err != nil {
 		return nil, err
@@ -827,6 +824,10 @@ func (c *Client) ExportImage(image string, target string) (string, error) {
 }
 
 func (c *Client) PostImageURL(imageFile string, public bool, aliases []string) (string, error) {
+	if c.Remote.Public {
+		return "", fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	source := shared.Jmap{
 		"type": "url",
 		"mode": "pull",
@@ -865,6 +866,10 @@ func (c *Client) PostImageURL(imageFile string, public bool, aliases []string) (
 }
 
 func (c *Client) PostImage(imageFile string, rootfsFile string, properties []string, public bool, aliases []string, progressHandler func(percent int)) (string, error) {
+	if c.Remote.Public {
+		return "", fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	uri := c.url(shared.APIVersion, "images")
 
 	var err error
@@ -1030,6 +1035,10 @@ func (c *Client) GetImageInfo(image string) (*shared.ImageInfo, error) {
 }
 
 func (c *Client) PutImageInfo(name string, p shared.BriefImageInfo) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	_, err := c.put(fmt.Sprintf("images/%s", name), p, Sync)
 	return err
 }
@@ -1053,11 +1062,19 @@ func (c *Client) ListImages() ([]shared.ImageInfo, error) {
 }
 
 func (c *Client) DeleteImage(image string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	_, err := c.delete(fmt.Sprintf("images/%s", image), nil, Sync)
 	return err
 }
 
 func (c *Client) PostAlias(alias string, desc string, target string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{"description": desc, "target": target, "name": alias}
 
 	_, err := c.post("images/aliases", body, Sync)
@@ -1065,6 +1082,10 @@ func (c *Client) PostAlias(alias string, desc string, target string) error {
 }
 
 func (c *Client) DeleteAlias(alias string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	_, err := c.delete(fmt.Sprintf("images/aliases/%s", alias), nil, Sync)
 	return err
 }
@@ -1089,6 +1110,10 @@ func (c *Client) ListAliases() (shared.ImageAliases, error) {
 }
 
 func (c *Client) CertificateList() ([]shared.CertInfo, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	resp, err := c.get("certificates?recursion=1")
 	if err != nil {
 		return nil, err
@@ -1103,6 +1128,10 @@ func (c *Client) CertificateList() ([]shared.CertInfo, error) {
 }
 
 func (c *Client) AddMyCertToServer(pwd string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{"type": "client", "password": pwd}
 
 	_, err := c.post("certificates", body, Sync)
@@ -1110,12 +1139,20 @@ func (c *Client) AddMyCertToServer(pwd string) error {
 }
 
 func (c *Client) CertificateAdd(cert *x509.Certificate, name string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	b64 := base64.StdEncoding.EncodeToString(cert.Raw)
 	_, err := c.post("certificates", shared.Jmap{"type": "client", "certificate": b64, "name": name}, Sync)
 	return err
 }
 
 func (c *Client) CertificateRemove(fingerprint string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	_, err := c.delete(fmt.Sprintf("certificates/%s", fingerprint), nil, Sync)
 	return err
 }
@@ -1156,6 +1193,10 @@ func (c *Client) GetAlias(alias string) string {
 // Init creates a container from either a fingerprint or an alias; you must
 // provide at least one.
 func (c *Client) Init(name string, imgremote string, image string, profiles *[]string, config map[string]string, ephem bool) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	var tmpremote *Client
 	var err error
 
@@ -1291,6 +1332,10 @@ func (c *Client) Init(name string, imgremote string, image string, profiles *[]s
 }
 
 func (c *Client) LocalCopy(source string, name string, config map[string]string, profiles []string, ephemeral bool) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{
 		"source": shared.Jmap{
 			"type":   "copy",
@@ -1306,6 +1351,10 @@ func (c *Client) LocalCopy(source string, name string, config map[string]string,
 }
 
 func (c *Client) Monitor(types []string, handler func(interface{})) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	url := c.BaseWSURL + path.Join("/", "1.0", "events")
 	if len(types) != 0 {
 		url += "?type=" + strings.Join(types, ",")
@@ -1343,6 +1392,10 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
 	stdin io.ReadCloser, stdout io.WriteCloser,
 	stderr io.WriteCloser, controlHandler func(*Client, *websocket.Conn)) (int, error) {
 
+	if c.Remote.Public {
+		return -1, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{
 		"command":            cmd,
 		"wait-for-websocket": true,
@@ -1453,6 +1506,10 @@ func (c *Client) Exec(name string, cmd []string, env map[string]string,
 }
 
 func (c *Client) Action(name string, action shared.ContainerAction, timeout int, force bool, stateful bool) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{
 		"action":  action,
 		"timeout": timeout,
@@ -1466,6 +1523,10 @@ func (c *Client) Action(name string, action shared.ContainerAction, timeout int,
 }
 
 func (c *Client) Delete(name string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	var url string
 	s := strings.SplitN(name, "/", 2)
 	if len(s) == 2 {
@@ -1493,6 +1554,10 @@ func (c *Client) ServerStatus() (*shared.ServerState, error) {
 }
 
 func (c *Client) ContainerInfo(name string) (*shared.ContainerInfo, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	ct := shared.ContainerInfo{}
 
 	resp, err := c.get(fmt.Sprintf("containers/%s", name))
@@ -1508,6 +1573,10 @@ func (c *Client) ContainerInfo(name string) (*shared.ContainerInfo, error) {
 }
 
 func (c *Client) ContainerState(name string) (*shared.ContainerState, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	ct := shared.ContainerState{}
 
 	resp, err := c.get(fmt.Sprintf("containers/%s/state", name))
@@ -1523,6 +1592,10 @@ func (c *Client) ContainerState(name string) (*shared.ContainerState, error) {
 }
 
 func (c *Client) GetLog(container string, log string) (io.Reader, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	uri := c.url(shared.APIVersion, "containers", container, "logs", log)
 	resp, err := c.getRaw(uri)
 	if err != nil {
@@ -1533,6 +1606,10 @@ func (c *Client) GetLog(container string, log string) (io.Reader, error) {
 }
 
 func (c *Client) ProfileConfig(name string) (*shared.ProfileConfig, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	ct := shared.ProfileConfig{}
 
 	resp, err := c.get(fmt.Sprintf("profiles/%s", name))
@@ -1548,6 +1625,10 @@ func (c *Client) ProfileConfig(name string) (*shared.ProfileConfig, error) {
 }
 
 func (c *Client) PushFile(container string, p string, gid int, uid int, mode os.FileMode, buf io.ReadSeeker) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	query := url.Values{"path": []string{p}}
 	uri := c.url(shared.APIVersion, "containers", container, "files") + "?" + query.Encode()
 
@@ -1571,6 +1652,10 @@ func (c *Client) PushFile(container string, p string, gid int, uid int, mode os.
 }
 
 func (c *Client) PullFile(container string, p string) (int, int, os.FileMode, io.ReadCloser, error) {
+	if c.Remote.Public {
+		return 0, 0, 0, nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	uri := c.url(shared.APIVersion, "containers", container, "files")
 	query := url.Values{"path": []string{p}}
 
@@ -1585,6 +1670,10 @@ func (c *Client) PullFile(container string, p string) (int, int, os.FileMode, io
 }
 
 func (c *Client) GetMigrationSourceWS(container string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{"migration": true}
 	url := fmt.Sprintf("containers/%s", container)
 	if shared.IsSnapshot(container) {
@@ -1600,6 +1689,10 @@ func (c *Client) GetMigrationSourceWS(container string) (*Response, error) {
 }
 
 func (c *Client) MigrateFrom(name string, operation string, certificate string, secrets map[string]string, architecture string, config map[string]string, devices shared.Devices, profiles []string, baseImage string, ephemeral bool) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	source := shared.Jmap{
 		"type":        "migration",
 		"mode":        "pull",
@@ -1622,6 +1715,10 @@ func (c *Client) MigrateFrom(name string, operation string, certificate string,
 }
 
 func (c *Client) Rename(name string, newName string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	oldNameParts := strings.SplitN(name, "/", 2)
 	newNameParts := strings.SplitN(newName, "/", 2)
 	if len(oldNameParts) != len(newNameParts) {
@@ -1672,16 +1769,28 @@ func (c *Client) WaitForSuccess(waitURL string) error {
 }
 
 func (c *Client) RestoreSnapshot(container string, snapshotName string, stateful bool) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{"restore": snapshotName, "stateful": stateful}
 	return c.put(fmt.Sprintf("containers/%s", container), body, Async)
 }
 
 func (c *Client) Snapshot(container string, snapshotName string, stateful bool) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{"name": snapshotName, "stateful": stateful}
 	return c.post(fmt.Sprintf("containers/%s/snapshots", container), body, Async)
 }
 
 func (c *Client) ListSnapshots(container string) ([]shared.SnapshotInfo, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	qUrl := fmt.Sprintf("containers/%s/snapshots?recursion=1", container)
 	resp, err := c.get(qUrl)
 	if err != nil {
@@ -1721,6 +1830,10 @@ func (c *Client) GetServerConfigString() ([]string, error) {
 }
 
 func (c *Client) SetServerConfig(key string, value string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	ss, err := c.ServerStatus()
 	if err != nil {
 		return nil, err
@@ -1732,6 +1845,10 @@ func (c *Client) SetServerConfig(key string, value string) (*Response, error) {
 }
 
 func (c *Client) UpdateServerConfig(ss shared.BriefServerState) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	return c.put("", ss, Sync)
 }
 
@@ -1739,6 +1856,10 @@ func (c *Client) UpdateServerConfig(ss shared.BriefServerState) (*Response, erro
  * return string array representing a container's full configuration
  */
 func (c *Client) GetContainerConfig(container string) ([]string, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	var resp []string
 
 	st, err := c.ContainerInfo(container)
@@ -1759,6 +1880,10 @@ func (c *Client) GetContainerConfig(container string) ([]string, error) {
 }
 
 func (c *Client) SetContainerConfig(container, key, value string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ContainerInfo(container)
 	if err != nil {
 		return err
@@ -1784,6 +1909,10 @@ func (c *Client) SetContainerConfig(container, key, value string) error {
 }
 
 func (c *Client) UpdateContainerConfig(container string, st shared.BriefContainerInfo) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	resp, err := c.put(fmt.Sprintf("containers/%s", container), st, Async)
 	if err != nil {
 		return err
@@ -1793,6 +1922,10 @@ func (c *Client) UpdateContainerConfig(container string, st shared.BriefContaine
 }
 
 func (c *Client) ProfileCreate(p string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	body := shared.Jmap{"name": p}
 
 	_, err := c.post("profiles", body, Sync)
@@ -1800,11 +1933,19 @@ func (c *Client) ProfileCreate(p string) error {
 }
 
 func (c *Client) ProfileDelete(p string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	_, err := c.delete(fmt.Sprintf("profiles/%s", p), nil, Sync)
 	return err
 }
 
 func (c *Client) GetProfileConfig(profile string) (map[string]string, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ProfileConfig(profile)
 	if err != nil {
 		return nil, err
@@ -1814,6 +1955,10 @@ func (c *Client) GetProfileConfig(profile string) (map[string]string, error) {
 }
 
 func (c *Client) SetProfileConfigItem(profile, key, value string) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ProfileConfig(profile)
 	if err != nil {
 		shared.Debugf("Error getting profile %s to update", profile)
@@ -1831,6 +1976,10 @@ func (c *Client) SetProfileConfigItem(profile, key, value string) error {
 }
 
 func (c *Client) PutProfile(name string, profile shared.ProfileConfig) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	if profile.Name != name {
 		return fmt.Errorf("Cannot change profile name")
 	}
@@ -1840,6 +1989,10 @@ func (c *Client) PutProfile(name string, profile shared.ProfileConfig) error {
 }
 
 func (c *Client) ListProfiles() ([]string, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	resp, err := c.get("profiles")
 	if err != nil {
 		return nil, err
@@ -1877,6 +2030,10 @@ func (c *Client) ListProfiles() ([]string, error) {
 }
 
 func (c *Client) ApplyProfile(container, profile string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ContainerInfo(container)
 	if err != nil {
 		return nil, err
@@ -1888,6 +2045,10 @@ func (c *Client) ApplyProfile(container, profile string) (*Response, error) {
 }
 
 func (c *Client) ContainerDeviceDelete(container, devname string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ContainerInfo(container)
 	if err != nil {
 		return nil, err
@@ -1899,6 +2060,10 @@ func (c *Client) ContainerDeviceDelete(container, devname string) (*Response, er
 }
 
 func (c *Client) ContainerDeviceAdd(container, devname, devtype string, props []string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ContainerInfo(container)
 	if err != nil {
 		return nil, err
@@ -1930,6 +2095,10 @@ func (c *Client) ContainerDeviceAdd(container, devname, devtype string, props []
 }
 
 func (c *Client) ContainerListDevices(container string) ([]string, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ContainerInfo(container)
 	if err != nil {
 		return nil, err
@@ -1942,6 +2111,10 @@ func (c *Client) ContainerListDevices(container string) ([]string, error) {
 }
 
 func (c *Client) ProfileDeviceDelete(profile, devname string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ProfileConfig(profile)
 	if err != nil {
 		return nil, err
@@ -1957,6 +2130,10 @@ func (c *Client) ProfileDeviceDelete(profile, devname string) (*Response, error)
 }
 
 func (c *Client) ProfileDeviceAdd(profile, devname, devtype string, props []string) (*Response, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ProfileConfig(profile)
 	if err != nil {
 		return nil, err
@@ -1985,6 +2162,10 @@ func (c *Client) ProfileDeviceAdd(profile, devname, devtype string, props []stri
 }
 
 func (c *Client) ProfileListDevices(profile string) ([]string, error) {
+	if c.Remote.Public {
+		return nil, fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ProfileConfig(profile)
 	if err != nil {
 		return nil, err
@@ -1994,7 +2175,6 @@ func (c *Client) ProfileListDevices(profile string) ([]string, error) {
 		devs = append(devs, fmt.Sprintf("%s: %s", n, d["type"]))
 	}
 	return devs, nil
-
 }
 
 // WebsocketDial attempts to dial a websocket to a LXD instance, parsing
@@ -2015,6 +2195,10 @@ func WebsocketDial(dialer websocket.Dialer, url string) (*websocket.Conn, error)
 }
 
 func (c *Client) ProfileCopy(name, newname string, dest *Client) error {
+	if c.Remote.Public {
+		return fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	st, err := c.ProfileConfig(name)
 	if err != nil {
 		return err
@@ -2043,6 +2227,10 @@ func (c *Client) AsyncWaitMeta(resp *Response) (*shared.Jmap, error) {
 }
 
 func (c *Client) ImageFromContainer(cname string, public bool, aliases []string, properties map[string]string) (string, error) {
+	if c.Remote.Public {
+		return "", fmt.Errorf("This function isn't supported by public remotes.")
+	}
+
 	source := shared.Jmap{"type": "container", "name": cname}
 	if shared.IsSnapshot(cname) {
 		source["type"] = "snapshot"
diff --git a/lxc/remote.go b/lxc/remote.go
index 6f54717..8a7b5a3 100644
--- a/lxc/remote.go
+++ b/lxc/remote.go
@@ -184,7 +184,7 @@ func (c *remoteCmd) addServer(config *lxd.Config, server string, addr string, ac
 	var certificate *x509.Certificate
 
 	/* Attempt to connect using the system root CA */
-	err = d.Finger()
+	_, err = d.GetServerConfig()
 	if err != nil {
 		// Failed to connect using the system CA, so retrieve the remote certificate
 		certificate, err = getRemoteCertificate(addr)
@@ -234,7 +234,7 @@ func (c *remoteCmd) addServer(config *lxd.Config, server string, addr string, ac
 	if d.IsPublic() || public {
 		config.Remotes[server] = lxd.RemoteConfig{Addr: addr, Public: true}
 
-		if err := d.Finger(); err != nil {
+		if _, err := d.GetServerConfig(); err != nil {
 			return err
 		}
 


More information about the lxc-devel mailing list