Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/ivangfr/springboot-kong-plugins
The goal of this project is to create a simple Spring Boot REST API and securing it with Kong using the LDAP Authentication and Basic Authentication plugins. Besides, we will explore more plugins that Kong offers like: Rate Limiting and Prometheus plugins.
https://github.com/ivangfr/springboot-kong-plugins
basic-authentication docker java kong kong-plugin ldap ldap-authentication openldap phpldapadmin postgresql prometheus rate-limiting spring-boot web
Last synced: 3 months ago
JSON representation
The goal of this project is to create a simple Spring Boot REST API and securing it with Kong using the LDAP Authentication and Basic Authentication plugins. Besides, we will explore more plugins that Kong offers like: Rate Limiting and Prometheus plugins.
- Host: GitHub
- URL: https://github.com/ivangfr/springboot-kong-plugins
- Owner: ivangfr
- Created: 2018-06-02T13:39:23.000Z (over 6 years ago)
- Default Branch: master
- Last Pushed: 2024-04-13T20:52:46.000Z (10 months ago)
- Last Synced: 2024-05-07T18:23:17.831Z (9 months ago)
- Topics: basic-authentication, docker, java, kong, kong-plugin, ldap, ldap-authentication, openldap, phpldapadmin, postgresql, prometheus, rate-limiting, spring-boot, web
- Language: Shell
- Homepage:
- Size: 1.7 MB
- Stars: 14
- Watchers: 2
- Forks: 15
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
Awesome Lists containing this project
README
# springboot-kong-plugins
The goal of this project is to create a simple [`Spring Boot`](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/) REST API and securing it with [`Kong`](https://konghq.com/kong/) using the `LDAP Authentication` and `Basic Authentication` plugins. Besides, we will explore more plugins that `Kong` offers like: `Rate Limiting` and `Prometheus` plugins.
## Proof-of-Concepts & Articles
On [ivangfr.github.io](https://ivangfr.github.io), I have compiled my Proof-of-Concepts (PoCs) and articles. You can easily search for the technology you are interested in by using the filter. Who knows, perhaps I have already implemented a PoC or written an article about what you are looking for.
## Additional Readings
- \[**Medium**\] [**Using Kong to secure a Simple Spring Boot REST API with Basic Authentication plugin**](https://medium.com/javarevisited/using-kong-to-secure-a-simple-spring-boot-rest-api-with-basic-authentication-plugin-90f3529043f3)
- \[**Medium**\] [**Using Kong to secure a Simple Spring Boot REST API with LDAP Authentication plugin**](https://medium.com/javarevisited/using-kong-to-secure-a-simple-spring-boot-rest-api-with-ldap-authentication-plugin-3a499e01382a)
- \[**Medium**\] [**Using Kong to configure Rate Limiting to a Simple Spring Boot REST API**](https://medium.com/javarevisited/using-kong-to-configure-rate-limiting-to-a-simple-spring-boot-rest-api-33b1899077d)
- \[**Medium**\] [**Using Kong to secure a Simple Spring Boot REST API with Kong OIDC plugin and Keycloak**](https://medium.com/@ivangfr/using-kong-to-secure-a-simple-spring-boot-rest-api-with-kong-oidc-plugin-and-keycloak-c8fa8de32e6e)## Project Diagram
![project-diagram](documentation/project-diagram.jpeg)
## Application
- ### simple-service
`Spring Boot` Java Web application that exposes two endpoints:
- `/api/public`: that can be access by anyone, it is not secured;
- `/api/private`: that must be accessed only by authenticated users.## Prerequisites
- [`Java 17+`](https://www.oracle.com/java/technologies/downloads/#java17)
- [`Docker`](https://www.docker.com/)## Run application during development using Maven
- Open a terminal and navigate to `springboot-kong-plugins` root folder
- Run the command below to start
```
./mvnw clean spring-boot:run --projects simple-service
```- Open another terminal and call application endpoints
```
curl -i localhost:8080/api/public
curl -i localhost:8080/api/private
curl -i localhost:8080/actuator/beans
curl -i localhost:8080/actuator/health
```- To stop, go to the terminal where the application is running and press `Ctrl+C`
## Build application Docker Image
- In a terminal, make sure you are in `springboot-kong-plugins` root folder
- Build Docker Image
- JVM
```
./docker-build.sh
```
- Native
```
./docker-build.sh native
```
## Test application Docker Image- In a terminal, run the following command
```
docker run --rm -p 8080:8080 --name simple-service ivanfranchin/simple-service:1.0.0
```- Open another terminal and call application endpoints
```
curl -i localhost:8080/api/public
curl -i localhost:8080/api/private
curl -i localhost:8080/actuator/beans
curl -i localhost:8080/actuator/health
```- To stop, go to the terminal where the application is running and press `Ctrl+C`
## Initialize Environment
- In a terminal, make use you are in `springboot-kong-plugins` root folder
- Run the following script
```
./init-environment.sh
```
> **Note**: `simple-service` application is running as a Docker container. The container does not expose any port to HOST machine. So, it cannot be accessed directly, forcing the caller to use `Kong` as gateway server in order to access it.## Import OpenLDAP Users
The `LDIF` file that we will use, `ldap/ldap-mycompany-com.ldif`, has already a pre-defined structure for `mycompany.com`. Basically, it has 2 groups (`developers` and `admin`) and 4 users (`Bill Gates`, `Steve Jobs`, `Mark Cuban` and `Ivan Franchin`). Besides, it's defined that `Bill Gates`, `Steve Jobs` and `Mark Cuban` belong to `developers` group and `Ivan Franchin` belongs to `admin` group.
```
Bill Gates > username: bgates, password: 123
Steve Jobs > username: sjobs, password: 123
Mark Cuban > username: mcuban, password: 123
Ivan Franchin > username: ifranchin, password: 123
```There are two ways to import those users: by running a script or using `phpldapadmin`
### Import users running a script
- In another terminal, make use you are in `springboot-kong-plugins` root folder
- Run the following script
```
./import-openldap-users.sh
```
- Check users imported using [`ldapsearch`](https://linux.die.net/man/1/ldapsearch)
```
ldapsearch -x -D "cn=admin,dc=mycompany,dc=com" \
-w admin -H ldap://localhost:389 \
-b "ou=users,dc=mycompany,dc=com" \
-s sub "(uid=*)"
```### Import users using phpldapadmin
- Access https://localhost:6443
- Login with the credentials
```
Login DN: cn=admin,dc=mycompany,dc=com
Password: admin
```- Import the file `ldap/ldap-mycompany-com.ldif`
- You should see something like
![openldap](documentation/openldap.jpeg)
## Kong
In a terminal, follow the steps below to configure `Kong`
### Check Status
- Before starting, check if `Kong` admin API is accessible
```
curl -I http://localhost:8001
```It should return
```
HTTP/1.1 200 OK
```### Add Service
1. The following call will add the `simple-service` service
```
curl -i -X POST http://localhost:8001/services/ \
-d "name=simple-service" \
-d "protocol=http" \
-d "host=simple-service" \
-d "port=8080"
```2. \[Optional\] To list all services run
```
curl -i http://localhost:8001/services
```### Add routes
1. One default route for the service, no specific `path` included
```
curl -i -X POST http://localhost:8001/services/simple-service/routes/ \
-d "name=simple-service-default" \
-d "protocols[]=http" \
-d "hosts[]=simple-service"
```2. Another route specifically for `/api/private` endpoint (it will be secured and only accessible by LDAP users)
```
curl -i -X POST http://localhost:8001/services/simple-service/routes/ \
-d "name=simple-service-api-private" \
-d "protocols[]=http" \
-d "hosts[]=simple-service" \
-d "paths[]=/api/private" \
-d "strip_path=false"
```3. Finally, one route for `/actuator/beans` endpoint (it will be secured and only accessible by pre-defined users)
```
curl -i -X POST http://localhost:8001/services/simple-service/routes/ \
-d "name=simple-service-actuator-beans" \
-d "protocols[]=http" \
-d "hosts[]=simple-service" \
-d "paths[]=/actuator/beans" \
-d "strip_path=false"
```4. \[Optional\] To list all `simple-service` routes run
```
curl -i http://localhost:8001/services/simple-service/routes
```### Call endpoints
1. Call `/api/public` endpoint
```
curl -i http://localhost:8000/api/public -H 'Host: simple-service'
```It should return
```
HTTP/1.1 200
It is public.
```2. Call `/api/private` endpoint
```
curl -i http://localhost:8000/api/private -H 'Host: simple-service'
```It should return
```
HTTP/1.1 200
null, it is private.
```> **Note**: This endpoint is not secured by the application, that is why the response is returned. The idea is to use `Kong` to secure it. It will be done on the next steps.
3. Call `/actuator/beans` endpoint
```
curl -i http://localhost:8000/actuator/beans -H 'Host: simple-service'
```It should return
```
HTTP/1.1 200
{"contexts":{"simple-service":{"beans":...
```> **Note**: As happened previously with `/api/private`, `/actuator/beans` endpoint is not secured by the application. We will use `Kong` to secure it on the next steps.
## Plugins
In this project, we are going to add these plugins: `LDAP Authentication`, `Basic Authentication`, `Rate Limiting` and `Prometheus`. Please refer to https://konghq.com/plugins for more.
### Add LDAP Authentication plugin
The `LDAP Authentication` plugin will be used to secure the `/api/private` endpoint.
1. Add plugin to route `simple-service-api-private`
```
curl -i -X POST http://localhost:8001/routes/simple-service-api-private/plugins \
-d "name=ldap-auth" \
-d "config.hide_credentials=true" \
-d "config.ldap_host=openldap" \
-d "config.ldap_port=389" \
-d "config.start_tls=false" \
-d "config.base_dn=ou=users,dc=mycompany,dc=com" \
-d "config.verify_ldap_host=false" \
-d "config.attribute=cn" \
-d "config.cache_ttl=60" \
-d "config.header_type=ldap"
```2. Try to call `/api/private` endpoint without credentials
```
curl -i http://localhost:8000/api/private -H 'Host: simple-service'
```It should return
```
HTTP/1.1 401 Unauthorized
{"message":"Unauthorized"}
```3. Call `/api/private` endpoint using Bill Gates base64 encode credentials
```
curl -i http://localhost:8000/api/private \
-H "Authorization:ldap $(echo -n 'Bill Gates':123 | base64)" \
-H 'Host: simple-service'
```It should return
```
HTTP/1.1 200
Bill Gates, it is private.
```### Add Basic Authentication plugin
The `Basic Authentication` plugin will be used to secure the `/actuator/beans` endpoint
1. Add plugin to route `simple-service-actuator-beans`
```
curl -i -X POST http://localhost:8001/routes/simple-service-actuator-beans/plugins \
-d "name=basic-auth" \
-d "config.hide_credentials=true"
```2. Try to call `/actuator/beans` endpoint without credentials.
```
curl -i http://localhost:8000/actuator/beans -H 'Host: simple-service'
```It should return
```
HTTP/1.1 401 Unauthorized
{"message":"Unauthorized"}
```3. Create a consumer
```
curl -i -X POST http://localhost:8001/consumers -d "username=ivanfranchin"
```4. Create a credential for consumer
```
curl -i -X POST http://localhost:8001/consumers/ivanfranchin/basic-auth \
-d "username=ivan.franchin" \
-d "password=123"
```5. Call `/api/private` endpoint using `ivan.franchin` credential
```
curl -i -u ivan.franchin:123 http://localhost:8000/actuator/beans -H 'Host: simple-service'
```It should return
```
HTTP/1.1 200
{"contentDescriptor":{"providerVersion":...
```6. Let's create another consumer just for testing purpose
```
curl -i -X POST http://localhost:8001/consumers -d "username=administrator"
curl -i -X POST http://localhost:8001/consumers/administrator/basic-auth \
-d "username=administrator" \
-d "password=123"
```### Add Rate Limiting plugin
We are going to add the following rate limitings:
- `/api/public` and `/actuator/health`: one request per second
- `/api/private`: 5 requests a minute
- `/actuator/beans`: 2 requests a minute or 100 requests an hour1. Add plugin to route `simple-service-default`
```
curl -i -X POST http://localhost:8001/routes/simple-service-default/plugins \
-d "name=rate-limiting" \
-d "config.second=1"
```2. Add plugin to route `simple-service-api-private`
```
curl -i -X POST http://localhost:8001/routes/simple-service-api-private/plugins \
-d "name=rate-limiting" \
-d "config.minute=5"
```3. Add plugin to route `simple-service-actuator-beans`
```
curl -i -X POST http://localhost:8001/routes/simple-service-actuator-beans/plugins \
-d "name=rate-limiting" \
-d "config.minute=2" \
-d "config.hour=100"
```4. Make some calls to these endpoints
- Test `/api/public`
```
curl -i http://localhost:8000/api/public -H 'Host: simple-service'
curl -i http://localhost:8000/actuator/health -H 'Host: simple-service'
```- Test `/actuator/beans`
```
curl -i -u ivan.franchin:123 http://localhost:8000/actuator/beans -H 'Host: simple-service'
curl -i -u administrator:123 http://localhost:8000/actuator/beans -H 'Host: simple-service'
```- Test `/api/private`
```
curl -i http://localhost:8000/api/private \
-H "Authorization:ldap $(echo -n 'Bill Gates':123 | base64)" \
-H 'Host: simple-service'
curl -i http://localhost:8000/api/private \
-H "Authorization:ldap $(echo -n 'Mark Cuban':123 | base64)" \
-H 'Host: simple-service'
```5. After exceeding some calls in a minute, you should see
```
HTTP/1.1 429 Too Many Requests
{"message":"API rate limit exceeded"}
```### Add Prometheus plugin
1. Add plugin to `simple-service`
```
curl -i -X POST http://localhost:8001/services/simple-service/plugins -d "name=prometheus"
```2. Make some requests to `simple-service` endpoints
3. You can see some metrics
```
curl -i http://localhost:8001/metrics
```## Shutdown
In a terminal and, inside `springboot-kong-plugins` root folder, run the following script
```
./shutdown-environment.sh
```## Cleanup
To remove the Docker image created by this project, go to a terminal and, inside `springboot-kong-plugins` root folder, run the script below
```
./remove-docker-images.sh
```## Issues
When upgrading postgres to a version above `13.x` (using current kong version), there is an error while running `kong-database migration`
```
Running kong-database migration
-------------------------------
Error: module 'openssl.rand' not found:No LuaRocks module found for openssl.rand
no field package.preload['openssl.rand']
no file './openssl/rand.lua'
no file './openssl/rand/init.lua'
no file './openssl/rand.lua'
no file './openssl/rand/init.lua'
no file '/usr/local/openresty/site/lualib/openssl/rand.ljbc'
no file '/usr/local/openresty/site/lualib/openssl/rand/init.ljbc'
no file '/usr/local/openresty/lualib/openssl/rand.ljbc'
no file '/usr/local/openresty/lualib/openssl/rand/init.ljbc'
no file '/usr/local/openresty/site/lualib/openssl/rand.lua'
no file '/usr/local/openresty/site/lualib/openssl/rand/init.lua'
no file '/usr/local/openresty/lualib/openssl/rand.lua'
no file '/usr/local/openresty/lualib/openssl/rand/init.lua'
no file './openssl/rand.lua'
no file '/usr/local/openresty/luajit/share/luajit-2.1.0-beta3/openssl/rand.lua'
no file '/usr/local/share/lua/5.1/openssl/rand.lua'
no file '/usr/local/share/lua/5.1/openssl/rand/init.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/openssl/rand.lua'
no file '/usr/local/openresty/luajit/share/lua/5.1/openssl/rand/init.lua'
no file '/home/kong/.luarocks/share/lua/5.1/openssl/rand.lua'
no file '/home/kong/.luarocks/share/lua/5.1/openssl/rand/init.lua'
no file '/usr/local/openresty/site/lualib/openssl/rand.so'
no file '/usr/local/openresty/lualib/openssl/rand.so'
no file './openssl/rand.so'
no file '/usr/local/lib/lua/5.1/openssl/rand.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/openssl/rand.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file '/home/kong/.luarocks/lib/lua/5.1/openssl/rand.so'
no file '/usr/local/openresty/site/lualib/openssl.so'
no file '/usr/local/openresty/lualib/openssl.so'
no file './openssl.so'
no file '/usr/local/lib/lua/5.1/openssl.so'
no file '/usr/local/openresty/luajit/lib/lua/5.1/openssl.so'
no file '/usr/local/lib/lua/5.1/loadall.so'
no file '/home/kong/.luarocks/lib/lua/5.1/openssl.so'Run with --v (verbose) or --vv (debug) for more details
```