I am using Grafana 9.1.2 version on Ubuntu 22.04 operating system. I am trying to achieve embed a panel from the dashboard into the Power BI. I have enabled all the required configurations to make iframe embedding available and it works perfectly fine on an HTML static page. But then I encountered the CORS issue when I integrated Power BI with the Grafana dashboard panel which is also resolved by running Grafana behind a reverse proxy using Nginx.
This was working fine as I have successfully embedded my company website URL in Power BI’s iframe.
This is the Grafana configuration.
content_security_policy = true
allow_embedding = true
enabled = true
org_name = Kube Network
org_role = Viewer
hide_version = true
This is the Nginx configuration.
server {
listen 443 ssl;
ssl_certificate /etc/grafana/abc.crt;
ssl_certificate_key /etc/grafana/def.key;
server_name monitoring.*****.net;
listen 80;
access_log /var/log/nginx/grafana.log;
location / {
add_header Access-Control-Allow-Origin "*";
add_header "Access-Control-Allow-Credentials" "true";
add_header "Access-Control-Allow-Methods" "GET, POST, OPTIONS";
add_header "Access-Control-Allow-Headers" "Authorization, origin, accept";
proxy_pass http://localhost:3000;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host:$server_port;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Not showing any error in Grafana logs nor Nginx logs. But I caught this error in the Power BI web interface in Chrome. Does anyone have any clue what is going on in my scenario?
There can be many problems, but one obvious:
add_header Access-Control-Allow-Origin "*";
See doc https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Headers
In requests with credentials, it is treated as the literal header name "*" without special semantics.
So you allowed only URL '*', not any URL (wildchar URL). That doesn't make sense. Allow your origin explicitly. Keep in mind Origin != URL. See Origin definition https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Origin
Related
I'm having issues with what i believe is my nginx.conf which is causing the instance to be restarted again and again, as the health-checks fail on my managed container service.
I'm running my setup in AWS Lightsail Containers, where I have three containers running:
nginx
django
nextjs
When publishing a new release on my AWS Lightsail instance it runs fine for a few minutes, then I hit a 503 error, which causes the instance to reboot - run a few minutes then reboot again.
Looking at the logs I can see that the health-check failed, and django throws and error saying that I should add the request IP to the allowed hosts:
[28/Aug/2021:13:56:23] Invalid HTTP_HOST header: 'x.x.x.x'. You may need to add 'x.x.x.x' to ALLOWED_HOSTS.
[28/Aug/2021:13:56:23] Bad Request: /health.txt
The problem is that my lightsail container service does not have a static IP (nor do I believe I can get a static IP).
My current nginx.conf is below (feedback is appreciated). My question here is how should I deal with this issue? I feel like setting ALLOWED_HOSTS = ['*'] is not a great approach. Can I hardcode the host for the healthcheck or similar?
nginx.conf:
upstream backend {
server ${BACKEND_HOST}:${BACKEND_PORT};
}
upstream frontend {
server ${FRONTEND_HOST}:${FRONTEND_PORT};
}
server {
listen 80 default_server;
server_name example.com;
server_tokens off;
gzip on;
gzip_proxied any;
gzip_comp_level 4;
gzip_types text/css application/javascript image/svg+xml;
location /robots.txt {
include proxy_params;
proxy_pass http://backend;
}
location /health.txt {
include proxy_params;
proxy_pass http://backend;
}
location /api {
include proxy_params;
proxy_pass http://backend;
}
location /admin {
include proxy_params;
proxy_pass http://backend;
}
location / {
proxy_pass http://frontend;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
proxy_set_header Host $host;
proxy_cache_bypass $http_upgrade;
}
}
I use AWS EC2, to pass the health check I get the ip of the instance dynamically and then I insert it into ALLOWED_HOSTS (I think it should work also for Lightsail Containers):
import requests
def get_instance_ip():
try:
ip = requests.get('http://169.254.169.254/latest/meta-data/local-ipv4').text
except requests.exceptions.ConnectionError:
return None
return ip
AWS_IP = get_ec2_instance_ip()
if AWS_IP is not None:
ALLOWED_HOSTS += [AWS_IP]
You can also create a middleware that always returns a 200 status code for the path used by health check (insert the custom middleware before django.middleware.security.SecurityMiddleware in MIDDLEWARE to avoid Invalid HTTP_HOST header error).
I am running Django + Channels server using Daphne. Daphne server is behind Nginx. My Nginx config looks like as given at end.
When I try to connect to ws://example.com/ws/endpoint I am getting NOT FOUNT /ws/endpoint error.
For me, it looks like Daphne is using protocol to route to either Django views or Channels app. If it sees http it routes to Django view and when it sees ws it routes to Channels app.
With following Nginx proxy pass configuration the URL always has http protocol prefix. So I am getting 404 or NOT FOUND in logs. If I change proxy_pass prefix to ws Nginx config fails.
What is the ideal way to setup Channels in the this scenario?
server {
listen 443 ssl;
server_name example.com
location / {
# prevents 502 bad gateway error
proxy_buffers 8 32k;
proxy_buffer_size 64k;
# redirect all HTTP traffic to localhost:8088;
proxy_pass http://0.0.0.0:8000/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
#proxy_set_header X-NginX-Proxy true;
# enables WS support
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 999999999;
}
}
Yes, as in the question Channels detects the route based on the protocol header ws or http/https
Using ws prefix in proxy_pass http://0.0.0.0:8000/; is not possible. To forward the protocol information following config should be included.
proxy_set_header X-Forwarded-Proto $scheme;
This will forward the schema/protocol(ws) information to Channels app. And channels routes according to this information.
Goal: I want to serve django-rest-framework(drf) responses over https.
I'm trying to understand why my setup isn't working, whether it can be fixed by a simple settings change in one of the components, or if I should try a different approach.
I have a drf application served by gunicorn. Gunicorn is behind an NGINX proxy. I also have some static content served by NGINX. This setup has worked fine for plain old http.
Then I set NGINX to listen over ssl (,and installed certs, etc.). The static content works over https. I can still get to drf via http, but I get nothing / timed out over https. The gunicorn logs haven't been that helpful to me, but django dev server gives "dev server doesn't work over https" when sent a request.
Here is my first attempt at the nginx config (anonymized). This was mostly from reading the gunicorn and nginx manuals.
server {
server_name example.com;
listen 443 ssl;
ssl_certificate /path/to/cert;
ssl_certificate_key /path/to/key;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers ...
location /static/{
alias /path/to/static/content/;
}
location / {
return 301 $scheme://example.com/static;
}
location /drf/{
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Protocol $scheme;
proxy_set_header X-Real_IP $remote_addr;
proxy_redirect off;
proxy_buffering off;
proxy_pass http://127.0.0.1:6565; # bound to gunicorn, can be reached from http://example.com:6565 - ideally want to be available from https://example.com:6565
}
}
server {
listen 80;
server_name example.com;
return 301 https://example.com
}
I don't think there is an additional step for gunicorn. On the DRF side, I read the relevant Django security section and tried using the SECURE_SSL_REDIRECT = True (new in 1.8 - not addressed in a lot of existing questions) and SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https').
I also tried with a redirect, but this was sort of a shot in the dark:
...after `proxy_pass` above, and commenting out proxy_redirect off:
proxy_redirect http://127.0.0.1:6565 https://example.com/drf
Ubuntu 14, Gunicorn 19, Nginx 1.1,
Django 1.8 (DRF 3.2)
Update:
NGINX Error logging:
I get nothing if I go to https://example.com:6565. Here is an error from nginx's error log for a request to https://example.com/drf/endpoint.
2015/09/08 13:53:52 [error] 12564#0: *14 connect() failed (111: Connection refused while connecting to upstream, client: 155.xxx.xxx.xx, server:example.com, request:"GET /drf/endpoint", upstream: "http://127.0.0.1:6565/drf/endpoint", host: "example.com"
After I deployed my Django App last night I got tons of strange Emails saying:
ERROR: Invalid HTTP_HOST header: '/webapps/example_com/run/gunicorn.sock
I'm sure this is somehow related to the following nginx config:
upstream example_app_server {
server unix:/webapps/example_com/run/gunicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name example.com;
client_max_body_size 4G;
access_log /webapps/example_com/logs/nginx-access.log;
error_log /webapps/example_com/logs/nginx-error.log;
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
if (!-f $request_filename) {
proxy_pass http://example_app_server;
break;
}
}
}
I found the answer to my my question in a django bug report.
proxy_set_header Host $http_host;
has to be replaced with:
proxy_set_header Host $host;
to make nginx pass the correct headers from that on instead of the gunicorn socket the requested page was in the django alerts.
This person explains a bit more what is going on based on this very same post. Here's his/her explanation:
...when a request is made to the server and the HTTP Host is empty, nginx sets the HTTP host to the gunicorn sock.
I can generate this error using curl:
curl -H "HOST:" MY_DOMAIN_NAME -0 -v
This sends a request without a HTTP Host. The -0 causes curl to use HTTP version 1.0. If you do not set this, the request will use HTTP version 1.1, which will cause the request to be rejected immediately and not generate the error.
The solution is to replace $http_host with $host (as pointed out on Stackoverflow). When HTTP Host is missing, $host will take on the value of the “server_name” directive. This is a valid domain name and is the one that should be used.
Add this in your settings.py file:
from django.http.request import HttpRequest
HttpRequest.get_host = HttpRequest._get_raw_host
I would like nginx set an appropriate accept-language header depending on requested domain:
www.domain.ru set ru-RU
www.domain.com set en-US
www.domain.de set de-DE
www.domain.eu do nothing let Django get the header from the browser.
For 3 specified above domains force changing of the accept-language header even
if english user enters www.domain.ru (force it to use russian language).
Here's my nginx config:
server {
listen 1.1.1.1;
server_name domain.eu www.domain.eu domain.de www.domain.de domain.com www.domain.com domain.ru www.domain.ru;
if($host ~* (.*)\.ru) {
set $http_accept_language 'ru-RU';
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_connect_timeout 10;
proxy_read_timeout 10;
proxy_pass http://127.0.0.1:8888/;
}
}
This is a part of my config (running Django via gunicorn).
Django checks accept-language header if session language is not set.
try
if ($host ~* \.ru$) {
set $language 'ru-RU';
}
add_header Accept-Language $language;
setting the variable with $http_.... is probably not the best idea as variables starting with $http_ are interpreted and set with by nginx itself (specifically the name you used would mean 'content of the http header 'accept_language' see http://wiki.nginx.org/HttpCoreModule#Variables). I'm not sure whether your set or nginx's would win but why play with fire?