如何编写自动响应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); }
关键注意事项
- 权限问题:PHP运行的用户(通常是www-data)需要拥有easy-rsa目录的读写权限,以及build-key.sh、expect脚本的执行权限。可以用
chown -R www-data:www-data /usr/share/easy-rsa来设置。 - 命令注入防护:所有作为命令参数的变量必须用
escapeshellarg()转义,避免恶意输入破坏系统。 - 提问顺序验证:不同版本的easy-rsa脚本提问顺序可能略有不同,建议先手动执行一次
./build-key.sh test,记录所有提问的顺序和默认值,再调整代码里的回答顺序。
内容的提问来源于stack exchange,提问作者tom318
相关产品推荐
相关产品推荐

