fix(installer): Improve Node.js binary detection, dependency management, and SNMPv3 fallback logic
This commit is contained in:
parent
5a13e49803
commit
4de6081a74
55
bin/nupst
55
bin/nupst
@ -22,16 +22,63 @@ fi
|
|||||||
# For debugging
|
# For debugging
|
||||||
# echo "Project root: $PROJECT_ROOT"
|
# echo "Project root: $PROJECT_ROOT"
|
||||||
|
|
||||||
# Set Node.js binary path directly
|
# Detect architecture and OS
|
||||||
NODE_BIN="$PROJECT_ROOT/vendor/node-linux-x64/bin/node"
|
ARCH=$(uname -m)
|
||||||
|
OS=$(uname -s)
|
||||||
|
|
||||||
|
# Determine Node.js binary location based on architecture and OS
|
||||||
|
NODE_BIN=""
|
||||||
|
case "$OS" in
|
||||||
|
Linux)
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64)
|
||||||
|
NODE_BIN="$PROJECT_ROOT/vendor/node-linux-x64/bin/node"
|
||||||
|
;;
|
||||||
|
aarch64|arm64)
|
||||||
|
NODE_BIN="$PROJECT_ROOT/vendor/node-linux-arm64/bin/node"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Use system Node as fallback for other architectures
|
||||||
|
if command -v node &> /dev/null; then
|
||||||
|
NODE_BIN="node"
|
||||||
|
echo "Using system Node.js installation for unsupported architecture: $ARCH"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
Darwin)
|
||||||
|
case "$ARCH" in
|
||||||
|
x86_64)
|
||||||
|
NODE_BIN="$PROJECT_ROOT/vendor/node-darwin-x64/bin/node"
|
||||||
|
;;
|
||||||
|
arm64)
|
||||||
|
NODE_BIN="$PROJECT_ROOT/vendor/node-darwin-arm64/bin/node"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Use system Node as fallback for other architectures
|
||||||
|
if command -v node &> /dev/null; then
|
||||||
|
NODE_BIN="node"
|
||||||
|
echo "Using system Node.js installation for unsupported architecture: $ARCH"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
# Use system Node as fallback for other operating systems
|
||||||
|
if command -v node &> /dev/null; then
|
||||||
|
NODE_BIN="node"
|
||||||
|
echo "Using system Node.js installation for unsupported OS: $OS"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
# If binary doesn't exist, try system Node as fallback
|
# If binary doesn't exist, try system Node as fallback
|
||||||
if [ ! -f "$NODE_BIN" ]; then
|
if [ -z "$NODE_BIN" ] || [ ! -f "$NODE_BIN" ]; then
|
||||||
if command -v node &> /dev/null; then
|
if command -v node &> /dev/null; then
|
||||||
NODE_BIN="node"
|
NODE_BIN="node"
|
||||||
echo "Using system Node.js installation"
|
echo "Using system Node.js installation"
|
||||||
else
|
else
|
||||||
echo "Error: Node.js binary not found at $NODE_BIN"
|
echo "Error: Node.js binary not found for $OS-$ARCH"
|
||||||
echo "Please run the setup script or install Node.js manually."
|
echo "Please run the setup script or install Node.js manually."
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
@ -1,5 +1,14 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 2025-03-26 - 2.5.2 - fix(installer)
|
||||||
|
Improve Node.js binary detection, dependency management, and SNMPv3 fallback logic
|
||||||
|
|
||||||
|
- Enhanced bin/nupst to detect OS and architecture (Linux and Darwin) and fall back to system Node.js for unsupported platforms
|
||||||
|
- Moved net-snmp from devDependencies to dependencies in package.json
|
||||||
|
- Updated setup.sh to install production dependencies and handle installation errors gracefully
|
||||||
|
- Refined SNMPv3 user configuration and fallback mechanism in ts/snmp/manager.ts
|
||||||
|
- Revised README to clarify minimal runtime dependencies and secure SNMP features
|
||||||
|
|
||||||
## 2025-03-25 - 2.5.1 - fix(snmp)
|
## 2025-03-25 - 2.5.1 - fix(snmp)
|
||||||
Fix Eaton UPS support by updating power status OID and adjusting battery runtime conversion.
|
Fix Eaton UPS support by updating power status OID and adjusting battery runtime conversion.
|
||||||
|
|
||||||
|
@ -37,9 +37,9 @@
|
|||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"net-snmp": "3.20.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"net-snmp": "3.20.0",
|
|
||||||
"@git.zone/tsbuild": "^2.3.2",
|
"@git.zone/tsbuild": "^2.3.2",
|
||||||
"@git.zone/tsrun": "^1.3.3",
|
"@git.zone/tsrun": "^1.3.3",
|
||||||
"@git.zone/tstest": "^1.0.96",
|
"@git.zone/tstest": "^1.0.96",
|
||||||
|
22
readme.md
22
readme.md
@ -236,10 +236,10 @@ NUPST was designed with security in mind:
|
|||||||
|
|
||||||
### Minimal Dependencies
|
### Minimal Dependencies
|
||||||
|
|
||||||
- **Zero Runtime NPM Dependencies**: NUPST is built without any external NPM packages to minimize the attack surface and avoid supply chain risks.
|
- **Minimal Runtime Dependencies**: NUPST uses only one carefully selected NPM package (net-snmp) to minimize the attack surface and avoid supply chain risks while providing robust SNMP functionality.
|
||||||
- **Self-contained Node.js**: NUPST ships with its own Node.js binary, isolated from the system's Node.js installation. This ensures:
|
- **Self-contained Node.js**: NUPST ships with its own Node.js binary, isolated from the system's Node.js installation. This ensures:
|
||||||
- No dependency on system Node.js versions
|
- No dependency on system Node.js versions
|
||||||
- Zero external libraries that could become compromised
|
- Minimal external libraries that could become compromised
|
||||||
- Consistent, tested environment for execution
|
- Consistent, tested environment for execution
|
||||||
- Reduced risk of dependency-based attacks
|
- Reduced risk of dependency-based attacks
|
||||||
|
|
||||||
@ -247,14 +247,30 @@ NUPST was designed with security in mind:
|
|||||||
|
|
||||||
- **Privilege Separation**: Only specific commands that require elevated permissions (`enable`, `disable`, `update`) check for root access; all other functionality runs with minimal privileges.
|
- **Privilege Separation**: Only specific commands that require elevated permissions (`enable`, `disable`, `update`) check for root access; all other functionality runs with minimal privileges.
|
||||||
- **Limited Network Access**: NUPST only communicates with the UPS device over SNMP and contacts npmjs.org only to check for updates.
|
- **Limited Network Access**: NUPST only communicates with the UPS device over SNMP and contacts npmjs.org only to check for updates.
|
||||||
- **Secure SNMPv3 Support**: Supports encrypted authentication and privacy for secure communication with the UPS device.
|
|
||||||
- **Isolated Execution**: The application runs in its working directory (`/opt/nupst`) or specified installation location, minimizing the impact on the rest of the system.
|
- **Isolated Execution**: The application runs in its working directory (`/opt/nupst`) or specified installation location, minimizing the impact on the rest of the system.
|
||||||
|
|
||||||
|
### SNMP Security Features
|
||||||
|
|
||||||
|
- **SNMPv3 Support with Secure Authentication and Privacy**:
|
||||||
|
- Three security levels available:
|
||||||
|
- `noAuthNoPriv`: No authentication or encryption (basic access)
|
||||||
|
- `authNoPriv`: Authentication without encryption (verifies identity)
|
||||||
|
- `authPriv`: Full authentication and encryption (most secure)
|
||||||
|
- Authentication protocols: MD5 or SHA
|
||||||
|
- Privacy/encryption protocols: DES or AES
|
||||||
|
- Automatic fallback mechanisms for compatibility
|
||||||
|
- Context support for segmented SNMP deployments
|
||||||
|
- Configurable timeouts based on security level
|
||||||
|
- **Graceful degradation**: If authentication or privacy details are missing or invalid, NUPST will automatically fall back to a lower security level while logging appropriate warnings.
|
||||||
|
- **Interactive setup**: Guided setup process to properly configure SNMPv3 security settings with clear explanations of each security option.
|
||||||
|
|
||||||
### Installation Security
|
### Installation Security
|
||||||
|
|
||||||
- The installation script can be reviewed before execution (`curl -sSL [url] | less`)
|
- The installation script can be reviewed before execution (`curl -sSL [url] | less`)
|
||||||
- All setup scripts download only verified versions and check integrity
|
- All setup scripts download only verified versions and check integrity
|
||||||
- Installation is transparent and places files in standard locations (`/opt/nupst`, `/usr/local/bin`, `/etc/systemd/system`)
|
- Installation is transparent and places files in standard locations (`/opt/nupst`, `/usr/local/bin`, `/etc/systemd/system`)
|
||||||
|
- Automatically detects platform architecture and OS for proper binary selection
|
||||||
|
- Installs production dependencies locally without requiring global npm packages
|
||||||
|
|
||||||
### Audit and Review
|
### Audit and Review
|
||||||
|
|
||||||
|
19
setup.sh
19
setup.sh
@ -222,6 +222,25 @@ echo "dist_ts directory successfully downloaded from npm registry."
|
|||||||
# Make launcher script executable
|
# Make launcher script executable
|
||||||
chmod +x "$SCRIPT_DIR/bin/nupst"
|
chmod +x "$SCRIPT_DIR/bin/nupst"
|
||||||
|
|
||||||
|
# Install production dependencies
|
||||||
|
echo "Installing production dependencies..."
|
||||||
|
"$SCRIPT_DIR/vendor/$NODE_DIR/bin/npm" --prefix "$SCRIPT_DIR" ci --only=production --no-audit --no-fund
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Warning: Failed to install dependencies with 'npm ci'. Trying 'npm install'..."
|
||||||
|
"$SCRIPT_DIR/vendor/$NODE_DIR/bin/npm" --prefix "$SCRIPT_DIR" install --only=production --no-audit --no-fund
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
echo "Error: Failed to install dependencies. NUPST may not function correctly."
|
||||||
|
echo "You can try to install dependencies manually by running:"
|
||||||
|
echo "cd $SCRIPT_DIR && npm install --only=production"
|
||||||
|
else
|
||||||
|
echo "Dependencies installed successfully with 'npm install'."
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "Dependencies installed successfully with 'npm ci'."
|
||||||
|
fi
|
||||||
|
|
||||||
echo "NUPST setup completed successfully."
|
echo "NUPST setup completed successfully."
|
||||||
echo "You can now run NUPST using: $SCRIPT_DIR/bin/nupst"
|
echo "You can now run NUPST using: $SCRIPT_DIR/bin/nupst"
|
||||||
echo "To install NUPST globally, run: sudo ln -s $SCRIPT_DIR/bin/nupst /usr/local/bin/nupst"
|
echo "To install NUPST globally, run: sudo ln -s $SCRIPT_DIR/bin/nupst /usr/local/bin/nupst"
|
||||||
|
@ -3,6 +3,6 @@
|
|||||||
*/
|
*/
|
||||||
export const commitinfo = {
|
export const commitinfo = {
|
||||||
name: '@serve.zone/nupst',
|
name: '@serve.zone/nupst',
|
||||||
version: '2.5.1',
|
version: '2.5.2',
|
||||||
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
description: 'Node.js UPS Shutdown Tool for SNMP-enabled UPS devices'
|
||||||
}
|
}
|
||||||
|
@ -105,13 +105,14 @@ export class NupstSnmp {
|
|||||||
retries: 2, // Number of retries
|
retries: 2, // Number of retries
|
||||||
timeout: config.timeout,
|
timeout: config.timeout,
|
||||||
transport: 'udp4',
|
transport: 'udp4',
|
||||||
idBitsSize: 32
|
idBitsSize: 32,
|
||||||
|
context: config.context || ''
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set version based on config
|
// Set version based on config
|
||||||
if (config.version === 1) {
|
if (config.version === 1) {
|
||||||
options.version = snmp.Version1;
|
options.version = snmp.Version1;
|
||||||
} else if (config.version === 2 || config.version === 2) {
|
} else if (config.version === 2) {
|
||||||
options.version = snmp.Version2c;
|
options.version = snmp.Version2c;
|
||||||
} else {
|
} else {
|
||||||
options.version = snmp.Version3;
|
options.version = snmp.Version3;
|
||||||
@ -122,15 +123,82 @@ export class NupstSnmp {
|
|||||||
|
|
||||||
if (config.version === 3) {
|
if (config.version === 3) {
|
||||||
// For SNMPv3, we need to set up authentication and privacy
|
// For SNMPv3, we need to set up authentication and privacy
|
||||||
const user = {
|
// For SNMPv3, we need a valid security level
|
||||||
name: config.username || '',
|
const securityLevel = config.securityLevel || 'noAuthNoPriv';
|
||||||
level: snmp.SecurityLevel[config.securityLevel || 'noAuthNoPriv'],
|
|
||||||
authProtocol: config.authProtocol ? snmp.AuthProtocols[config.authProtocol] : undefined,
|
// Create the user object with required structure for net-snmp
|
||||||
authKey: config.authKey || '',
|
const user: any = {
|
||||||
privProtocol: config.privProtocol ? snmp.PrivProtocols[config.privProtocol] : undefined,
|
name: config.username || ''
|
||||||
privKey: config.privKey || ''
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Set security level
|
||||||
|
if (securityLevel === 'noAuthNoPriv') {
|
||||||
|
user.level = snmp.SecurityLevel.noAuthNoPriv;
|
||||||
|
} else if (securityLevel === 'authNoPriv') {
|
||||||
|
user.level = snmp.SecurityLevel.authNoPriv;
|
||||||
|
|
||||||
|
// Set auth protocol - must provide both protocol and key
|
||||||
|
if (config.authProtocol && config.authKey) {
|
||||||
|
if (config.authProtocol === 'MD5') {
|
||||||
|
user.authProtocol = snmp.AuthProtocols.md5;
|
||||||
|
} else if (config.authProtocol === 'SHA') {
|
||||||
|
user.authProtocol = snmp.AuthProtocols.sha;
|
||||||
|
}
|
||||||
|
user.authKey = config.authKey;
|
||||||
|
} else {
|
||||||
|
// Fallback to noAuthNoPriv if auth details missing
|
||||||
|
user.level = snmp.SecurityLevel.noAuthNoPriv;
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('Warning: Missing authProtocol or authKey, falling back to noAuthNoPriv');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (securityLevel === 'authPriv') {
|
||||||
|
user.level = snmp.SecurityLevel.authPriv;
|
||||||
|
|
||||||
|
// Set auth protocol - must provide both protocol and key
|
||||||
|
if (config.authProtocol && config.authKey) {
|
||||||
|
if (config.authProtocol === 'MD5') {
|
||||||
|
user.authProtocol = snmp.AuthProtocols.md5;
|
||||||
|
} else if (config.authProtocol === 'SHA') {
|
||||||
|
user.authProtocol = snmp.AuthProtocols.sha;
|
||||||
|
}
|
||||||
|
user.authKey = config.authKey;
|
||||||
|
|
||||||
|
// Set privacy protocol - must provide both protocol and key
|
||||||
|
if (config.privProtocol && config.privKey) {
|
||||||
|
if (config.privProtocol === 'DES') {
|
||||||
|
user.privProtocol = snmp.PrivProtocols.des;
|
||||||
|
} else if (config.privProtocol === 'AES') {
|
||||||
|
user.privProtocol = snmp.PrivProtocols.aes;
|
||||||
|
}
|
||||||
|
user.privKey = config.privKey;
|
||||||
|
} else {
|
||||||
|
// Fallback to authNoPriv if priv details missing
|
||||||
|
user.level = snmp.SecurityLevel.authNoPriv;
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('Warning: Missing privProtocol or privKey, falling back to authNoPriv');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Fallback to noAuthNoPriv if auth details missing
|
||||||
|
user.level = snmp.SecurityLevel.noAuthNoPriv;
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('Warning: Missing authProtocol or authKey, falling back to noAuthNoPriv');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.debug) {
|
||||||
|
console.log('SNMPv3 user configuration:', {
|
||||||
|
name: user.name,
|
||||||
|
level: Object.keys(snmp.SecurityLevel).find(key => snmp.SecurityLevel[key] === user.level),
|
||||||
|
authProtocol: user.authProtocol ? 'Set' : 'Not Set',
|
||||||
|
authKey: user.authKey ? 'Set' : 'Not Set',
|
||||||
|
privProtocol: user.privProtocol ? 'Set' : 'Not Set',
|
||||||
|
privKey: user.privKey ? 'Set' : 'Not Set'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
session = snmp.createV3Session(config.host, user, options);
|
session = snmp.createV3Session(config.host, user, options);
|
||||||
} else {
|
} else {
|
||||||
// For SNMPv1/v2c, we use the community string
|
// For SNMPv1/v2c, we use the community string
|
||||||
|
Loading…
x
Reference in New Issue
Block a user