Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Openresty grafana #7

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docker/.env
Original file line number Diff line number Diff line change
Expand Up @@ -190,3 +190,6 @@ MF_TWINS_CHANNEL_ID=
MF_TWINS_CACHE_URL=es-redis:6379
MF_TWINS_CACHE_PASS=
MF_TWINS_CACHE_DB=0

# Grafana
GF_SERVER_ROOT_URL=http://localhost/grafana/
25 changes: 16 additions & 9 deletions docker/configs/grafana-defaults.ini
Original file line number Diff line number Diff line change
Expand Up @@ -45,13 +45,13 @@ domain = localhost
enforce_domain = false

# The full public facing url
root_url = %(protocol)s://%(domain)s:%(http_port)s/
root_url = %(protocol)s://%(domain)s:%(http_port)s/grafana

# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
serve_from_sub_path = false
serve_from_sub_path = true

# Log web requests
router_logging = false
router_logging = true

# the path relative working path
static_root_path = public
Expand Down Expand Up @@ -124,7 +124,7 @@ connstr =
[dataproxy]

# This enables data proxy logging, default is false
logging = false
logging = true

# How long the data proxy waits before timing out, default is 30 seconds.
# This setting also applies to core backend HTTP data sources where query requests use an HTTP client with timeout set.
Expand Down Expand Up @@ -202,7 +202,7 @@ cookie_secure = false
cookie_samesite = lax

# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
allow_embedding = false
allow_embedding = true

# Set to true if you want to enable http strict transport security (HSTS) response header.
# This is only sent when HTTPS is enabled in this configuration.
Expand All @@ -225,7 +225,7 @@ x_content_type_options = true

# Set to true to enable the X-XSS-Protection header, which tells browsers to stop pages from loading
# when they detect reflected cross-site scripting (XSS) attacks.
x_xss_protection = true
x_xss_protection = false


#################################### Snapshots ###########################
Expand Down Expand Up @@ -456,12 +456,19 @@ tls_client_key =
tls_client_ca =

#################################### Basic Auth ##########################
# we will disable basic auth, as otherwise Mainflux Authorization header would make
# a problem in Grafana, or we would have to rewrite it in nginx configuration
# before proxying request to grafana
[auth.basic]
enabled = true
enabled = false


#################################### Auth Proxy ##########################
# We will enable proxy as mainflux will serve as proxy
# Mainflux will use authorization header, and if user is authenticated
# it will forward user into the X-WEBAUTH-USER header
[auth.proxy]
enabled = false
enabled = true
header_name = X-WEBAUTH-USER
header_property = username
auto_sign_up = true
Expand Down Expand Up @@ -509,7 +516,7 @@ templates_pattern = emails/*.html
mode = console file

# Either "debug", "info", "warn", "error", "critical", default is "info"
level = info
level = debug

# optional settings to set different levels for specific loggers. Ex filters = sqlstore:debug
filters =
Expand Down
16 changes: 11 additions & 5 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,16 +37,18 @@ services:
MF_UI_PORT: ${MF_UI_PORT}
MF_UI_MQTT_WS_URL: ${MF_UI_MQTT_WS_URL}
command: /entrypoint.sh

nginx:
image: nginx:1.16.0-alpine
image: mainflux-nginx:latest
container_name: mainflux-nginx
tty: true
restart: on-failure
volumes:
- ./nginx/nginx-${AUTH-key}.conf:/etc/nginx/nginx.conf.template
- ./nginx/nginx-${AUTH-key}.conf:/usr/local/openresty/nginx/conf/nginx.conf.template
- ./nginx/nginx.conf:/usr/local/openresty/nginx/conf/nginx.conf
- ./nginx/entrypoint.sh:/entrypoint.sh
- ./nginx/snippets:/etc/nginx/snippets
- ./ssl/authorization.js:/etc/nginx/authorization.js
- ./nginx/snippets:/usr/local/openresty/nginx/conf/snippets
- ./ssl/authorization.js:/usr/local/openresty/nginx/conf/authorization.js
- ./ssl/certs/mainflux-server.crt:/etc/ssl/certs/mainflux-server.crt
- ./ssl/certs/ca.crt:/etc/ssl/certs/ca.crt
- ./ssl/certs/mainflux-server.key:/etc/ssl/private/mainflux-server.key
Expand Down Expand Up @@ -129,6 +131,8 @@ services:
- mainflux-base-net
volumes:
- mainflux-users-db-volume:/var/lib/postgresql/data
ports:
- 6002:5432

users:
image: mainflux/users:latest
Expand Down Expand Up @@ -178,6 +182,8 @@ services:
- mainflux-base-net
volumes:
- mainflux-things-db-volume:/var/lib/postgresql/data
ports:
- 6001:5432

auth-redis:
image: redis:5.0-alpine
Expand Down
14 changes: 14 additions & 0 deletions docker/nginx/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
FROM openresty/openresty:alpine-fat

RUN mkdir -p /var/log/nginx && \
ln -sf /dev/stdout /var/log/nginx/access.log && \
ln -sf /dev/stderr /var/log/nginx/error.log

RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-http
RUN /usr/local/openresty/luajit/bin/luarocks install lua-cjson
RUN /usr/local/openresty/luajit/bin/luarocks install lua-resty-session


RUN mkdir -p /data/nginx/cache/ui

ENTRYPOINT ["/entrypoint.sh"]
13 changes: 7 additions & 6 deletions docker/nginx/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@

if [ -z "$MF_MQTT_CLUSTER" ]
then
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /etc/nginx/snippets/mqtt-upstream-single.conf > /etc/nginx/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-single.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-upstream-single.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream-single.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream.conf
else
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /etc/nginx/snippets/mqtt-upstream-cluster.conf > /etc/nginx/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /etc/nginx/snippets/mqtt-ws-upstream-cluster.conf > /etc/nginx/snippets/mqtt-ws-upstream.conf
envsubst '${MF_MQTT_ADAPTER_MQTT_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-upstream-cluster.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-upstream.conf
envsubst '${MF_MQTT_ADAPTER_WS_PORT}' < /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream-cluster.conf > /usr/local/openresty/nginx/conf/snippets/mqtt-ws-upstream.conf
fi

envsubst '
Expand All @@ -22,6 +22,7 @@ envsubst '
${MF_INFLUX_READER_PORT}
${MF_BOOTSTRAP_PORT}
${MF_TWINS_HTTP_PORT}
${MF_OPCUA_ADAPTER_HTTP_PORT}' < /etc/nginx/nginx.conf.template > /etc/nginx/nginx.conf
${MF_OPCUA_ADAPTER_HTTP_PORT}' < /usr/local/openresty/nginx/conf/nginx.conf.template > /usr/local/openresty/nginx/conf/mfx-nginx.conf

exec nginx -g "daemon off;"

exec /usr/local/openresty/bin/openresty -g "daemon off;"
177 changes: 117 additions & 60 deletions docker/nginx/nginx-key.conf
Original file line number Diff line number Diff line change
Expand Up @@ -3,35 +3,7 @@

# This is the default Mainflux NGINX configuration.

user nginx;
worker_processes auto;
worker_cpu_affinity auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;

events {
# Explanation: https://serverfault.com/questions/787919/optimal-value-for-nginx-worker-connections
# We'll keep 10k connections per core (assuming one worker per core)
worker_connections 10000;
}

http {
include snippets/http_access_log.conf;

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;

include /etc/nginx/mime.types;
default_type application/octet-stream;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;

# Include single-node or multiple-node (cluster) upstream
include snippets/mqtt-ws-upstream.conf;

server {
listen 80 default_server;
Expand All @@ -42,7 +14,7 @@ http {
include snippets/ssl.conf;

add_header Strict-Transport-Security "max-age=63072000; includeSubdomains";
add_header X-Frame-Options DENY;
add_header X-Frame-Options SAMEORIGIN;
add_header X-Content-Type-Options nosniff;
add_header Access-Control-Allow-Origin '*';
add_header Access-Control-Allow-Methods '*';
Expand All @@ -51,36 +23,75 @@ http {
server_name localhost;

# Proxy pass to users service
location ~ ^/(users|tokens|password) {
location ~ ^/(users|password) {
include snippets/proxy-headers.conf;
proxy_pass http://users:${MF_USERS_HTTP_PORT};
}

# Proxy pass to things service
location ~ ^/(things|channels|connect) {
# Proxy pass to users service
location /tokens {
include snippets/proxy-headers.conf;
add_header Access-Control-Expose-Headers Location;
proxy_pass http://things:${MF_THINGS_HTTP_PORT};
# This block will save retrieved token into session
# upon succsessfull authentication
# it uses helper endpoint _tokens since I wasnt able
# to parse response and set jwt inside body_filter_by_lua
# so I use content_by_lua to proxy request to _token
# parse the response to set the jwt inside session
# and retrieve response from _tokens as is.
content_by_lua_block {
ngx.req.read_body()
local res = ngx.location.capture(
"/_tokens",
{always_forward_body = true, method = ngx.HTTP_POST})
if res.status == 201 then
local cjson = require "cjson"
local body = res.body
local value = cjson.decode(body)
ngx.status = res.status
local session = require "resty.session".open()
if not session then
session = require "resty.session".start()
end
session.data.jwt = value.token
session:save()
ngx.print(res.body)
ngx.exit(res.status)
else
ngx.log(ngx.ERR, "STATUS ERR", res.status)
ngx.print(res.body)
end
ngx.exit(501)
}
}

location ~ ^/(identify){
location /_tokens {
include snippets/proxy-headers.conf;
proxy_pass http://users:${MF_USERS_HTTP_PORT}/tokens;
}


location /groups/users/ {
include snippets/proxy-headers.conf;
proxy_pass http://users:${MF_USERS_HTTP_PORT}/groups/;
}

# Proxy pass to things service
location ~ ^/(things|channels|connect) {
include snippets/proxy-headers.conf;
add_header Access-Control-Expose-Headers Location;
proxy_pass http://things:${MF_THINGS_AUTH_HTTP_PORT};
proxy_pass http://things:${MF_THINGS_HTTP_PORT};
}

# Proxy pass for groups to things service
location ^~ /groups/things/ {
location /groups/things/ {
include snippets/proxy-headers.conf;
add_header Access-Control-Expose-Headers Location;
proxy_pass http://things:${MF_THINGS_HTTP_PORT}/groups/;
}

# Proxy pass for groups to users service
location ^~ /groups/users/ {
location ~ ^/(identify){
include snippets/proxy-headers.conf;
add_header Access-Control-Expose-Headers Location;
proxy_pass http://users:${MF_USERS_HTTP_PORT}/groups/;
proxy_pass http://things:${MF_THINGS_AUTH_HTTP_PORT};
}

location ~ ^/(groups|members|keys) {
Expand Down Expand Up @@ -139,26 +150,72 @@ http {
include snippets/proxy-headers.conf;
proxy_pass http://ui:${MF_UI_PORT};
}
}
}

# MQTT
stream {
include snippets/stream_access_log.conf;

# Include single-node or multiple-node (cluster) upstream
include snippets/mqtt-upstream.conf;

server {
listen 1883;
listen [::]:1883;
listen 8883 ssl;
listen [::]:8883 ssl;

include snippets/ssl.conf;
location /grafana {
include snippets/proxy-headers.conf;
access_by_lua_block {

local session = require "resty.session".open()
if not session then
ngx.log(ngx.ERR, "request failed, session not created")
ngx.exit(ngx.HTTP_FORBIDDEN)
end

if not session.data.jwt then
ngx.log(ngx.ERR, "request failed, session not authenticated")
ngx.exit(ngx.HTTP_FORBIDDEN)
end


local cjson = require "cjson"
-- Angular application is calling grafana url directly interceptor cannot be used
-- so Authorization header is not automatically appended.
-- We must set Authorization header, JWT we retrieve from session if exists.
ngx.req.set_header("Authorization", session.data.jwt)

-- When loading dashboard first URL will contain var-thing variable
-- so first we check if the user has access rights to thing and if
-- the dashboard for the thing is configured.
if ngx.req.get_uri_args()["var-thing"] then
local thingpath = "/things/" .. ngx.req.get_uri_args()["var-thing"]

local res = ngx.location.capture(thingpath, { method = ngx.HTTP_GET})

if not res then
ngx.log(ngx.ERR, "request failed: ", err)
ngx.exit(ngx.HTTP_FORBIDDEN)
end
if not res.status == ngx.HTTP_OK then
ngx.log(ngx.ERR, "request failed: ", err)
ngx.exit(res.status)
end

local thing = cjson.decode(res.body)
ngx.log(ngx.ERR, "THING ID: ", thing.id)
ngx.log(ngx.ERR, "THING DASH: ", thing.metadata.grafana.dashboard)
end

-- Further subsequent calls that grafana makes api, scripts loading will
-- need authentication, so we send user in X-WEBAUTH-USER header
-- we extract user using Authorization header and making a call
-- to /users/profile
local res = ngx.location.capture("/users/profile",
{ method = ngx.HTTP_GET })
if not res then
ngx.log(ngx.ERR, "request failed: ", err)
ngx.exit(ngx.HTTP_FORBIDDEN)
end

ngx.log(ngx.ERR, "USER:", res.body)
local user = cjson.decode(res.body)
ngx.log(ngx.ERR, "EMAIL:", user.email)
ngx.req.set_header("X-WEBAUTH-USER", user.email)
}

proxy_pass http://grafana:3000;
proxy_set_header Referer $scheme://$host/grafana;
}

proxy_pass mqtt_cluster;
}
}

error_log info.log info;

Loading