Add safe OpenVPN preflight and dry-run provisioning

This commit is contained in:
litoral05
2026-05-05 14:05:21 +01:00
parent 6f70cbfe67
commit 40ae52f00e
4 changed files with 64 additions and 6 deletions
@@ -6,6 +6,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
public class OpenVpnProperties {
private String toolsPath;
private boolean provisionDryRun = true;
public String getToolsPath() {
return toolsPath;
@@ -14,4 +15,11 @@ public class OpenVpnProperties {
public void setToolsPath(String toolsPath) {
this.toolsPath = toolsPath;
}
public boolean isProvisionDryRun() {
return provisionDryRun;
}
public void setProvisionDryRun(boolean provisionDryRun) {
this.provisionDryRun = provisionDryRun;
}
}
@@ -1,5 +1,6 @@
package com.litoralregas.openvpn.openvpn;
import com.litoralregas.openvpn.ssh.SshCommandResult;
import com.litoralregas.openvpn.ssh.SshService;
import org.springframework.stereotype.Service;
@@ -82,4 +83,34 @@ public class OpenVpnService {
throw new IllegalArgumentException("Unsafe command value: " + value);
}
}
public SshCommandResult runPreflightCheck() {
return sshService.executeOnConfiguredVps(
"test -x " + properties.getToolsPath() + "/provision-client.sh && " +
"test -x " + properties.getToolsPath() + "/remove-client.sh && " +
"test -x " + properties.getToolsPath() + "/list-clients.sh && " +
"test -d /etc/openvpn/easy-rsa && " +
"test -d /etc/openvpn/server/ccd && " +
"test -f /etc/openvpn/server/server.conf && " +
"systemctl is-active openvpn-server@server"
);
}
public SshCommandResult provisionClient(String clientName, String lanSubnet, String vpnIp) {
String command = buildProvisionCommand(clientName, lanSubnet, vpnIp);
if (properties.isProvisionDryRun()) {
return new SshCommandResult(
0,
"DRY RUN ONLY. Would execute: " + command,
""
);
}
return sshService.executeOnConfiguredVps(command);
}
public boolean isDryRun() {
return properties.isProvisionDryRun();
}
}
@@ -69,20 +69,38 @@ public class RouterController {
var allocation = ipAllocationService.findByRouterId(id);
String command = openVpnService.buildProvisionCommand(
var preflight = openVpnService.runPreflightCheck();
if (preflight.exitCode() != 0) {
throw new IllegalStateException(
"Preflight failed. stdout: " + preflight.stdout()
+ " stderr: " + preflight.stderr()
);
}
var result = openVpnService.provisionClient(
allocation.getClientName(),
allocation.getLanSubnet(),
allocation.getVpnIp()
);
if (result.exitCode() != 0) {
throw new IllegalStateException(
"Provision failed. stdout: " + result.stdout()
+ " stderr: " + result.stderr()
);
}
var finishedDeployment = deploymentService.finishSuccess(
deployment,
"DRY RUN ONLY. Would execute: " + command
result.stdout()
);
// 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);
if (openVpnService.isDryRun()) {
service.forceStatus(id, RouterStatus.PENDING);
} else {
service.forceStatus(id, RouterStatus.PROVISIONED);
}
return DeploymentResponse.from(finishedDeployment);
} catch (Exception exception) {
+2 -1
View File
@@ -33,4 +33,5 @@ lr:
password: ${LR_VPS_SSH_PASSWORD}
openvpn:
tools-path: ${LR_OPENVPN_TOOLS_PATH:/var/litoral_regas_openvpn/tools}
tools-path: ${LR_OPENVPN_TOOLS_PATH:/var/litoral_regas_openvpn/tools}
provision-dry-run: ${LR_OPENVPN_PROVISION_DRY_RUN:true}