Fixed and added more health support plus network endpoint
This commit is contained in:
@@ -31,10 +31,15 @@ public class ApiKeyAuthFilter extends OncePerRequestFilter {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doFilterInternal(
|
protected void doFilterInternal(
|
||||||
|
|
||||||
HttpServletRequest request,
|
HttpServletRequest request,
|
||||||
HttpServletResponse response,
|
HttpServletResponse response,
|
||||||
FilterChain filterChain
|
FilterChain filterChain
|
||||||
) throws ServletException, IOException {
|
) throws ServletException, IOException {
|
||||||
|
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||||
|
filterChain.doFilter(request, response);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
String providedApiKey = request.getHeader(API_KEY_HEADER);
|
String providedApiKey = request.getHeader(API_KEY_HEADER);
|
||||||
String expectedApiKey = securityProperties.getApiKey();
|
String expectedApiKey = securityProperties.getApiKey();
|
||||||
|
|||||||
@@ -1,14 +1,26 @@
|
|||||||
package com.litoralregas.vpnorchestrator.config;
|
package com.litoralregas.vpnorchestrator.config;
|
||||||
|
|
||||||
import com.litoralregas.vpnorchestrator.auth.ApiKeyAuthFilter;
|
import com.litoralregas.vpnorchestrator.auth.ApiKeyAuthFilter;
|
||||||
|
|
||||||
|
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||||
|
|
||||||
import org.springframework.context.annotation.Bean;
|
import org.springframework.context.annotation.Bean;
|
||||||
import org.springframework.context.annotation.Configuration;
|
import org.springframework.context.annotation.Configuration;
|
||||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
|
||||||
|
import org.springframework.http.HttpMethod;
|
||||||
|
|
||||||
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
|
||||||
import org.springframework.security.config.http.SessionCreationPolicy;
|
import org.springframework.security.config.http.SessionCreationPolicy;
|
||||||
|
|
||||||
import org.springframework.security.web.SecurityFilterChain;
|
import org.springframework.security.web.SecurityFilterChain;
|
||||||
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
|
||||||
|
|
||||||
|
import org.springframework.web.cors.CorsConfiguration;
|
||||||
|
import org.springframework.web.cors.CorsConfigurationSource;
|
||||||
|
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
@Configuration
|
@Configuration
|
||||||
@EnableConfigurationProperties({
|
@EnableConfigurationProperties({
|
||||||
AppSecurityProperties.class,
|
AppSecurityProperties.class,
|
||||||
@@ -18,28 +30,95 @@ public class SecurityConfig {
|
|||||||
|
|
||||||
private final AppSecurityProperties securityProperties;
|
private final AppSecurityProperties securityProperties;
|
||||||
|
|
||||||
public SecurityConfig(AppSecurityProperties securityProperties) {
|
public SecurityConfig(
|
||||||
|
AppSecurityProperties securityProperties
|
||||||
|
) {
|
||||||
this.securityProperties = securityProperties;
|
this.securityProperties = securityProperties;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Bean
|
@Bean
|
||||||
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
|
public SecurityFilterChain securityFilterChain(
|
||||||
ApiKeyAuthFilter apiKeyAuthFilter = new ApiKeyAuthFilter(securityProperties);
|
HttpSecurity http
|
||||||
|
) throws Exception {
|
||||||
|
|
||||||
|
ApiKeyAuthFilter apiKeyAuthFilter =
|
||||||
|
new ApiKeyAuthFilter(securityProperties);
|
||||||
|
|
||||||
return http
|
return http
|
||||||
.csrf(csrf -> csrf.disable())
|
.csrf(csrf -> csrf.disable())
|
||||||
.cors(cors -> cors.disable())
|
|
||||||
|
.cors(cors -> {
|
||||||
|
})
|
||||||
|
|
||||||
.formLogin(form -> form.disable())
|
.formLogin(form -> form.disable())
|
||||||
|
|
||||||
.httpBasic(basic -> basic.disable())
|
.httpBasic(basic -> basic.disable())
|
||||||
|
|
||||||
.logout(logout -> logout.disable())
|
.logout(logout -> logout.disable())
|
||||||
.sessionManagement(session -> session
|
|
||||||
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
|
.sessionManagement(session ->
|
||||||
|
session.sessionCreationPolicy(
|
||||||
|
SessionCreationPolicy.STATELESS
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
.authorizeHttpRequests(auth -> auth
|
.authorizeHttpRequests(auth -> auth
|
||||||
.requestMatchers("/actuator/health").permitAll()
|
.requestMatchers(
|
||||||
|
"/actuator/health"
|
||||||
|
).permitAll()
|
||||||
|
|
||||||
|
.requestMatchers(
|
||||||
|
HttpMethod.OPTIONS,
|
||||||
|
"/**"
|
||||||
|
).permitAll()
|
||||||
|
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
)
|
)
|
||||||
.addFilterBefore(apiKeyAuthFilter, UsernamePasswordAuthenticationFilter.class)
|
|
||||||
|
.addFilterBefore(
|
||||||
|
apiKeyAuthFilter,
|
||||||
|
UsernamePasswordAuthenticationFilter.class
|
||||||
|
)
|
||||||
|
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Bean
|
||||||
|
public CorsConfigurationSource corsConfigurationSource() {
|
||||||
|
|
||||||
|
CorsConfiguration configuration =
|
||||||
|
new CorsConfiguration();
|
||||||
|
|
||||||
|
configuration.setAllowedOrigins(
|
||||||
|
List.of(
|
||||||
|
"http://localhost:1420"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
configuration.setAllowedMethods(
|
||||||
|
List.of(
|
||||||
|
"GET",
|
||||||
|
"POST",
|
||||||
|
"PUT",
|
||||||
|
"DELETE",
|
||||||
|
"OPTIONS"
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
configuration.setAllowedHeaders(
|
||||||
|
List.of("*")
|
||||||
|
);
|
||||||
|
|
||||||
|
configuration.setAllowCredentials(true);
|
||||||
|
|
||||||
|
UrlBasedCorsConfigurationSource source =
|
||||||
|
new UrlBasedCorsConfigurationSource();
|
||||||
|
|
||||||
|
source.registerCorsConfiguration(
|
||||||
|
"/**",
|
||||||
|
configuration
|
||||||
|
);
|
||||||
|
|
||||||
|
return source;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
package com.litoralregas.vpnorchestrator.vps;
|
package com.litoralregas.vpnorchestrator.vps;
|
||||||
|
|
||||||
|
import com.litoralregas.vpnorchestrator.vps.dto.NetworkTrafficResponse;
|
||||||
import com.litoralregas.vpnorchestrator.vps.dto.VpsHealthResponse;
|
import com.litoralregas.vpnorchestrator.vps.dto.VpsHealthResponse;
|
||||||
import org.springframework.http.MediaType;
|
import org.springframework.http.MediaType;
|
||||||
import org.springframework.web.bind.annotation.*;
|
import org.springframework.web.bind.annotation.*;
|
||||||
@@ -26,4 +27,9 @@ public class VpsController {
|
|||||||
public String rollbackLastBackup() {
|
public String rollbackLastBackup() {
|
||||||
return wireGuardService.restoreLastWireGuardBackup();
|
return wireGuardService.restoreLastWireGuardBackup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GetMapping("/network-traffic")
|
||||||
|
public NetworkTrafficResponse getNetworkTraffic() {
|
||||||
|
return wireGuardService.getNetworkTraffic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,6 +2,7 @@ package com.litoralregas.vpnorchestrator.vps;
|
|||||||
|
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.litoralregas.vpnorchestrator.vps.dto.NetworkTrafficResponse;
|
||||||
import com.litoralregas.vpnorchestrator.vps.dto.VpsHealthResponse;
|
import com.litoralregas.vpnorchestrator.vps.dto.VpsHealthResponse;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -76,19 +77,57 @@ public class WireGuardService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public VpsHealthResponse getVpsHealth() {
|
public VpsHealthResponse getVpsHealth() {
|
||||||
|
|
||||||
SshCommandResult result = sshService.executeOnConfiguredVps(
|
SshCommandResult result = sshService.executeOnConfiguredVps(
|
||||||
"sudo /usr/local/sbin/lr-vps-health"
|
"sudo /usr/local/sbin/lr-vps-health"
|
||||||
);
|
);
|
||||||
|
|
||||||
if (result.exitCode() != 0) {
|
if (result.exitCode() != 0) {
|
||||||
throw new SshCommandException(
|
throw new SshCommandException(
|
||||||
"Failed to query VPS health: " + result.stderr()
|
"Failed to query VPS health: "
|
||||||
|
+ result.stderr()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return objectMapper.readValue(result.stdout(), VpsHealthResponse.class);
|
|
||||||
|
VpsHealthResponse base =
|
||||||
|
objectMapper.readValue(
|
||||||
|
result.stdout(),
|
||||||
|
VpsHealthResponse.class
|
||||||
|
);
|
||||||
|
|
||||||
|
Set<String> usedIps =
|
||||||
|
findUsedVpnIps();
|
||||||
|
|
||||||
|
return new VpsHealthResponse(
|
||||||
|
|
||||||
|
base.wireGuardInterface(),
|
||||||
|
base.wireGuardRunning(),
|
||||||
|
base.wireGuardPeerCount(),
|
||||||
|
base.wireGuardConfigExists(),
|
||||||
|
|
||||||
|
base.udp2rawService(),
|
||||||
|
base.udp2rawActive(),
|
||||||
|
|
||||||
|
base.latestWireGuardBackup(),
|
||||||
|
|
||||||
|
base.systemUptime(),
|
||||||
|
|
||||||
|
base.diskUsagePercent(),
|
||||||
|
base.memoryUsagePercent(),
|
||||||
|
|
||||||
|
base.loadAverage(),
|
||||||
|
base.publicIp(),
|
||||||
|
|
||||||
|
true,
|
||||||
|
usedIps.size(),
|
||||||
|
65534,
|
||||||
|
java.time.Instant.now().toString()
|
||||||
|
);
|
||||||
|
|
||||||
} catch (JsonProcessingException e) {
|
} catch (JsonProcessingException e) {
|
||||||
|
|
||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"Invalid VPS health JSON returned by script",
|
"Invalid VPS health JSON returned by script",
|
||||||
e
|
e
|
||||||
@@ -123,4 +162,34 @@ public class WireGuardService {
|
|||||||
|
|
||||||
return result.stdout();
|
return result.stdout();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public NetworkTrafficResponse getNetworkTraffic() {
|
||||||
|
|
||||||
|
SshCommandResult result =
|
||||||
|
sshService.executeOnConfiguredVps(
|
||||||
|
"sudo /usr/local/sbin/lr-vps-network-traffic"
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.exitCode() != 0) {
|
||||||
|
throw new SshCommandException(
|
||||||
|
"Failed to query network traffic: "
|
||||||
|
+ result.stderr()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
return objectMapper.readValue(
|
||||||
|
result.stdout(),
|
||||||
|
NetworkTrafficResponse.class
|
||||||
|
);
|
||||||
|
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Invalid network traffic JSON returned by script",
|
||||||
|
e
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package com.litoralregas.vpnorchestrator.vps.dto;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
|
||||||
|
public record NetworkTrafficResponse(
|
||||||
|
|
||||||
|
@JsonProperty("interface")
|
||||||
|
String interfaceName,
|
||||||
|
|
||||||
|
int sampleSeconds,
|
||||||
|
long rxBytesPerSecond,
|
||||||
|
long txBytesPerSecond,
|
||||||
|
double downloadMbps,
|
||||||
|
double uploadMbps,
|
||||||
|
String updatedAt
|
||||||
|
) {
|
||||||
|
}
|
||||||
@@ -1,17 +1,28 @@
|
|||||||
package com.litoralregas.vpnorchestrator.vps.dto;
|
package com.litoralregas.vpnorchestrator.vps.dto;
|
||||||
|
|
||||||
public record VpsHealthResponse(
|
public record VpsHealthResponse(
|
||||||
|
|
||||||
String wireGuardInterface,
|
String wireGuardInterface,
|
||||||
boolean wireGuardRunning,
|
boolean wireGuardRunning,
|
||||||
int wireGuardPeerCount,
|
int wireGuardPeerCount,
|
||||||
boolean wireGuardConfigExists,
|
boolean wireGuardConfigExists,
|
||||||
|
|
||||||
String udp2rawService,
|
String udp2rawService,
|
||||||
boolean udp2rawActive,
|
boolean udp2rawActive,
|
||||||
|
|
||||||
String latestWireGuardBackup,
|
String latestWireGuardBackup,
|
||||||
|
|
||||||
String systemUptime,
|
String systemUptime,
|
||||||
|
|
||||||
int diskUsagePercent,
|
int diskUsagePercent,
|
||||||
int memoryUsagePercent,
|
int memoryUsagePercent,
|
||||||
|
|
||||||
String loadAverage,
|
String loadAverage,
|
||||||
String publicIp
|
String publicIp,
|
||||||
|
|
||||||
|
boolean backend,
|
||||||
|
int ipPoolUsed,
|
||||||
|
int ipPoolTotal,
|
||||||
|
String updatedAt
|
||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user