From 6f70cbfe678532fb4983f94ae166afaf9e68b5c6 Mon Sep 17 00:00:00 2001 From: litoral05 Date: Tue, 5 May 2026 12:29:33 +0100 Subject: [PATCH] Add dry-run provisioning command generation --- .../openvpn/IpAllocationRepository.java | 2 ++ .../openvpn/openvpn/IpAllocationService.java | 5 ++++ .../openvpn/openvpn/OpenVpnService.java | 18 +++++++++++++ .../openvpn/router/RouterController.java | 26 ++++++++++++------- 4 files changed, 42 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationRepository.java b/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationRepository.java index 35d64c6..7fb6e76 100644 --- a/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationRepository.java +++ b/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationRepository.java @@ -2,6 +2,7 @@ package com.litoralregas.openvpn.openvpn; import org.springframework.data.jpa.repository.JpaRepository; +import java.util.Optional; import java.util.UUID; public interface IpAllocationRepository extends JpaRepository { @@ -11,4 +12,5 @@ public interface IpAllocationRepository extends JpaRepository findByRouterId(UUID routerId); } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationService.java b/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationService.java index 64da794..c3ba59f 100644 --- a/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationService.java +++ b/src/main/java/com/litoralregas/openvpn/openvpn/IpAllocationService.java @@ -113,4 +113,9 @@ public class IpAllocationService { public void delete(UUID id) { repository.deleteById(id); } + + public IpAllocation findByRouterId(UUID routerId) { + return repository.findByRouterId(routerId) + .orElseThrow(() -> new IllegalArgumentException("No IP allocation found for router: " + routerId)); + } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/openvpn/openvpn/OpenVpnService.java b/src/main/java/com/litoralregas/openvpn/openvpn/OpenVpnService.java index f9c8a5c..08348c8 100644 --- a/src/main/java/com/litoralregas/openvpn/openvpn/OpenVpnService.java +++ b/src/main/java/com/litoralregas/openvpn/openvpn/OpenVpnService.java @@ -64,4 +64,22 @@ public class OpenVpnService { return null; } + + public String buildProvisionCommand(String clientName, String lanSubnet, String vpnIp) { + validateShellSafe(clientName); + validateShellSafe(lanSubnet); + validateShellSafe(vpnIp); + + return properties.getToolsPath() + + "/provision-client.sh " + + clientName + " " + + lanSubnet + " " + + vpnIp; + } + + private void validateShellSafe(String value) { + if (value == null || !value.matches("^[a-zA-Z0-9._/-]+$")) { + throw new IllegalArgumentException("Unsafe command value: " + value); + } + } } \ No newline at end of file diff --git a/src/main/java/com/litoralregas/openvpn/router/RouterController.java b/src/main/java/com/litoralregas/openvpn/router/RouterController.java index 85cf988..597225c 100644 --- a/src/main/java/com/litoralregas/openvpn/router/RouterController.java +++ b/src/main/java/com/litoralregas/openvpn/router/RouterController.java @@ -3,6 +3,8 @@ package com.litoralregas.openvpn.router; import com.litoralregas.openvpn.deployment.DeploymentAction; import com.litoralregas.openvpn.deployment.DeploymentResponse; import com.litoralregas.openvpn.deployment.DeploymentService; +import com.litoralregas.openvpn.openvpn.IpAllocationService; +import com.litoralregas.openvpn.openvpn.OpenVpnService; import com.litoralregas.openvpn.ssh.SshService; import jakarta.validation.Valid; import org.springframework.web.bind.annotation.*; @@ -17,11 +19,15 @@ public class RouterController { private final RouterService service; private final DeploymentService deploymentService; private final SshService sshService; + private final IpAllocationService ipAllocationService; + private final OpenVpnService openVpnService; - public RouterController(RouterService service, DeploymentService deploymentService, SshService sshService) { + public RouterController(RouterService service, DeploymentService deploymentService, SshService sshService, IpAllocationService ipAllocationService, OpenVpnService openVpnService) { this.service = service; this.deploymentService = deploymentService; this.sshService = sshService; + this.ipAllocationService = ipAllocationService; + this.openVpnService = openVpnService; } @GetMapping @@ -61,20 +67,22 @@ public class RouterController { try { service.forceStatus(id, RouterStatus.PROVISIONING); - var result = sshService.executeOnConfiguredVps( - "echo 'Provisioning router: " + router.getName() + "' && whoami && hostname" - ); + var allocation = ipAllocationService.findByRouterId(id); - if (result.exitCode() != 0) { - throw new IllegalStateException(result.stderr()); - } + String command = openVpnService.buildProvisionCommand( + allocation.getClientName(), + allocation.getLanSubnet(), + allocation.getVpnIp() + ); var finishedDeployment = deploymentService.finishSuccess( deployment, - result.stdout() + "DRY RUN ONLY. Would execute: " + command ); - service.forceStatus(id, RouterStatus.REMOVING); + // Keep this as PROVISIONING? No — dry run succeeded but real provision did not happen. + // So we should NOT mark as PROVISIONED yet. + service.forceStatus(id, RouterStatus.PENDING); return DeploymentResponse.from(finishedDeployment); } catch (Exception exception) {