Add safe OpenVPN preflight and dry-run provisioning
This commit is contained in:
@@ -6,6 +6,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
|
|||||||
public class OpenVpnProperties {
|
public class OpenVpnProperties {
|
||||||
|
|
||||||
private String toolsPath;
|
private String toolsPath;
|
||||||
|
private boolean provisionDryRun = true;
|
||||||
|
|
||||||
public String getToolsPath() {
|
public String getToolsPath() {
|
||||||
return toolsPath;
|
return toolsPath;
|
||||||
@@ -14,4 +15,11 @@ public class OpenVpnProperties {
|
|||||||
public void setToolsPath(String toolsPath) {
|
public void setToolsPath(String toolsPath) {
|
||||||
this.toolsPath = 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;
|
package com.litoralregas.openvpn.openvpn;
|
||||||
|
|
||||||
|
import com.litoralregas.openvpn.ssh.SshCommandResult;
|
||||||
import com.litoralregas.openvpn.ssh.SshService;
|
import com.litoralregas.openvpn.ssh.SshService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -82,4 +83,34 @@ public class OpenVpnService {
|
|||||||
throw new IllegalArgumentException("Unsafe command value: " + value);
|
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);
|
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.getClientName(),
|
||||||
allocation.getLanSubnet(),
|
allocation.getLanSubnet(),
|
||||||
allocation.getVpnIp()
|
allocation.getVpnIp()
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (result.exitCode() != 0) {
|
||||||
|
throw new IllegalStateException(
|
||||||
|
"Provision failed. stdout: " + result.stdout()
|
||||||
|
+ " stderr: " + result.stderr()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
var finishedDeployment = deploymentService.finishSuccess(
|
var finishedDeployment = deploymentService.finishSuccess(
|
||||||
deployment,
|
deployment,
|
||||||
"DRY RUN ONLY. Would execute: " + command
|
result.stdout()
|
||||||
);
|
);
|
||||||
|
|
||||||
// Keep this as PROVISIONING? No — dry run succeeded but real provision did not happen.
|
if (openVpnService.isDryRun()) {
|
||||||
// So we should NOT mark as PROVISIONED yet.
|
service.forceStatus(id, RouterStatus.PENDING);
|
||||||
service.forceStatus(id, RouterStatus.PENDING);
|
} else {
|
||||||
|
service.forceStatus(id, RouterStatus.PROVISIONED);
|
||||||
|
}
|
||||||
|
|
||||||
return DeploymentResponse.from(finishedDeployment);
|
return DeploymentResponse.from(finishedDeployment);
|
||||||
} catch (Exception exception) {
|
} catch (Exception exception) {
|
||||||
|
|||||||
@@ -34,3 +34,4 @@ lr:
|
|||||||
|
|
||||||
openvpn:
|
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}
|
||||||
Reference in New Issue
Block a user