From 7124171278a08a8e21b58c58bb8c21cc98265d65 Mon Sep 17 00:00:00 2001 From: litoral05 Date: Tue, 5 May 2026 16:01:05 +0100 Subject: [PATCH] feat: protect API endpoints with API key authentication --- .gitignore | 2 + .../openvpn/security/ApiKeyFilter.java | 46 +++++++++++++++++++ .../openvpn/security/ApiKeyProperties.java | 17 +++++++ .../openvpn/security/SecurityConfig.java | 15 ++++++ src/main/resources/application.yaml | 5 +- 5 files changed, 84 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/litoralregas/openvpn/security/ApiKeyFilter.java create mode 100644 src/main/java/com/litoralregas/openvpn/security/ApiKeyProperties.java create mode 100644 src/main/java/com/litoralregas/openvpn/security/SecurityConfig.java diff --git a/.gitignore b/.gitignore index 05a1c0c..bc89e7a 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,5 @@ build/ ### Postgres ### postgres-data/ + +.env diff --git a/src/main/java/com/litoralregas/openvpn/security/ApiKeyFilter.java b/src/main/java/com/litoralregas/openvpn/security/ApiKeyFilter.java new file mode 100644 index 0000000..4f519c5 --- /dev/null +++ b/src/main/java/com/litoralregas/openvpn/security/ApiKeyFilter.java @@ -0,0 +1,46 @@ +package com.litoralregas.openvpn.security; + +import jakarta.servlet.FilterChain; +import jakarta.servlet.ServletException; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; +import org.springframework.web.filter.OncePerRequestFilter; + +import java.io.IOException; + +public class ApiKeyFilter extends OncePerRequestFilter { + + private static final String HEADER = "X-API-Key"; + + private final ApiKeyProperties properties; + + public ApiKeyFilter(ApiKeyProperties properties) { + this.properties = properties; + } + + @Override + protected void doFilterInternal( + HttpServletRequest request, + HttpServletResponse response, + FilterChain filterChain + ) throws ServletException, IOException { + + String path = request.getRequestURI(); + + // allow health or non-api paths if needed + if (!path.startsWith("/api")) { + filterChain.doFilter(request, response); + return; + } + + String apiKey = request.getHeader(HEADER); + + if (apiKey == null || !apiKey.equals(properties.getApiKey())) { + response.setStatus(HttpServletResponse.SC_UNAUTHORIZED); + response.getWriter().write("{\"error\":\"Unauthorized\"}"); + return; + } + + filterChain.doFilter(request, response); + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/openvpn/security/ApiKeyProperties.java b/src/main/java/com/litoralregas/openvpn/security/ApiKeyProperties.java new file mode 100644 index 0000000..7f22250 --- /dev/null +++ b/src/main/java/com/litoralregas/openvpn/security/ApiKeyProperties.java @@ -0,0 +1,17 @@ +package com.litoralregas.openvpn.security; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +@ConfigurationProperties(prefix = "security") +public class ApiKeyProperties { + + private String apiKey; + + public String getApiKey() { + return apiKey; + } + + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + } +} \ No newline at end of file diff --git a/src/main/java/com/litoralregas/openvpn/security/SecurityConfig.java b/src/main/java/com/litoralregas/openvpn/security/SecurityConfig.java new file mode 100644 index 0000000..9b60b95 --- /dev/null +++ b/src/main/java/com/litoralregas/openvpn/security/SecurityConfig.java @@ -0,0 +1,15 @@ +package com.litoralregas.openvpn.security; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +@EnableConfigurationProperties(ApiKeyProperties.class) +public class SecurityConfig { + + @Bean + public ApiKeyFilter apiKeyFilter(ApiKeyProperties properties) { + return new ApiKeyFilter(properties); + } +} \ No newline at end of file diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index 87c836d..5b2750c 100644 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -34,4 +34,7 @@ lr: openvpn: tools-path: ${LR_OPENVPN_TOOLS_PATH:/var/litoral_regas_openvpn/tools} - provision-dry-run: ${LR_OPENVPN_PROVISION_DRY_RUN:true} \ No newline at end of file + provision-dry-run: ${LR_OPENVPN_PROVISION_DRY_RUN:true} + +security: + api-key: ${API_KEY} \ No newline at end of file