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:
commit
5b7fad0bef
|
@ -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
|
||||||
# ------------------------------------------------------------------------------
|
# ------------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
Hardened security thanks to CSP and additional HTTP headers (#880)
|
|
@ -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``.
|
||||||
|
|
|
@ -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/;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
3
dev.yml
3
dev.yml
|
@ -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
|
||||||
|
|
|
@ -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/;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 }
|
||||||
})
|
})
|
||||||
|
|
|
@ -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 }
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue