Issue
My goal is to trigger an SSH login to my GCP instance and start my node server when a website is hit. My function crashes early in the process.
POST500736 B336 msGoogle-Cloud-Functions https://function-name
Reading private key file contents:
Error starting Node.js server on VM tr-instance-1: Error: ENOENT: no such file or directory
While this sounds like the SSH key doesn't exist, it indeed does.
- I have recreated the keys multiple times and retested.
- I am sure the key is in RSA/PEM format.
- I have appropriate (600) permissions on the key file.
- I have confirmed that I am using the correct VM external IP.
I believe it's not making it to the key location correctly because it is failing to connect to the VM via SSH. In other words, the error is perhaps misleading.
I am able to automatically log into my instance via SSH through the VM console. However, when I try to log in via a manual SSH (i.e., using the private key from my local computer to SSH in), I get an error that I'm not able to upload my key file.
Failed to upload your file. nf
This error remains when I recreate the keys and use a brand new one from my local machine. It is simply the RSA private key with limited text. I don't know if this is a separate issue or is related to my function not connecting to my SSH. It seems odd that I can't use this key, and the error message is not helpful.
Here is my node.js function code. The package.json has firebase-functions and node-ssh.
const functions = require('firebase-functions');
const { NodeSSH } = require('node-ssh');
const startServer = async (instanceName) => {
try {
const ssh = new NodeSSH();
const fs = require('fs');
const privateKeyPath = '/home/username/.ssh/ai_keys';
console.log('Reading private key file contents:');
const privateKeyContents = fs.readFileSync(privateKeyPath, 'utf8');
console.log(privateKeyContents);
// Continue with the SSH connection code
// Connect to the VM via SSH
await ssh.connect({
host: 'external.IP.here', // VM's external IP
username: 'my_username', // VM's SSH username
privateKey: privateKeyPath, // SSH private key file path
});
// Check if SSH connection is established successfully
if (ssh.isConnected()) {
console.log(`SSH connection to VM ${instanceName} established successfully.`);
} else {
console.error(`Failed to establish SSH connection to VM ${instanceName}.`);
return false;
}
// Run the Node.js server start command
await ssh.execCommand('cd /user/project_dir && node server.js');
// Disconnect the SSH connection
ssh.dispose();
console.log(`Node.js server on VM ${instanceName} started successfully.`);
return true;
} catch (error) {
console.error(`Error starting Node.js server on VM ${instanceName}:`, error);
return false;
}
};
exports.startServer = functions.https.onRequest(async (req, res) => {
// Set CORS headers to allow requests from a specific origin
res.set('Access-Control-Allow-Origin', 'https://example.com');
// Check if the request method is OPTIONS (pre-flight request)
if (req.method === 'OPTIONS') {
// Set CORS headers for pre-flight requests
res.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.set('Access-Control-Allow-Headers', 'Content-Type');
res.status(204).send('');
return;
}
const instanceName = 'my_instance_name'; // instance name
const success = await startServer(instanceName);
if (success) {
res.status(200).send('Node.js server started successfully.');
} else {
res.status(500).send('Error starting Node.js server.');
}
});
I am fairly new at this, so please don't overlook a basic and/or stupid mistake! Thanks in advance.
Solution
You're assuming that /home/username/.ssh/ai_keys exists on the Cloud Functions instance running your code. It almost certainly does not.
If you want some file to be available to code running in Cloud Functions, you will have to arrange for that file to be part of your deployment. That means it's going to have to be in the folder along with your source code. Read the documentation for the --source
flag for gcloud functions deploy
.
You can read those files at runtime relative to the current working directory (don't try to code a full path to that file - you don't necessarily know what that's going to be.
Answered By - Doug Stevenson Answer Checked By - Mary Flores (WPSolving Volunteer)