Merge branch '880-header-security' into 'develop'

Resolve "Improve the security via HTTP headers"

Closes #880

See merge request funkwhale/funkwhale!826
This commit is contained in:
Eliot Berriot 2019-07-18 11:11:26 +02:00
commit 5b7fad0bef
11 changed files with 126 additions and 11 deletions

View File

@ -222,14 +222,15 @@ INSTALLED_APPS = (
# MIDDLEWARE CONFIGURATION # MIDDLEWARE CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
MIDDLEWARE = ( MIDDLEWARE = (
"django.middleware.security.SecurityMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"corsheaders.middleware.CorsMiddleware",
"funkwhale_api.common.middleware.SPAFallbackMiddleware", "funkwhale_api.common.middleware.SPAFallbackMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware", "django.contrib.sessions.middleware.SessionMiddleware",
"corsheaders.middleware.CorsMiddleware",
"django.middleware.common.CommonMiddleware", "django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware", "django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
"funkwhale_api.users.middleware.RecordActivityMiddleware", "funkwhale_api.users.middleware.RecordActivityMiddleware",
) )
@ -398,6 +399,8 @@ ASGI_APPLICATION = "config.routing.application"
# This ensures that Django will be able to detect a secure connection # This ensures that Django will be able to detect a secure connection
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https") SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
# AUTHENTICATION CONFIGURATION # AUTHENTICATION CONFIGURATION
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------

View File

@ -0,0 +1 @@
Hardened security thanks to CSP and additional HTTP headers (#880)

View File

@ -43,3 +43,58 @@ Then, edit your ``/etc/systemd/system/funkwhale-server.service`` and replace the
``ExecStart=/srv/funkwhale/virtualenv/bin/gunicorn config.asgi:application -w ${FUNKWHALE_WEB_WORKERS} -k uvicorn.workers.UvicornWorker -b ${FUNKWHALE_API_IP}:${FUNKWHALE_API_PORT}`` ``ExecStart=/srv/funkwhale/virtualenv/bin/gunicorn config.asgi:application -w ${FUNKWHALE_WEB_WORKERS} -k uvicorn.workers.UvicornWorker -b ${FUNKWHALE_API_IP}:${FUNKWHALE_API_PORT}``
Then reload the configuration change with ``sudo systemctl daemon-reload`` and ``sudo systemctl restart funkwhale-server``. Then reload the configuration change with ``sudo systemctl daemon-reload`` and ``sudo systemctl restart funkwhale-server``.
Content-Security-Policy and additional security headers [manual action suggested]
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
To improve the security and reduce the attack surface in case of a successfull exploit, we suggest
you add the following Content-Security-Policy to your nginx configuration.
**On non-docker setups**, in ``/etc/nginx/sites-available/funkwhale.conf``::
server {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
location /front/ {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "SAMEORIGIN";
# … existing content here
}
# Also create a new location for the embeds to ensure external iframes work
# Simply copy-paste the /front/ location, but replace the following lines:
location /front/embed.html {
add_header X-Frame-Options "ALLOW";
alias ${FUNKWHALE_FRONTEND_PATH}/embed.html;
}
}
Then reload nginx with ``systemctl reload nginx``.
**On docker setups**, in ``/srv/funkwhalenginx/funkwhale.template``::
server {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
location /front/ {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "SAMEORIGIN";
# … existing content here
}
# Also create a new location for the embeds to ensure external iframes work
# Simply copy-paste the /front/ location, but replace the following lines:
location /front/embed.html {
add_header X-Frame-Options "ALLOW";
alias /frontent/embed.html;
}
}
Then reload nginx with ``docker-compose restart nginx``.

View File

@ -23,6 +23,10 @@ server {
root /frontend; root /frontend;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
location / { location / {
include /etc/nginx/funkwhale_proxy.conf; include /etc/nginx/funkwhale_proxy.conf;
# this is needed if you have file import via upload enabled # this is needed if you have file import via upload enabled
@ -31,12 +35,27 @@ server {
} }
location /front/ { location /front/ {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "ALLOW";
alias /frontend/; alias /frontend/;
expires 30d; expires 30d;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
location /front/embed.html {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "ALLOW";
alias /frontend/embed.html;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location /federation/ { location /federation/ {
include /etc/nginx/funkwhale_proxy.conf; include /etc/nginx/funkwhale_proxy.conf;
proxy_pass http://funkwhale-api/federation/; proxy_pass http://funkwhale-api/federation/;

View File

@ -29,6 +29,9 @@ server {
# HSTS # HSTS
add_header Strict-Transport-Security "max-age=31536000"; add_header Strict-Transport-Security "max-age=31536000";
# Security related headers
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
# compression settings # compression settings
gzip on; gzip on;
gzip_comp_level 5; gzip_comp_level 5;

View File

@ -41,6 +41,9 @@ server {
# HSTS # HSTS
add_header Strict-Transport-Security "max-age=31536000"; add_header Strict-Transport-Security "max-age=31536000";
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
root ${FUNKWHALE_FRONTEND_PATH}; root ${FUNKWHALE_FRONTEND_PATH};
# compression settings # compression settings
@ -78,11 +81,25 @@ server {
} }
location /front/ { location /front/ {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "SAMEORIGIN";
alias ${FUNKWHALE_FRONTEND_PATH}/; alias ${FUNKWHALE_FRONTEND_PATH}/;
expires 30d; expires 30d;
add_header Pragma public; add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate"; add_header Cache-Control "public, must-revalidate, proxy-revalidate";
} }
location /front/embed.html {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "ALLOW";
alias ${FUNKWHALE_FRONTEND_PATH}/embed.html;
expires 30d;
add_header Pragma public;
add_header Cache-Control "public, must-revalidate, proxy-revalidate";
}
location /federation/ { location /federation/ {
include /etc/nginx/funkwhale_proxy.conf; include /etc/nginx/funkwhale_proxy.conf;

View File

@ -49,7 +49,7 @@ services:
args: args:
install_dev_deps: 1 install_dev_deps: 1
entrypoint: compose/django/dev-entrypoint.sh entrypoint: compose/django/dev-entrypoint.sh
command: python /app/manage.py runserver 0.0.0.0:${FUNKWHALE_API_PORT-5000} command: uvicorn --reload config.asgi:application --host 0.0.0.0 --port 5000 --reload-dir config/ --reload-dir=funkwhale_api/
volumes: volumes:
- ./api:/app - ./api:/app
- "${MUSIC_DIRECTORY_SERVE_PATH-./data/music}:/music:ro" - "${MUSIC_DIRECTORY_SERVE_PATH-./data/music}:/music:ro"
@ -119,6 +119,7 @@ services:
- "${MUSIC_DIRECTORY_SERVE_PATH-./data/music}:/music:ro" - "${MUSIC_DIRECTORY_SERVE_PATH-./data/music}:/music:ro"
- ./deploy/funkwhale_proxy.conf:/etc/nginx/funkwhale_proxy.conf:ro - ./deploy/funkwhale_proxy.conf:/etc/nginx/funkwhale_proxy.conf:ro
- "${MEDIA_ROOT-./api/funkwhale_api/media}:/protected/media:ro" - "${MEDIA_ROOT-./api/funkwhale_api/media}:/protected/media:ro"
- "./front:/frontend:ro"
networks: networks:
- federation - federation
- internal - internal

View File

@ -69,9 +69,24 @@ http {
text/x-component text/x-component
text/x-cross-domain-policy; text/x-cross-domain-policy;
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
location /front/ { location /front/ {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "SAMEORIGIN";
# uncomment the following line and comment the proxy-pass one
# to use the frontend build with "yarn build"
#alias /frontend/dist/;
proxy_pass http://funkwhale-front/front/; proxy_pass http://funkwhale-front/front/;
} }
location /front/embed.html {
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; object-src 'none'; media-src 'self' data:";
add_header Referrer-Policy "strict-origin-when-cross-origin";
add_header X-Frame-Options "ALLOW";
proxy_pass http://funkwhale-front/front/embed.html;
}
location /front-server/ { location /front-server/ {
proxy_pass http://funkwhale-front/; proxy_pass http://funkwhale-front/;
} }

View File

@ -10,6 +10,8 @@ Vue.config.productionTip = false
/* eslint-disable no-new */ /* eslint-disable no-new */
new Vue({ new Vue({
el: '#app', el: '#app',
template: '<EmbedFrame/>', render (h) {
return h('EmbedFrame')
},
components: { EmbedFrame } components: { EmbedFrame }
}) })

View File

@ -122,7 +122,9 @@ store.dispatch('instance/fetchFrontSettings').finally(() => {
el: '#app', el: '#app',
router, router,
store, store,
template: '<App/>', render (h) {
return h('App')
},
components: { App } components: { App }
}) })

View File

@ -11,6 +11,7 @@ if (process.env.BUNDLE_ANALYZE === '1') {
} }
module.exports = { module.exports = {
baseUrl: process.env.BASE_URL || '/front/', baseUrl: process.env.BASE_URL || '/front/',
productionSourceMap: false,
pages: { pages: {
embed: { embed: {
entry: 'src/embed.js', entry: 'src/embed.js',
@ -30,11 +31,7 @@ module.exports = {
}, },
configureWebpack: { configureWebpack: {
plugins: plugins, plugins: plugins,
resolve: { devtool: false
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
}
}, },
devServer: { devServer: {
disableHostCheck: true, disableHostCheck: true,