[lxc-devel] [lxd/master] lxd: Make pongo2 look inside the container

stgraber on Github lxc-bot at linuxcontainers.org
Sat Feb 17 01:26:32 UTC 2018


A non-text attachment was scrubbed...
Name: not available
Type: text/x-mailbox
Size: 370 bytes
Desc: not available
URL: <http://lists.linuxcontainers.org/pipermail/lxc-devel/attachments/20180217/96252ce0/attachment.bin>
-------------- next part --------------
From 979a7dedc7dfcdc97ca5e81355fee2bf5afd4973 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?St=C3=A9phane=20Graber?= <stgraber at ubuntu.com>
Date: Fri, 16 Feb 2018 20:25:26 -0500
Subject: [PATCH] lxd: Make pongo2 look inside the container
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Closes #4209

Signed-off-by: Stéphane Graber <stgraber at ubuntu.com>
---
 lxd/container_lxc.go           | 24 +++++++++++---------
 lxd/template/chroot.go         | 51 ++++++++++++++++++++++++++++++++++++++++++
 test/suites/static_analysis.sh |  1 +
 3 files changed, 66 insertions(+), 10 deletions(-)
 create mode 100644 lxd/template/chroot.go

diff --git a/lxd/container_lxc.go b/lxd/container_lxc.go
index 301c69b26..528468d25 100644
--- a/lxd/container_lxc.go
+++ b/lxd/container_lxc.go
@@ -20,13 +20,14 @@ import (
 	"syscall"
 	"time"
 
-	"gopkg.in/flosch/pongo2.v3"
+	"github.com/flosch/pongo2"
 	"gopkg.in/lxc/go-lxc.v2"
 	"gopkg.in/yaml.v2"
 
 	"github.com/lxc/lxd/lxd/db"
 	"github.com/lxc/lxd/lxd/maas"
 	"github.com/lxc/lxd/lxd/state"
+	"github.com/lxc/lxd/lxd/template"
 	"github.com/lxc/lxd/lxd/types"
 	"github.com/lxc/lxd/lxd/util"
 	"github.com/lxc/lxd/shared"
@@ -4998,12 +4999,12 @@ func (c *containerLXC) templateApplyNow(trigger string) error {
 	}
 
 	// Go through the templates
-	for templatePath, template := range metadata.Templates {
+	for tplPath, tpl := range metadata.Templates {
 		var w *os.File
 
 		// Check if the template should be applied now
 		found := false
-		for _, tplTrigger := range template.When {
+		for _, tplTrigger := range tpl.When {
 			if tplTrigger == trigger {
 				found = true
 				break
@@ -5015,9 +5016,9 @@ func (c *containerLXC) templateApplyNow(trigger string) error {
 		}
 
 		// Open the file to template, create if needed
-		fullpath := filepath.Join(c.RootfsPath(), strings.TrimLeft(templatePath, "/"))
+		fullpath := filepath.Join(c.RootfsPath(), strings.TrimLeft(tplPath, "/"))
 		if shared.PathExists(fullpath) {
-			if template.CreateOnly {
+			if tpl.CreateOnly {
 				continue
 			}
 
@@ -5059,12 +5060,15 @@ func (c *containerLXC) templateApplyNow(trigger string) error {
 		defer w.Close()
 
 		// Read the template
-		tplString, err := ioutil.ReadFile(filepath.Join(c.TemplatesPath(), template.Template))
+		tplString, err := ioutil.ReadFile(filepath.Join(c.TemplatesPath(), tpl.Template))
 		if err != nil {
 			return err
 		}
 
-		tpl, err := pongo2.FromString("{% autoescape off %}" + string(tplString) + "{% endautoescape %}")
+		// Restrict filesystem access to within the container's rootfs
+		tplSet := pongo2.NewSet(fmt.Sprintf("%s-%s", c.name, tpl.Template), template.ChrootLoader{Path: c.RootfsPath()})
+
+		tplRender, err := tplSet.FromString("{% autoescape off %}" + string(tplString) + "{% endautoescape %}")
 		if err != nil {
 			return err
 		}
@@ -5105,12 +5109,12 @@ func (c *containerLXC) templateApplyNow(trigger string) error {
 		}
 
 		// Render the template
-		tpl.ExecuteWriter(pongo2.Context{"trigger": trigger,
-			"path":       templatePath,
+		tplRender.ExecuteWriter(pongo2.Context{"trigger": trigger,
+			"path":       tplPath,
 			"container":  containerMeta,
 			"config":     c.expandedConfig,
 			"devices":    c.expandedDevices,
-			"properties": template.Properties,
+			"properties": tpl.Properties,
 			"config_get": configGet}, w)
 	}
 
diff --git a/lxd/template/chroot.go b/lxd/template/chroot.go
new file mode 100644
index 000000000..94a11e1db
--- /dev/null
+++ b/lxd/template/chroot.go
@@ -0,0 +1,51 @@
+package template
+
+import (
+	"bytes"
+	"fmt"
+	"io"
+	"io/ioutil"
+	"path/filepath"
+	"strings"
+)
+
+// ChrootLoader is a pong2 compatible file loader which restricts all accesses to a directory
+type ChrootLoader struct {
+	Path string
+}
+
+// Abs resolves a filename relative to the base directory. Absolute paths are allowed.
+// When there's no base dir set, the absolute path to the filename
+// will be calculated based on either the provided base directory (which
+// might be a path of a template which includes another template) or
+// the current working directory.
+func (l ChrootLoader) Abs(base string, name string) string {
+	return filepath.Clean(fmt.Sprintf("%s/%s", l.Path, name))
+}
+
+// Get reads the path's content from your local filesystem.
+func (l ChrootLoader) Get(path string) (io.Reader, error) {
+	// Get the full path
+	path, err := filepath.EvalSymlinks(path)
+	if err != nil {
+		return nil, err
+	}
+
+	basePath, err := filepath.EvalSymlinks(l.Path)
+	if err != nil {
+		return nil, err
+	}
+
+	// Validate that we're under the expected prefix
+	if !strings.HasPrefix(path, basePath) {
+		return nil, fmt.Errorf("Attempting to access a file outside the container")
+	}
+
+	// Open and read the file
+	buf, err := ioutil.ReadFile(path)
+	if err != nil {
+		return nil, err
+	}
+
+	return bytes.NewReader(buf), nil
+}
diff --git a/test/suites/static_analysis.sh b/test/suites/static_analysis.sh
index 425287a42..91c3ab5a6 100644
--- a/test/suites/static_analysis.sh
+++ b/test/suites/static_analysis.sh
@@ -57,6 +57,7 @@ test_static_analysis() {
     if which golint >/dev/null 2>&1; then
       golint -set_exit_status client/
       golint -set_exit_status lxc/config/
+      golint -set_exit_status lxd/template/
       golint -set_exit_status shared/api/
       golint -set_exit_status shared/cancel/
       golint -set_exit_status shared/cmd/


More information about the lxc-devel mailing list