[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