The following configurations should be done in order to embed Grafana dashboards directly in the GUI of a Spring based Java application deployed on Jboss.
Note that this is a mix of Java code, Jboss configurations and Grafana configurations that should be done so to be able to smoothly embed Grafana Dashboards.
The current example is using Jboss as an application server but the configuration can be done almost the same for a Spring Boot based application using nginx as a reverse proxy. At every step I will mention what is different for a Spring Boot based application.
STEP 1: Grafana configuration changes
All configurations are done under grafana.ini configuration file. Note that Grafana must be started with grafana.ini as a configuration file. You can start creating your configuration file using the sample.ini file that comes with Grafana install
./grafana-server -config $GRAFANA_HOME/conf/grafana.ini
Serve Grafana dashboards for anonymous users
We want to avoid having to login to Grafana using an API_KEY to make things easier. If we activate anonymous user access to Grafana dashboards we can still control the level of access (viewer only) and we can also segregate based on organizations at what Dashboards the anonymous user has access.
Under [auth.anonymous] section set the following values as bellow :
[auth.anonymous]
# enable anonymous access
enabled = true
# specify organization name that should be used for unauthenticated users
org_name = MyOrg
# specify role for unauthenticated users
org_role = Viewer
# mask the Grafana version number for unauthenticated users
hide_version = true
Under the Grafana main user interface as admin user go to Configuration -> Preferences
Then define there “MyOrg” as an organization, the same as defined under org_name in grafana.ini
All the dashboards that should be visible to our application should be created under this organization.
Configure Grafana to serve dashboards under a sub-path.
Under the [server] section set the following as bellow.
[server]
# Protocol (http, https, h2, socket)
protocol = http
...
# The http port to use
http_port = 3000
# The public facing domain name used to access grafana from a browser
domain = test.voina.org
...
# The full public facing url you use in browser, used for redirects and emails
# If you use reverse proxy and sub path specify full url (with sub path)
root_url = %(protocol)s://%(domain)s:%(http_port)s/myapp/grafana/
# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons.
serve_from_sub_path = true
# Log web requests
;router_logging = true
...
# enable gzip
enable_gzip = true
...
Note:
- http is enough as the resource will be behind a reverse_proxy so not directly exposed to the end user and the exposed port can be blocked by a firewall
- domain must match the name of the application server (test.voina.org)
- the suffix of the root_url must match the same path under which Grafana is served in Jboss (myapp/grafana/)
Configure Grafana security to allow embedding
Under [security] section set the following:
# set to true if you host Grafana behind HTTPS. default is false.
;cookie_secure = false
# set cookie SameSite attribute. defaults to `lax`. can be set to "lax", "strict", "none" and "disabled"
cookie_samesite = none
# set to true if you want to allow browsers to render Grafana in a <frame>, <iframe>, <embed> or <object>. default is false.
allow_embedding = true
STEP 2: Jboss Configuration changes
The following configurations are done under jboss-cli.
The first step is to add a reverse proxy handler to the Undertow subsystem
[standalone@localhost:9992 /] /subsystem=undertow/configuration=handler/reverse-proxy=grafana/:add(cached-connections-per-thread=5,connection-idle-timeout=60,connections-per-thread=10,max-request-time=-1,problem-server-retry=30,request-queue-size=10,session-cookie-names=JSESSIONID)
Define outbound socket bindings for remote hosts
Note that the service to which calls will be resolved is the Grafana server, so use the name and port of the Grafana server. By default http is used.
[standalone@localhost:9992 /] /socket-binding-group=standard-sockets/remote-destination-outbound-socket-binding=grafana/:add(host=test.voina.org,port=3000)
Add remote hosts to reverse proxy handler
Note that path is the sub path under which Grafana dashboards are served by Grafana service.
[standalone@localhost:9992 /] /subsystem=undertow/configuration=handler/reverse-proxy=grafana/host=grafana/:add(outbound-socket-binding=grafana,path=/myapp/grafana/)
Add handler to location in default-server
Note that the intercepted path that will be forwarded to the remote Grafana service is “/myapp/grafana/” the path under which Grafana dashboard will be embedded in GUI of our application and the same as the sub path under which Grafana dashboards are served by Grafana service.
[standalone@localhost:9992 /] /subsystem=undertow/server=default-server/host=default-host/location=\/myapp\/grafana\//:add(handler=grafana)
STEP 3: Application changes
Define a GrafanaResponseFilter implementation of javax.servlet.Filter that will put a simple extra header to allow Grafana dashboards to be embedded in IFRAME.
The only important method is the doFilter method that sets the X-Frame-Options header parameter to SAMEORIGIN, to indicate the web browser that the embedded Grafana dashboard should be allowed to be loaded.
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) response;
httpServletResponse.setHeader("X-Frame-Options", "SAMEORIGIN");
chain.doFilter(request, response);
log.info(httpServletResponse.getHeaderNames());
}
Add filter declaration and mapping in web.xml
In the web application configuration file web.xml we should declare and map the new filter.
....
<filter>
<filter-name>GrafanaResponseFilter</filter-name>
<filter-class>org.voina.spring.myapp.view.controllers.GrafanaResponseFilter</filter-class>
<async-supported>true</async-supported>
</filter>
....
<filter-mapping>
<filter-name>GrafanaResponseFilter</filter-name>
<url-pattern>/grafana/*</url-pattern>
</filter-mapping>
Note that the filter will add the extra Header on the response returned from Grafana server.
Note that url-pattern must not contain the application context prefix “/myapp”
Add Grafana URLs
On the production/test server under montranConfig directory rtgs/routing/module.xml define the addresses used for Grafana.
<init-param>
<param-name>grafanaBaseURL</param-name>
<param-value>http://test.voina.org:3000</param-value>
</init-param>
<init-param>
<param-name>grafanaProxyDashboardURL</param-name>
<param-value>https://test.voina.org:8443/myapp/grafana/playlists/play/1?kiosk=tv</param-value>
</init-param>
Where:
- grafanaBaseURL = base path of Grafana (without the /myapp/grafana sub-path under which Dashboards are served)
- grafanaProxyDashboardURL = URL that is embedded in MYAPP dashboard in IFRAME
STEP 3′: Application changes when using Spring Boot
In case of a Spring Boot application instead of an application deployed on a Jboss application Server this step 3 becomes much simpler.
We just need to define a WebFilter like the following:
@Component
public class GrafanaResponseFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
exchange.getResponse()
.getHeaders()
.add("X-Frame-Options", "SAMEORIGIN");
return chain.filter(exchange);
}
}
Scenario for the final setup.
- Main application page embeds in an IFRAME the following URL:
“https://test.voina.org:8443/myapp/grafana/playlists/play/1?kiosk=tv” - The Jboss Undertow “location” will intercept requests to path “/myapp/grafana” and will forward the requests to the “grafana” reverse proxy handler
- Grafana reverse proxy handler will forward the request to the outbound-socket-binding grafana with the path /myapp/grafana/.
The actual call will be made to the host and port of the outbound-socket-binding grafana, the final called URL will be:
“http://test.voina.org:3000/myapp/grafana/playlists/play/1?kiosk=tv” - Grafana service will serve the dashboards from the /myapp/grafana/ sub path as configured.
- In Jboss the GrafanaResponseFilter will intercept the requests response according to the filter mapping and will append the the response header “X-Frame-Options: SAMEORIGIN”. This will inform the web browser to allow the response to be embedded in the iframe from the main application page.