Only allow iframes to be loaded from our domain
This commit is contained in:
parent
911c439858
commit
bae28c5f57
3 changed files with 78 additions and 1 deletions
|
@ -33,6 +33,13 @@ func (g DashboardHandler) Append(router *mux.Router) {
|
||||||
Handler(http.StripPrefix("/dashboard/", http.FileServer(g.Assets)))
|
Handler(http.StripPrefix("/dashboard/", http.FileServer(g.Assets)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (g DashboardHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// allow iframes from our domains only
|
||||||
|
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/frame-src
|
||||||
|
w.Header().Set("Content-Security-Policy", "frame-src 'self' https://traefik.io https://*.traefik.io;")
|
||||||
|
http.FileServer(g.Assets).ServeHTTP(w, r)
|
||||||
|
}
|
||||||
|
|
||||||
func safePrefix(req *http.Request) string {
|
func safePrefix(req *http.Request) string {
|
||||||
prefix := req.Header.Get("X-Forwarded-Prefix")
|
prefix := req.Header.Get("X-Forwarded-Prefix")
|
||||||
if prefix == "" {
|
if prefix == "" {
|
||||||
|
|
|
@ -1,9 +1,12 @@
|
||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
assetfs "github.com/elazarl/go-bindata-assetfs"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
@ -52,3 +55,70 @@ func Test_safePrefix(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_ContentSecurityPolicy(t *testing.T) {
|
||||||
|
testCases := []struct {
|
||||||
|
desc string
|
||||||
|
handler DashboardHandler
|
||||||
|
expected int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
desc: "OK",
|
||||||
|
handler: DashboardHandler{
|
||||||
|
Assets: &assetfs.AssetFS{
|
||||||
|
Asset: func(path string) ([]byte, error) {
|
||||||
|
return []byte{}, nil
|
||||||
|
},
|
||||||
|
AssetDir: func(path string) ([]string, error) {
|
||||||
|
return []string{}, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: http.StatusOK,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Not found",
|
||||||
|
handler: DashboardHandler{
|
||||||
|
Assets: &assetfs.AssetFS{
|
||||||
|
Asset: func(path string) ([]byte, error) {
|
||||||
|
return []byte{}, fmt.Errorf("not found")
|
||||||
|
},
|
||||||
|
AssetDir: func(path string) ([]string, error) {
|
||||||
|
return []string{}, fmt.Errorf("not found")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: http.StatusNotFound,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
desc: "Internal server error",
|
||||||
|
handler: DashboardHandler{
|
||||||
|
Assets: &assetfs.AssetFS{
|
||||||
|
Asset: func(path string) ([]byte, error) {
|
||||||
|
return []byte{}, fmt.Errorf("oops")
|
||||||
|
},
|
||||||
|
AssetDir: func(path string) ([]string, error) {
|
||||||
|
return []string{}, fmt.Errorf("oops")
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
expected: http.StatusInternalServerError,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testCases {
|
||||||
|
test := test
|
||||||
|
t.Run(test.desc, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
req := httptest.NewRequest(http.MethodGet, "/foobar.html", nil)
|
||||||
|
|
||||||
|
rw := httptest.NewRecorder()
|
||||||
|
|
||||||
|
test.handler.ServeHTTP(rw, req)
|
||||||
|
|
||||||
|
assert.Equal(t, test.expected, rw.Code)
|
||||||
|
assert.Equal(t, "frame-src 'self' https://traefik.io https://*.traefik.io;", rw.Result().Header.Get("Content-Security-Policy"))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -39,7 +39,7 @@ func NewManagerFactory(staticConfiguration static.Configuration, routinesPool *s
|
||||||
factory.api = api.NewBuilder(staticConfiguration)
|
factory.api = api.NewBuilder(staticConfiguration)
|
||||||
|
|
||||||
if staticConfiguration.API.Dashboard {
|
if staticConfiguration.API.Dashboard {
|
||||||
factory.dashboardHandler = http.FileServer(staticConfiguration.API.DashboardAssets)
|
factory.dashboardHandler = api.DashboardHandler{Assets: staticConfiguration.API.DashboardAssets}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue