quarkus Posts

Securing Quarkus Metric API

Usually im creating a metrics API for displaying statistics and various metrics which are being use for measuring application healthiness. There are various tools that being use to capturing this statistics and displaying it, one example is using Prometheus and Grafana.

But on this example, we are not talking too much detail about Prometheus and Grafana, but more on how we providing those metrics on top of Quaskus while securing it so that no malicious user can access this metric API easily.

Lets start with a simple pom file,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>QuarkusPrometheus</groupId>
    <artifactId>com.edw</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <quarkus.version>1.6.1.Final</quarkus.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <surefire-plugin.version>2.22.1</surefire-plugin.version>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-bom</artifactId>
                <version>${quarkus.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
        </dependency>

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-metrics</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-smallrye-health</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-elytron-security-properties-file</artifactId>
        </dependency>

        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                    </systemProperties>
                </configuration>
            </plugin>
            <plugin>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>${quarkus.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>
</project>

And a very simple application.properties for storing all my configurations, in here we can see that we are defining a specific role for accessing our metrics endpoint.

#port
quarkus.http.port=8082

#security
quarkus.security.users.embedded.enabled=true
quarkus.security.users.embedded.plain-text=true
quarkus.security.users.embedded.users.admin=password
quarkus.security.users.embedded.roles.admin=prom-role

quarkus.http.auth.policy.prom-policy.roles-allowed=prom-role

quarkus.http.auth.permission.prom-roles.policy=prom-policy
quarkus.http.auth.permission.prom-roles.paths=/metrics

run by using below command,

compile quarkus:dev

Try opening our metrics url directly,

We can try to login by using a specific username and password,

admin / password

If successfully login, we can see this view

One interesting thing is that we can use a different url for Kubernetes’s health and liveness probe check, without have to use any credential at all.

Sourcecode for this example can be downloaded on below url,

https://github.com/edwin/quarkus-secure-prometheus-api

Have fun with Quarkus 🙂

Building A Quarkus Native Image Using GraalVM, Dockerize, And Deploy It on Top of Openshift Platform

Right now we are going to talk about Quarkus. According to its website, Quarkus tailors your application for GraalVM and HotSpot. Which will provide an amazingly fast boot time, incredibly low RSS memory (not just heap size!) offering near instant scale up and high density memory utilization in container orchestration platforms like Kubernetes.

Let’s start with a simple maven file,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.edw.quarkus</groupId>
    <artifactId>com.edw.quarkus</artifactId>
    <version>1.0.0</version>

    <properties>
        <quarkus.version>0.19.1</quarkus.version>
        <surefire-plugin.version>2.22.1</surefire-plugin.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-bom</artifactId>
                <version>${quarkus.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
            <version>${quarkus.version}</version>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy</artifactId>
            <version>${quarkus.version}</version>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-junit5</artifactId>
            <version>${quarkus.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.rest-assured</groupId>
            <artifactId>rest-assured</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire-plugin.version}</version>
                <configuration>
                    <systemProperties>
                        <java.util.logging.manager>org.jboss.logmanager.LogManager</java.util.logging.manager>
                    </systemProperties>
                </configuration>
            </plugin>
            <plugin>
                <groupId>io.quarkus</groupId>
                <artifactId>quarkus-maven-plugin</artifactId>
                <version>${quarkus.version}</version>
                <executions>
                    <execution>
                        <goals>
                            <goal>build</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

    <profiles>
        <profile>
            <id>native</id>
            <activation>
                <property>
                    <name>native</name>
                </property>
            </activation>
            <build>
                <plugins>
                    <plugin>
                        <groupId>io.quarkus</groupId>
                        <artifactId>quarkus-maven-plugin</artifactId>
                        <version>${quarkus.version}</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>native-image</goal>
                                </goals>
                                <configuration>
                                    <enableHttpUrlHandler>true</enableHttpUrlHandler>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                    <plugin>
                        <artifactId>maven-failsafe-plugin</artifactId>
                        <version>${surefire-plugin.version}</version>
                        <executions>
                            <execution>
                                <goals>
                                    <goal>integration-test</goal>
                                    <goal>verify</goal>
                                </goals>
                                <configuration>
                                    <systemProperties>
                                        <native.image.path>${project.build.directory}/${project.build.finalName}-runner</native.image.path>
                                    </systemProperties>
                                </configuration>
                            </execution>
                        </executions>
                    </plugin>
                </plugins>
            </build>
        </profile>
    </profiles>
</project>

And a simple java hello-world rest api endpoint,

package com.edw;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

@Path("/")
public class IndexController {
    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public String hello() {
        return "{\"Hello World\":\"Using Quarkus\"}";
    }
}

Next is downloading GraalVM to compile our java code to a native, we can download it here

https://github.com/oracle/graal/releases?

For this blog, im using below version,

graalvm-ce-19.1.1

Next is extracting our graal download and putting it on our JVM runtime. Im using MacOS, so im using below command

sudo mv Downloads/graalvm-ce-19.1.1 /Library/Java/JavaVirtualMachines

In order to create a native docker image using GraalVM, we need to install a specific component using below command,

/Library/Java/JavaVirtualMachines/graalvm-ce-19.1.1/Contents/Home/bin/gu install native-image

After we install both GraalVM and its Native Image component, set JDK path to GraalVM and use below maven command to build our app into native apps,

mvn package -Pnative -Dnative-image.docker-build=true

And we can see the result of our maven build on target folder. Depends on our operating system, we can run our maven build generated file directly using below command

./com.edw.quercus-1.0.0-runner

Next is create a Dockerfile to dockerize our executable file

FROM registry.access.redhat.com/ubi8/ubi-minimal
WORKDIR /work/
COPY target/*-runner /work/application
RUN chmod 775 /work
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]

And run below command,

docker build -t my-quarkus .

To validate our build, we can use below command

docker images | grep my-quarkus

If our docker creation is success, it will shows below output.

REPOSITORY                   TAG                 IMAGE ID            CREATED             SIZE
my-quarkus                   latest              35108d40afdb        24 seconds ago      110MB

We can test running it by using this command,

docker run -p 8080:8080 my-quarkus

And test it by opening browser directly or using curl

curl -v http://localhost:8080/

Once we convince that our dockerized quarkus app runs perfectly, next step is to tag it and push it to, either directly to openshift registry or to dockerhub. The easiest way would be directly, but sometimes we dont have the admin privilege to access registry so on this tutorial we’ll try to push it to dockerhub and then pull the docker image from openshift.

Make sure to create an account on docker hub (https://hub.docker.com/), and input the credentials on your local environment

docker login

After that, you can tag and push corresponding docker images to docker hub using below command,

docker tag my-quarkus edwinkun/my-quarkus
docker push edwinkun/my-quarkus

If push is success, you can see your docker image on docker hub

We can pull on Openshift using “Deploy Image” option,

Select your project, and put your dockerhub image tag

And deploy it afterward, after successfully deployed it will shows pod like this,

Have fun (Y)