@small-tech/auto-encrypt-localhost
Advanced tools
Comparing version 7.0.4 to 7.0.5
@@ -24,3 +24,2 @@ #!/usr/bin/env node | ||
import path from 'path' | ||
import childProcess from 'child_process' | ||
@@ -103,3 +102,4 @@ import { binaryPath as mkcertBinary } from '../lib/mkcert.js' | ||
// | ||
const caCertFilePath = path.join(settingsPath, 'rootCA.pem') | ||
const caKeyFilePath = path.join(settingsPath, 'rootCA-key.pem') | ||
const keyFilePath = path.join(settingsPath, 'localhost-key.pem') | ||
@@ -109,3 +109,6 @@ const certFilePath = path.join(settingsPath, 'localhost.pem') | ||
const allOK = () => { | ||
return fs.existsSync(path.join(settingsPath, 'rootCA.pem')) && fs.existsSync(path.join(settingsPath, 'rootCA-key.pem')) && fs.existsSync(path.join(settingsPath, 'localhost.pem')) && fs.existsSync(path.join(settingsPath, 'localhost-key.pem')) | ||
return fs.existsSync(caCertFilePath) | ||
&& fs.existsSync(caKeyFilePath) | ||
&& fs.existsSync(certFilePath) | ||
&& fs.existsSync(keyFilePath) | ||
} | ||
@@ -116,16 +119,24 @@ | ||
// that they can do it manually themselves. | ||
process.stdout.write(` ╰─ Installing certutil if necessary… `) | ||
installCertutil() | ||
process.stdout.write('done.\n') | ||
// mkcert uses the CAROOT environment variable to know where to create/find the certificate authority. | ||
// We also pass the rest of the system environment to the spawned processes. | ||
const mkcertProcessOptions = { | ||
env: process.env, | ||
stdio: 'pipe' // suppress output | ||
} | ||
mkcertProcessOptions.env.CAROOT = settingsPath | ||
// Support all local interfaces so that the machine can be reached over the local network via IPv4. | ||
// This is very useful for testing with multiple devices over the local area network without needing to expose | ||
// the machine over the wide area network/Internet using a service like ngrok. | ||
const localIPv4Addresses = | ||
Object.entries(os.networkInterfaces()) | ||
.map(iface => | ||
iface[1].filter(addresses => | ||
addresses.family === 'IPv4') | ||
.map(addresses => addresses.address)).flat() | ||
const certificateDetails = [ | ||
`-key-file=${keyFilePath}`, | ||
`-cert-file=${certFilePath}`, | ||
'localhost' | ||
].concat(localIPv4Addresses).join(' ') | ||
const account = os.userInfo().username | ||
// Create the local certificate authority. | ||
process.stdout.write(` ╰─ Creating local certificate authority (local CA) using mkcert… `) | ||
process.stdout.write(` ╰─ Initialising mkcert… `) | ||
@@ -140,6 +151,56 @@ // We are using the sudo-prompt package here, instead of childProcess.execFileSync() because | ||
// that works well with npm scripts. | ||
// | ||
// Since on macOS the certificate files are created with root permissions, we need to set | ||
// files back to regular account permissions afterwards (this is not an issue on Linux | ||
// where the files are created with regular account permissions even when the mkcert command | ||
// is launched using sudo). In any case, because of this, and because we want the person | ||
// to only enter their password once instead of multiple times, once for each command | ||
// (due to how sudo-prompt works), we first create a shell script and then we execute that. | ||
// | ||
// For Windows, we create and use a separate PowerShell script. | ||
let shellScriptTemplate | ||
let shellScriptFileName | ||
let shellScriptCommand | ||
const platform = os.platform() | ||
if (platform === 'linux' || platform === 'darwin') { | ||
shellScriptFileName = 'install-mkcert.sh' | ||
shellScriptCommand = `CAROOT=${settingsPath} /tmp/install-mkcert.sh` | ||
shellScriptTemplate = `#!/bin/bash | ||
set -e | ||
# Install mkcert and create the certificate authority | ||
${mkcertBinary} -install | ||
# Create the certificates | ||
${mkcertBinary} ${certificateDetails} | ||
# Reset file permissions to regular account | ||
chown -R ${account} ${settingsPath} | ||
` | ||
} else if (platform === 'win32') { | ||
shellScriptFileName = 'install-mkcert.ps1' | ||
shellScriptCommand = 'powershell.exe /tmp/install-mkcert.ps1' | ||
shellScriptTemplate = ` | ||
# Set the environment variable | ||
$env:CAROOT="${settingsPath}" | ||
# Install mkcert and create the certificate authority | ||
${mkcertBinary} -install | ||
# Create the certificates | ||
${mkcertBinary} ${certificateDetails} | ||
` | ||
} else { | ||
throw new Error(`Sorry, this module is not tested or supported on your platform (${platform}).`) | ||
} | ||
fs.writeFileSync(`/tmp/${shellScriptFileName}`, shellScriptTemplate, {mode: 0o755}) | ||
await (() => { | ||
return new Promise((resolve, reject) => { | ||
sudoPrompt.exec(`${mkcertBinary} -install`, {name: 'Auto Encrypt Localhost'}, function(error, stdout, stderr) { | ||
const options = { name: 'Auto Encrypt Localhost' } | ||
// Note: mkcert uses the CAROOT environment variable to know where to create/find the certificate authority. | ||
sudoPrompt.exec(shellScriptCommand, options, function(error, stdout, stderr) { | ||
if (error) reject(error) | ||
@@ -153,26 +214,2 @@ resolve() | ||
// Create the local certificate. | ||
process.stdout.write(' ╰─ Creating local TLS certificates using mkcert… ') | ||
// Support all local interfaces so that the machine can be reached over the local network via IPv4. | ||
// This is very useful for testing with multiple devices over the local area network without needing to expose | ||
// the machine over the wide area network/Internet using a service like ngrok. | ||
const localIPv4Addresses = | ||
Object.entries(os.networkInterfaces()) | ||
.map(iface => | ||
iface[1].filter(addresses => | ||
addresses.family === 'IPv4') | ||
.map(addresses => addresses.address)).flat() | ||
const certificateDetails = [ | ||
`-key-file=${keyFilePath}`, | ||
`-cert-file=${certFilePath}`, | ||
'localhost' | ||
].concat(localIPv4Addresses) | ||
// We can use a regular execFileSync call here as the sudo permissions will not have timed out yet. | ||
childProcess.execFileSync(mkcertBinary, certificateDetails, mkcertProcessOptions) | ||
process.stdout.write('done.\n') | ||
// This should never happen as an error in the above, if there is one, | ||
@@ -179,0 +216,0 @@ // should exit the process, but just in case. |
@@ -8,2 +8,12 @@ # Changelog | ||
## [7.0.5] - 2021-03-19 | ||
### Fixed | ||
- Installation on macOS. This was failing because of differences in how the graphical sudo prompt affects file permissions between Linux and macOS. | ||
### Added | ||
- Re-implemented Windows support (tested/supported only on Windows 10, under Windows Terminal, with PowerShell). | ||
## [7.0.4] - 2021-03-17 | ||
@@ -10,0 +20,0 @@ |
@@ -9,2 +9,3 @@ import os from 'os' | ||
export default function installCertutil () { | ||
process.stdout.write(` ╰─ Installing certutil if necessary… `) | ||
const platform = os.platform() | ||
@@ -18,5 +19,6 @@ if (platform === 'linux') { | ||
// required on Windows. | ||
console.log('done.') | ||
} else { | ||
// Unknown platform. This should have been caught earlier. Panic. | ||
throw new Error(' 🤯 ❨auto-encrypt-localhost❩ Panic: Unknown platform detected.', platform) | ||
throw new Error(' ╰─ 🤯 Panic: Unknown platform detected.', platform) | ||
} | ||
@@ -33,5 +35,9 @@ } | ||
function installCertutilOnLinux () { | ||
if (commandExists('certutil')) return // Already installed | ||
if (commandExists('certutil')) { | ||
// Already installed | ||
process.stdout.write('done.\n') | ||
return | ||
} | ||
print(' 📜 ❨auto-encrypt-localhost❩ Installing certutil dependency (Linux) ') | ||
print('\n ╰─ Installing certutil dependency (Linux) ') | ||
let options = { env: process.env } | ||
@@ -45,15 +51,15 @@ try { | ||
// Untested: if you test this, please let me know https://github.com/indie-mirror/https-server/issues | ||
log('\n 🤪 ❨auto-encrypt-localhost❩ Attempting to install required dependency using yum. This is currently untested. If it works (or blows up) for you, I’d appreciate it if you could open an issue at https://github.com/indie-mirror/https-server/issues and let me know. Thanks! – Aral\n') | ||
log(' ╰─ 🤪 Attempting to install required dependency using yum. This is currently untested. If it works (or blows up) for you, I’d appreciate it if you could open an issue at https://github.com/indie-mirror/https-server/issues and let me know. Thanks! – Aral\n') | ||
childProcess.execSync('sudo yum install nss-tools', options) | ||
log(' 📜 ❨auto-encrypt-localhost❩ Certutil installed using yum.') | ||
log(' ╰─ Certutil installed using yum.') | ||
} else if (commandExists('pacman')) { | ||
childProcess.execSync('sudo pacman -S nss', options) | ||
log(' 📜 ❨auto-encrypt-localhost❩ Certutil installed using pacman.') | ||
log(' ╰─ Certutil installed using pacman.') | ||
} else { | ||
// Neither Homebrew nor MacPorts is installed. Warn the person. | ||
log('\n ⚠️ ❨auto-encrypt-localhost❩ Linux: No supported package manager found for installing certutil on Linux (tried apt, yum, and pacman. Please install certutil manually and run Auto Encrypt Localhost again. For more instructions on installing mkcert dependencies, please see https://github.com/FiloSottile/mkcert/\n') | ||
log(' ╰─ ⚠️ Linux: No supported package manager found for installing certutil on Linux (tried apt, yum, and pacman. Please install certutil manually and run Auto Encrypt Localhost again. For more instructions on installing mkcert dependencies, please see https://github.com/FiloSottile/mkcert/\n') | ||
} | ||
} catch (error) { | ||
// There was an error and we couldn’t install the dependency. Warn the person. | ||
log('\n ⚠️ ❨auto-encrypt-localhost❩ Linux: Failed to install nss. Please install it manually and run Auto Encrypt Localhost again if you want your certificate to work in Chrome and Firefox', error) | ||
log(' ╰─ ⚠️ Linux: Failed to install nss. Please install it manually and run Auto Encrypt Localhost again if you want your certificate to work in Chrome and Firefox', error) | ||
} | ||
@@ -77,13 +83,12 @@ } | ||
// Homebrew can take a long time start, show current status. | ||
print(' 📜 ❨auto-encrypt-localhost❩ Checking if certutil dependency is installed (Darwin) using Homebrew… ') | ||
childProcess.execSync('brew list nss >/dev/null 2>&1', options) | ||
log(' ok.') | ||
log(' done.') | ||
} catch (error) { | ||
// NSS is not installed. Install it. | ||
try { | ||
print(' 📜 ❨auto-encrypt-localhost❩ Installing certutil dependency (Darwin) using Homebrew… ') | ||
print(' ╰─ Installing certutil dependency (Darwin) using Homebrew… ') | ||
childProcess.execSync('brew install nss >/dev/null 2>&1', options) | ||
log('done.') | ||
} catch (error) { | ||
log('\n ⚠️ ❨auto-encrypt-localhost❩ macOS: Failed to install nss via Homebrew. Please install it manually and run Auto Encrypt Localhost again if you want your certificate to work in Firefox', error) | ||
log(' ╰─ ⚠️ macOS: Failed to install nss via Homebrew. Please install it manually and run Auto Encrypt Localhost again if you want your certificate to work in Firefox', error) | ||
return | ||
@@ -96,3 +101,3 @@ } | ||
// by opening an issue on https://github.com/indie-mirror/https-server/issues | ||
log('\n 🤪 ❨auto-encrypt-localhost❩ Attempting to install required dependency using MacPorts. This is currently untested. If it works (or blows up) for you, I’d appreciate it if you could open an issue at https://github.com/indie-mirror/https-server/issues and let me know. Thanks! – Aral\n') | ||
log(' ╰─ 🤪 Attempting to install required dependency using MacPorts. This is currently untested. If it works (or blows up) for you, I’d appreciate it if you could open an issue at https://github.com/indie-mirror/https-server/issues and let me know. Thanks! – Aral\n') | ||
@@ -106,3 +111,3 @@ try { | ||
} catch (error) { | ||
log('\n ⚠️ ❨auto-encrypt-localhost❩ macOS: Failed to install nss via MacPorts. Please install it manually and run Auto Encrypt Localhost again if you want your certificate to work in Firefox', error) | ||
log(' ╰─ ⚠️ macOS: Failed to install nss via MacPorts. Please install it manually and run Auto Encrypt Localhost again if you want your certificate to work in Firefox', error) | ||
return | ||
@@ -113,3 +118,3 @@ } | ||
// Neither Homebrew nor MacPorts is installed. Warn the person. | ||
log('\n ⚠️ ❨auto-encrypt-localhost❩ macOS: Cannot install certutil (nss) as you don’t have Homebrew or MacPorts installed.\n\n If you want your certificate to work in Firefox, please install one of those package managers and then install nss manually:\n\n * Homebrew (https://brew.sh): brew install nss\n * MacPorts(https://macports.org): sudo port install nss\n') | ||
log(' ╰─ ⚠️ macOS: Cannot install certutil (nss) as you don’t have Homebrew or MacPorts installed.\n\n If you want your certificate to work in Firefox, please install one of those package managers and then install nss manually:\n ╰─ * Homebrew (https://brew.sh): brew install nss ╰─ * MacPorts(https://macports.org): sudo port install nss\n') | ||
return | ||
@@ -116,0 +121,0 @@ } |
{ | ||
"name": "@small-tech/auto-encrypt-localhost", | ||
"version": "7.0.4", | ||
"version": "7.0.5", | ||
"description": "Automatically provisions and installs locally-trusted TLS certificates for Node.js https servers (including Express.js, etc.) using mkcert.", | ||
@@ -25,5 +25,7 @@ "keywords": [ | ||
"scripts": { | ||
"postinstall": "bin/post-install.js", | ||
"postinstall": "node bin/post-install.js", | ||
"test": "QUIET=true esm-tape-runner 'test/**/*.js' | tap-monkey", | ||
"test-on-windows": "node test/index.js", | ||
"coverage": "QUIET=true c8 esm-tape-runner 'test/**/*.js' | tap-monkey", | ||
"coverage-on-windows": "c8 node .\\test\\index.js", | ||
"test-debug": "esm-tape-runner 'test/**/*.js' | tap-monkey", | ||
@@ -30,0 +32,0 @@ "coverage-debug": "c8 esm-tape-runner 'test/**/*.js' | tap-monkey", |
@@ -11,2 +11,10 @@ # Auto Encrypt Localhost | ||
## System requirements | ||
Tested and supported on: | ||
- Linux (tested with elementary OS Hera) | ||
- macOS (tested on Big Sur) | ||
- Windows 10 (tested in Windows Terminal with PowerShell) | ||
## Installation | ||
@@ -18,6 +26,8 @@ | ||
Note that during installation, Auto Encrypt Localhost will create your local certificate authority and install it in the system root store and generate locally-trusted certificates. These actions require elevated privileges (`sudo`). Since [npm does not handle sudo prompts correctly in lifecycle scripts](https://github.com/npm/cli/issues/2887), you will see a graphical sudo prompt pop up to ask you for your adminstrator password. Once you’ve provided it, installation will proceed as normal. | ||
Note that during installation, Auto Encrypt Localhost will create your local certificate authority and install it in the system root store and generate locally-trusted certificates. These actions require elevated privileges. Since [npm does not handle sudo prompts correctly in lifecycle scripts](https://github.com/npm/cli/issues/2887), you will see a graphical sudo prompt pop up to ask you for your adminstrator password. Once you’ve provided it, installation will proceed as normal. | ||
![Screenshot of graphical sudo prompt “Authentication required: Authentication is needed to run /bin/bash as the super user”](https://small-tech.org/images/graphical-sudo-prompt.png) | ||
On Windows, you will also be prompted separately to allow the installation of the certificates. | ||
## Usage | ||
@@ -24,0 +34,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
83168
575
231
6
4