You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何编写自动响应bash脚本交互的OpenVPN文件生成PHP脚本

自动回答build-key.sh交互问题的PHP实现方案

我之前也做过类似的OpenVPN客户端配置自动生成工具,刚好能解决你的问题。build-key.sh本质是依赖交互式输入的bash脚本,我们可以通过预定义输入流或者expect脚本两种方式让PHP自动填充这些提问,下面分场景给你具体方案:

场景1:不需要私钥密码(推荐大部分OpenVPN场景使用)

如果你的场景不需要给客户端私钥设置密码(大部分OpenVPN客户端部署都不需要),用here-document是最简洁的方案,它能把所有预设答案按顺序打包传给脚本:

步骤1:定义PHP变量存储预设值

先把需要填充的信息用变量存起来,方便后续维护:

// 预设的交互答案,根据你的需求修改
$country = 'CN';
$state = 'Beijing';
$city = 'Beijing';
$org = 'MyVPNOrganization';
$orgUnit = 'ClientServices';
$clientName = 'User001'; // 这个会作为证书的Common Name,建议设为唯一值
$email = 'support@myvpn.com';
$easyRsaPath = '/usr/share/easy-rsa'; // 你的easy-rsa目录路径

步骤2:构造带here-document的执行命令

把所有答案按build-key.sh的提问顺序放在EOF标记之间,同时切换到easy-rsa目录执行脚本:

// 转义客户端名称防止命令注入
$escapedClientName = escapeshellarg($clientName);

// 构造完整命令
$command = <<<CMD
cd {$easyRsaPath} && ./build-key.sh {$escapedClientName} << EOF
{$country}
{$state}
{$city}
{$org}
{$orgUnit}
{$clientName}
EasyRSA
{$email}
y
y
EOF
CMD;

// 执行命令并捕获输出和返回码
exec($command, $output, $returnCode);

// 检查执行结果
if ($returnCode === 0) {
    echo "证书生成成功!";
    // 这里衔接你已有的生成.ovpn文件并下载的逻辑
} else {
    echo "生成失败,错误信息:" . implode("\n", $output);
}

注意:末尾的两个y是用来回答脚本最后的确认问题("Sign the certificate? [y/n]"和"Commit? [y/n]")。如果你的easy-rsa版本提问顺序不同,建议先手动执行一次./build-key.sh test,把所有提问顺序记下来,再调整EOF里的内容顺序。

场景2:需要设置私钥密码

如果必须给客户端私钥设置密码,here-document就无法处理动态的密码输入了,这时候需要用expect工具来监听并自动响应脚本的交互:

步骤1:安装expect工具

在服务器上先安装expect(以Ubuntu为例):

sudo apt update && sudo apt install expect -y

步骤2:编写expect脚本(auto-build-key.exp)

在easy-rsa目录下创建这个脚本,用来监听build-key.sh的提问并自动回答:

#!/usr/bin/expect -f
# 接收PHP传递的参数
set clientName [lindex $argv 0]
set country [lindex $argv 1]
set state [lindex $argv 2]
set city [lindex $argv 3]
set org [lindex $argv 4]
set orgUnit [lindex $argv 5]
set email [lindex $argv 6]
set passphrase [lindex $argv 7]

# 启动build-key.sh脚本
spawn ./build-key.sh $clientName

# 匹配并回答每个提问
expect "Country Name (2 letter code) \[US\]:"
send "$country\r"

expect "State or Province Name (full name) \[California\]:"
send "$state\r"

expect "Locality Name (eg, city) \[SanFrancisco\]:"
send "$city\r"

expect "Organization Name (eg, company) \[Fort-Funston\]:"
send "$org\r"

expect "Organizational Unit Name (eg, section) \[MyOrganizationalUnit\]:"
send "$orgUnit\r"

expect "Common Name (eg, your name or your server's hostname) \[$clientName\]:"
send "$clientName\r"

expect "Name \[EasyRSA\]:"
send "\r" # 使用默认值,直接回车

expect "Email Address \[me@myhost.mydomain\]:"
send "$email\r"

# 处理私钥密码输入
expect "Enter PEM pass phrase:"
send "$passphrase\r"

expect "Verifying - Enter PEM pass phrase:"
send "$passphrase\r"

# 处理证书确认
expect "Sign the certificate? \[y/n\]:"
send "y\r"

expect "1 out of 1 certificate requests certified, commit? \[y/n\]:"
send "y\r"

# 等待脚本执行完成
expect eof

给脚本添加执行权限:

chmod +x /usr/share/easy-rsa/auto-build-key.exp

步骤3:PHP调用expect脚本

在PHP里构造命令,把参数传给expect脚本:

$clientName = 'User001';
$country = 'CN';
$state = 'Beijing';
$city = 'Beijing';
$org = 'MyVPNOrganization';
$orgUnit = 'ClientServices';
$email = 'support@myvpn.com';
$passphrase = 'MySecurePass123!'; // 客户端私钥密码
$easyRsaPath = '/usr/share/easy-rsa';

// 转义所有参数防止命令注入
$args = array_map('escapeshellarg', [
    $clientName, $country, $state, $city, $org, $orgUnit, $email, $passphrase
]);
$argsStr = implode(' ', $args);

$command = "cd {$easyRsaPath} && expect auto-build-key.exp {$argsStr}";
exec($command, $output, $returnCode);

if ($returnCode === 0) {
    echo "带密码的证书生成成功!";
} else {
    echo "生成失败:" . implode("\n", $output);
}

关键注意事项

  1. 权限问题:PHP运行的用户(通常是www-data)需要拥有easy-rsa目录的读写权限,以及build-key.sh、expect脚本的执行权限。可以用chown -R www-data:www-data /usr/share/easy-rsa来设置。
  2. 命令注入防护:所有作为命令参数的变量必须用escapeshellarg()转义,避免恶意输入破坏系统。
  3. 提问顺序验证:不同版本的easy-rsa脚本提问顺序可能略有不同,建议先手动执行一次./build-key.sh test,记录所有提问的顺序和默认值,再调整代码里的回答顺序。

内容的提问来源于stack exchange,提问作者tom318

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 07:36:20