[lxc-devel] [lxd/master] lxd: Add username/fingerprint to request context

stgraber on Github lxc-bot at linuxcontainers.org
Fri Mar 1 11:33:59 UTC 2019


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 354 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20190301/0c6ac866/attachment-0001.bin>
-------------- next part --------------
From 152cb6aa173379f33df84d22c9d349bbc340c1a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 1 Mar 2019 12:20:04 +0100
Subject: [PATCH] lxd: Add username/fingerprint to request context
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/api_1.0.go      |  3 ++-
 lxd/certificates.go | 10 ++++++----
 lxd/cluster/tls.go  |  7 +++++--
 lxd/daemon.go       | 43 ++++++++++++++++++++++++++++---------------
 lxd/images.go       | 19 +++++++++++--------
 lxd/util/http.go    | 10 +++++-----
 6 files changed, 57 insertions(+), 35 deletions(-)

diff --git a/lxd/api_1.0.go b/lxd/api_1.0.go
index eb33c60fc5..a00b25a3fe 100644
--- a/lxd/api_1.0.go
+++ b/lxd/api_1.0.go
@@ -110,7 +110,8 @@ func api10Get(d *Daemon, r *http.Request) Response {
 	}
 
 	// If untrusted, return now
-	if d.checkTrustedClient(r) != nil {
+	trusted, _ := d.checkTrustedClient(r)
+	if trusted != nil {
 		return SyncResponseETag(true, srv, nil)
 	}
 
diff --git a/lxd/certificates.go b/lxd/certificates.go
index e361a06618..0c2e014727 100644
--- a/lxd/certificates.go
+++ b/lxd/certificates.go
@@ -74,7 +74,7 @@ func certificatesGet(d *Daemon, r *http.Request) Response {
 }
 
 func readSavedClientCAList(d *Daemon) {
-	d.clientCerts = []x509.Certificate{}
+	d.clientCerts = map[string]x509.Certificate{}
 
 	dbCerts, err := d.cluster.CertificatesGet()
 	if err != nil {
@@ -94,7 +94,8 @@ func readSavedClientCAList(d *Daemon) {
 			logger.Infof("Error reading certificate for %s: %s", dbCert.Name, err)
 			continue
 		}
-		d.clientCerts = append(d.clientCerts, *cert)
+
+		d.clientCerts[shared.CertFingerprint(cert)] = *cert
 	}
 }
 
@@ -123,7 +124,8 @@ func certificatesPost(d *Daemon, r *http.Request) Response {
 		return SmartError(err)
 	}
 
-	if d.checkTrustedClient(r) != nil && util.PasswordCheck(secret, req.Password) != nil {
+	trusted, _ := d.checkTrustedClient(r)
+	if trusted != nil && util.PasswordCheck(secret, req.Password) != nil {
 		logger.Warn("Bad trust password", log.Ctx{"url": r.URL.RequestURI(), "ip": r.RemoteAddr})
 		return Forbidden(nil)
 	}
@@ -201,7 +203,7 @@ func certificatesPost(d *Daemon, r *http.Request) Response {
 		}
 	}
 
-	d.clientCerts = append(d.clientCerts, *cert)
+	d.clientCerts[shared.CertFingerprint(cert)] = *cert
 
 	return SyncResponseLocation(true, nil, fmt.Sprintf("/%s/certificates/%s", version.APIVersion, fingerprint))
 }
diff --git a/lxd/cluster/tls.go b/lxd/cluster/tls.go
index 7ed754ec4c..6d09ff60ea 100644
--- a/lxd/cluster/tls.go
+++ b/lxd/cluster/tls.go
@@ -46,6 +46,9 @@ func tlsCheckCert(r *http.Request, info *shared.CertInfo) bool {
 		// check for good measure.
 		panic(fmt.Sprintf("invalid keypair material: %v", err))
 	}
-	trustedCerts := []x509.Certificate{*cert}
-	return r.TLS != nil && util.CheckTrustState(*r.TLS.PeerCertificates[0], trustedCerts)
+	trustedCerts := map[string]x509.Certificate{"0": *cert}
+
+	trusted, _ := util.CheckTrustState(*r.TLS.PeerCertificates[0], trustedCerts)
+
+	return r.TLS != nil && trusted
 }
diff --git a/lxd/daemon.go b/lxd/daemon.go
index d9f1b77f84..c3f1e39b24 100644
--- a/lxd/daemon.go
+++ b/lxd/daemon.go
@@ -45,7 +45,7 @@ import (
 
 // A Daemon can respond to requests from a shared client.
 type Daemon struct {
-	clientCerts  []x509.Certificate
+	clientCerts  map[string]x509.Certificate
 	os           *sys.OS
 	db           *db.Node
 	maas         *maas.Controller
@@ -157,36 +157,38 @@ type Command struct {
 }
 
 // Check whether the request comes from a trusted client.
-func (d *Daemon) checkTrustedClient(r *http.Request) error {
+func (d *Daemon) checkTrustedClient(r *http.Request) (error, string) {
 	// Check the cluster certificate first, so we return an error if the
 	// notification header is set but the client is not presenting the
 	// cluster certificate (iow this request does not appear to come from a
 	// cluster node).
 	cert, _ := x509.ParseCertificate(d.endpoints.NetworkCert().KeyPair().Certificate[0])
-	clusterCerts := []x509.Certificate{*cert}
+	clusterCerts := map[string]x509.Certificate{"0": *cert}
 	if r.TLS != nil {
 		for i := range r.TLS.PeerCertificates {
-			if util.CheckTrustState(*r.TLS.PeerCertificates[i], clusterCerts) {
-				return nil
+			trusted, _ := util.CheckTrustState(*r.TLS.PeerCertificates[i], clusterCerts)
+			if trusted {
+				return nil, ""
 			}
 		}
 	}
+
 	if isClusterNotification(r) {
-		return fmt.Errorf("cluster notification not using cluster certificate")
+		return fmt.Errorf("cluster notification not using cluster certificate"), ""
 	}
 
 	if r.RemoteAddr == "@" {
 		// Unix socket
-		return nil
+		return nil, ""
 	}
 
 	if r.RemoteAddr == "@devlxd" {
 		// Devlxd unix socket
-		return fmt.Errorf("devlxd query")
+		return fmt.Errorf("devlxd query"), ""
 	}
 
 	if r.TLS == nil {
-		return fmt.Errorf("no TLS")
+		return fmt.Errorf("no TLS"), ""
 	}
 
 	if d.externalAuth != nil && r.Header.Get(httpbakery.BakeryProtocolHeader) != "" {
@@ -198,17 +200,26 @@ func (d *Daemon) checkTrustedClient(r *http.Request) error {
 			Action: r.Method,
 		}}
 
-		_, err := authChecker.Allow(ctx, ops...)
-		return err
+		info, err := authChecker.Allow(ctx, ops...)
+		if err != nil {
+			return err, ""
+		}
+
+		if info != nil && info.Identity != nil {
+			return nil, info.Identity.Id()
+		}
+
+		return nil, ""
 	}
 
 	for i := range r.TLS.PeerCertificates {
-		if util.CheckTrustState(*r.TLS.PeerCertificates[i], d.clientCerts) {
-			return nil
+		trusted, username := util.CheckTrustState(*r.TLS.PeerCertificates[i], d.clientCerts)
+		if trusted {
+			return nil, username
 		}
 	}
 
-	return fmt.Errorf("unauthorized")
+	return fmt.Errorf("unauthorized"), ""
 }
 
 func writeMacaroonsRequiredResponse(b *identchecker.Bakery, r *http.Request, w http.ResponseWriter, derr *bakery.DischargeRequiredError, expiry int64) {
@@ -285,11 +296,13 @@ func (d *Daemon) createCmd(restAPI *mux.Router, version string, c Command) {
 		}
 
 		untrustedOk := (r.Method == "GET" && c.untrustedGet) || (r.Method == "POST" && c.untrustedPost)
-		err := d.checkTrustedClient(r)
+		err, username := d.checkTrustedClient(r)
 		if err == nil {
 			logger.Debug(
 				"handling",
 				log.Ctx{"method": r.Method, "url": r.URL.RequestURI(), "ip": r.RemoteAddr})
+
+			r = r.WithContext(context.WithValue(r.Context(), "username", username))
 		} else if untrustedOk && r.Header.Get("X-LXD-authenticated") == "" {
 			logger.Debug(
 				fmt.Sprintf("allowing untrusted %s", r.Method),
diff --git a/lxd/images.go b/lxd/images.go
index c5a2a8b7ad..ce18c006ed 100644
--- a/lxd/images.go
+++ b/lxd/images.go
@@ -881,9 +881,9 @@ func doImagesGet(d *Daemon, recursion bool, project string, public bool) (interf
 
 func imagesGet(d *Daemon, r *http.Request) Response {
 	project := projectParam(r)
-	public := d.checkTrustedClient(r) != nil
+	trusted, _ := d.checkTrustedClient(r)
 
-	result, err := doImagesGet(d, util.IsRecursionRequest(r), project, public)
+	result, err := doImagesGet(d, util.IsRecursionRequest(r), project, trusted != nil)
 	if err != nil {
 		return SmartError(err)
 	}
@@ -1484,7 +1484,7 @@ func imageValidSecret(fingerprint string, secret string) bool {
 func imageGet(d *Daemon, r *http.Request) Response {
 	project := projectParam(r)
 	fingerprint := mux.Vars(r)["fingerprint"]
-	public := d.checkTrustedClient(r) != nil
+	trusted, _ := d.checkTrustedClient(r)
 	secret := r.FormValue("secret")
 
 	info, response := doImageGet(d.cluster, project, fingerprint, false)
@@ -1492,7 +1492,7 @@ func imageGet(d *Daemon, r *http.Request) Response {
 		return response
 	}
 
-	if !info.Public && public && !imageValidSecret(info.Fingerprint, secret) {
+	if !info.Public && trusted != nil && !imageValidSecret(info.Fingerprint, secret) {
 		return NotFound(fmt.Errorf("Image '%s' not found", info.Fingerprint))
 	}
 
@@ -1646,7 +1646,9 @@ func aliasesGet(d *Daemon, r *http.Request) Response {
 			responseStr = append(responseStr, url)
 
 		} else {
-			_, alias, err := d.cluster.ImageAliasGet(project, name, d.checkTrustedClient(r) == nil)
+			trusted, _ := d.checkTrustedClient(r)
+
+			_, alias, err := d.cluster.ImageAliasGet(project, name, trusted == nil)
 			if err != nil {
 				continue
 			}
@@ -1665,7 +1667,8 @@ func aliasGet(d *Daemon, r *http.Request) Response {
 	project := projectParam(r)
 	name := mux.Vars(r)["name"]
 
-	_, alias, err := d.cluster.ImageAliasGet(project, name, d.checkTrustedClient(r) == nil)
+	trusted, _ := d.checkTrustedClient(r)
+	_, alias, err := d.cluster.ImageAliasGet(project, name, trusted == nil)
 	if err != nil {
 		return SmartError(err)
 	}
@@ -1811,7 +1814,7 @@ func imageExport(d *Daemon, r *http.Request) Response {
 	project := projectParam(r)
 	fingerprint := mux.Vars(r)["fingerprint"]
 
-	public := d.checkTrustedClient(r) != nil
+	trusted, _ := d.checkTrustedClient(r)
 	secret := r.FormValue("secret")
 
 	var imgInfo *api.Image
@@ -1832,7 +1835,7 @@ func imageExport(d *Daemon, r *http.Request) Response {
 			return SmartError(err)
 		}
 
-		if !imgInfo.Public && public && !imageValidSecret(imgInfo.Fingerprint, secret) {
+		if !imgInfo.Public && trusted != nil && !imageValidSecret(imgInfo.Fingerprint, secret) {
 			return NotFound(fmt.Errorf("Image '%s' not found", imgInfo.Fingerprint))
 		}
 	}
diff --git a/lxd/util/http.go b/lxd/util/http.go
index d191d7c6fd..30d5103bb6 100644
--- a/lxd/util/http.go
+++ b/lxd/util/http.go
@@ -132,20 +132,20 @@ type ContextAwareRequest interface {
 // CheckTrustState checks whether the given client certificate is trusted
 // (i.e. it has a valid time span and it belongs to the given list of trusted
 // certificates).
-func CheckTrustState(cert x509.Certificate, trustedCerts []x509.Certificate) bool {
+func CheckTrustState(cert x509.Certificate, trustedCerts map[string]x509.Certificate) (bool, string) {
 	// Extra validity check (should have been caught by TLS stack)
 	if time.Now().Before(cert.NotBefore) || time.Now().After(cert.NotAfter) {
-		return false
+		return false, ""
 	}
 
 	for k, v := range trustedCerts {
 		if bytes.Compare(cert.Raw, v.Raw) == 0 {
-			logger.Debug("Found cert", log.Ctx{"k": k})
-			return true
+			logger.Debug("Found cert", log.Ctx{"name": k})
+			return true, k
 		}
 	}
 
-	return false
+	return false, ""
 }
 
 // IsRecursionRequest checks whether the given HTTP request is marked with the


More information about the lxc-devel mailing list