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
