The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/5538

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Signed-off-by: Stéphane Graber <[email protected]>
From 152cb6aa173379f33df84d22c9d349bbc340c1a1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <[email protected]>
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 <[email protected]>
---
 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
_______________________________________________
lxc-devel mailing list
[email protected]
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to