ansible/hg-web: define liberal CSP policy for reftest analyzer (bug 1333929); r?fubar draft
authorGregory Szorc <gps@mozilla.com>
Wed, 25 Jan 2017 17:23:23 -0800
changeset 10243 dceaff284b63efa140abd28a234c6aeb9ba4fb84
parent 10241 8101ecb97baaa289cd760601ca36dbacd75cac09
push id1485
push userbmo:gps@mozilla.com
push dateThu, 26 Jan 2017 02:32:53 +0000
reviewersfubar
bugs1333929, 1200501
ansible/hg-web: define liberal CSP policy for reftest analyzer (bug 1333929); r?fubar Reftest analyzer loads images from "data:" URIs. Yuck. Until bug 1200501 is in place, we have to host the reftest analyzer on hg.mo. This commit refactors the httpd code to define a separate, more liberal CSP policy for the reftest analyzer from the rest of the site. As part of this, the default CSP policy became more secure by dropping some connect-src items! There are multiple repositories with copies of the reftest analyzer. However, an audit of the HTTP logs revealed 99+% of them hit the copy in mozilla-central. So the URL check only looks for this instance. The httpd config is a bit ugly. As the inline comments explain, this is necessary because things like nested <If> and <Location> inside <If> don't work. Since the config code is now non-trivial and security is important, I added a test for the CSP header. MozReview-Commit-ID: 4MdsjRjrPam
ansible/roles/hg-web/templates/vhost.conf.j2
hgserver/tests/test-csp.t
--- a/ansible/roles/hg-web/templates/vhost.conf.j2
+++ b/ansible/roles/hg-web/templates/vhost.conf.j2
@@ -53,24 +53,46 @@ LimitRequestFields 1000
         {% endfor %}
     </Location>
 
     #LogLevel debug
     LogFormat "%h %v %u %t \"%r\" %>s %b %D \"%{Referer}i\" \"%{User-Agent}i\" \"%{Cookie}i\""
     ErrorLog "/var/log/httpd/hg.mozilla.org/error_log"
     CustomLog "/var/log/httpd/hg.mozilla.org/access_log" combined env=!image
 
-    # Enable HSTS and CSP. But don't enable for wire protocol requests, as they
-    # are not browsers and it just adds avoidable overhead.
-    <If "(%{QUERY_STRING} !~ /cmd=/) && (%{HTTP_USER_AGENT} !~ m#mercurial/proto#)">
+    # Nested <If> doesn't work reliably. <Location> also doesn't work inside
+    # <If>. So our strategy for complex conditionals is to set an environment
+    # variable then do vanilla <If> or <If> inside <Location>.
+
+    # Set a variable for Mercurial agent and wire protocol requests
+    SetEnvIf User-Agent mercurial\/proto HG_PROTOCOL=1
+    # SetEnvIf can't access the query string. Yes, really. So use mod_rewrite.
+    RewriteCond %{QUERY_STRING} cmd= [NC]
+    RewriteRule ^ - [E=HG_PROTOCOL:1]
+
+    # Enable HSTS unless for HG clients.
+    <If "-z env('HG_PROTOCOL')">
         Header set Strict-Transport-Security max-age=31536000
+    </If>
+
+    # Enable CSP unless for HG clients.
+    <Location "/">
         # bugzilla.mo is for l10n
-        # archive.mo, public-artifacts.taskcluster.net, and
-        # queue.taskcluster.net are for reftest analyzer
-        Header set Content-Security-Policy "default-src 'none'; connect-src 'self' https://bugzilla.mozilla.org/ https://archive.mozilla.org/ https://public-artifacts.taskcluster.net/ https://queue.taskcluster.net/; img-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
-    </If>
+        # TODO find out URL for l10n tools needing this and restrict to that.
+        <If "-z env('HG_PROTOCOL')">
+            Header set Content-Security-Policy "default-src 'none'; connect-src 'self' https://bugzilla.mozilla.org/; img-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
+        </If>
+    </Location>
+
+    # Reftest analyzer needs a lot of exceptions to work. Bug 1200501
+    # tracks not hosting it from VCS.
+    <Location "/mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml">
+        <If "-z env('HG_PROTOCOL')">
+            Header set Content-Security-Policy "default-src 'none'; connect-src 'self' https://archive.mozilla.org/ https://public-artifacts.taskcluster.net/ https://queue.taskcluster.net/; img-src 'self' data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'"
+        </If>
+    </Location>
 </VirtualHost>
 
 # Local variables:
 # mode: apache
 # tab-width: 4
 # indent-tabs-mode: nil
 # end:
new file mode 100644
--- /dev/null
+++ b/hgserver/tests/test-csp.t
@@ -0,0 +1,46 @@
+#require hgmodocker
+
+  $ . $TESTDIR/hgserver/tests/helpers.sh
+  $ hgmoenv
+
+  $ hgmo create-repo mozilla-central scm_level_3
+  (recorded repository creation in replication log)
+
+CSP header should be present on normal HTTP requests
+
+  $ http ${HGWEB_0_URL}mozilla-central --no-body --header content-security-policy
+  200
+  content-security-policy: default-src 'none'; connect-src 'self' https://bugzilla.mozilla.org/; img-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
+
+CSP header absent on protocol requests
+
+  $ http ${HGWEB_0_URL}mozilla-central?cmd=capabilities --no-body --header content-security-policy
+  200
+
+CSP header absent from Mercurial user agents
+
+  $ http ${HGWEB_0_URL}mozilla-central --agent 'mercurial/proto-1.0' --no-body --header content-security-policy
+  200
+
+  $ http ${HGWEB_0_URL}mozilla-central --agent 'mercurial/proto-1.0 (Mercurial 3.9)' --no-body --header content-security-policy
+  200
+
+CSP header absent if both conditions are true
+
+  $ http ${HGWEB_0_URL}mozilla-central?cmd=capabilities --agent 'mercurial/proto-1.0' --no-body --header content-security-policy
+  200
+
+reftest analyzer is a special snowflake
+
+  $ http ${HGWEB_0_URL}mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml --no-body --header content-security-policy
+  200
+  content-security-policy: default-src 'none'; connect-src 'self' https://archive.mozilla.org/ https://public-artifacts.taskcluster.net/ https://queue.taskcluster.net/; img-src 'self' data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
+
+  $ http "${HGWEB_0_URL}mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml#logurl=https://queue.taskcluster.net/v1/task/KQYN-Sa9TBmXR3m8GaXXwg/runs/0/artifacts/public/logs/live_backing.log&only_show_unexpected=1" --no-body --header content-security-policy
+  200
+  content-security-policy: default-src 'none'; connect-src 'self' https://archive.mozilla.org/ https://public-artifacts.taskcluster.net/ https://queue.taskcluster.net/; img-src 'self' data:; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'
+
+No CSP if HG user-agent
+
+  $ http ${HGWEB_0_URL}mozilla-central/raw-file/tip/layout/tools/reftest/reftest-analyzer.xhtml --agent 'mercurial/proto-1.0' --no-body --header content-security-policy
+  200