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
--- 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