Firebase云函数调用Shell脚本报错文件不存在,手动执行正常
问题原因与解决方案
核心原因
Firebase云函数默认运行在Google Cloud的托管环境中,并非你的EC2实例本地。这意味着云函数的代码完全无法访问EC2实例上的文件系统——不管是直接用fs.appendFile操作文件,还是调用./addvips.sh脚本,都找不到EC2上的/home/ubuntu/serverfiles/csgo/addons/sourcemod/configs/admins_simple.ini路径,因为这个路径只存在于你的EC2实例里,云函数的运行环境根本没有这个文件。
而你手动执行脚本时,是在EC2实例本地操作,自然能正常访问本地文件系统,所以不会报错。
解决方法
方法1:在EC2上搭建HTTP服务,云函数通过接口触发脚本
- 在你的EC2实例上部署一个简单的Node.js HTTP服务(比如用Express),暴露一个接口(比如
POST /add-vip),接口内部执行addvips.sh脚本。 - 配置EC2的安全组,允许云函数的IP段或者公网访问这个接口(注意做好身份验证,比如API Key)。
- 修改Firebase云函数代码,用
axios或fetch调用EC2上的这个接口,传递steamID参数。
示例EC2上的Express服务代码:
const express = require('express'); const { exec } = require('child_process'); const app = express(); app.use(express.json()); // 身份验证中间件,检查请求头里的API Key const authMiddleware = (req, res, next) => { const apiKey = req.headers['x-api-key']; if (apiKey !== '你的安全密钥') { return res.status(403).send('Unauthorized'); } next(); }; app.post('/add-vip', authMiddleware, (req, res) => { const { steamID } = req.body; // 用参数数组避免命令注入 exec(`/home/ubuntu/addvips.sh ${steamID} a`, (err, stdout, stderr) => { if (err) { console.error(err); return res.status(500).send('执行失败'); } res.send('Success: ' + stdout); }); }); app.listen(3000, () => { console.log('EC2服务运行在3000端口'); });
修改后的云函数代码:
const admin = require('firebase-admin'); const serviceAccount = require('./raniserverFBkey.json'); const functions = require('firebase-functions'); const axios = require('axios'); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), }); exports.addSteamID1 = functions.firestore.document('steamIDs/{docID}') .onCreate(async (snap, context) => { const steamID = snap.data().steamID; try { await axios.post('http://你的EC2公网IP:3000/add-vip', { steamID }, { headers: { 'x-api-key': '你的安全密钥' } } ); console.log('VIP添加成功'); } catch (err) { console.error('调用EC2接口失败:', err); } });
方法2:云函数通过SSH连接EC2执行脚本
- 生成SSH密钥对,把公钥添加到EC2实例的
~ubuntu/.ssh/authorized_keys里。 - 将私钥存储到Firebase Secrets中(避免硬编码)。
- 在云函数中使用
ssh2之类的库,连接到EC2实例并执行脚本。
示例云函数代码(使用ssh2):
const admin = require('firebase-admin'); const serviceAccount = require('./raniserverFBkey.json'); const functions = require('firebase-functions'); const { Client } = require('ssh2'); admin.initializeApp({ credential: admin.credential.cert(serviceAccount), }); exports.addSteamID1 = functions.firestore.document('steamIDs/{docID}') .onCreate(async (snap, context) => { const steamID = snap.data().steamID; const privateKey = functions.config().ec2.ssh_key; // 从Firebase Secrets获取私钥 const conn = new Client(); conn.on('ready', () => { conn.exec(`/home/ubuntu/addvips.sh ${steamID} a`, (err, stream) => { if (err) throw err; stream.on('close', (code, signal) => { console.log('脚本执行完成,退出码:', code); conn.end(); }).on('data', (data) => { console.log('stdout:', data.toString()); }).stderr.on('data', (data) => { console.error('stderr:', data.toString()); }); }); }).connect({ host: '你的EC2公网IP', port: 22, username: 'ubuntu', privateKey: privateKey }); });
关键注意事项
- 确保EC2安全组配置正确,允许对应的请求(HTTP/SSH)进入。
- 处理用户输入时必须做安全校验,比如对steamID进行转义,避免命令注入攻击(优先用exec的参数数组格式,而非字符串拼接)。
内容的提问来源于stack exchange,提问作者darinda
相关产品推荐
相关产品推荐

