How to configure the content of module.paths in a nodejs environment - node.js

On my debian 10 system I do the follwing as user "joerg":
joerg#h2257088:~/temporary/play$ export NODE_PATH=myOwnNodePath
joerg#h2257088:~/temporary/play$ node
Welcome to Node.js v12.20.0.
Type ".help" for more information.
> module.paths
[
'/home/joerg/temporary/play/repl/node_modules',
'/home/joerg/temporary/play/node_modules',
'/home/joerg/temporary/node_modules',
'/home/joerg/node_modules',
'/home/node_modules',
'/node_modules',
'/usr/lib/node'
]
>
Doing the same as user "root" gives:
root#h2257088:/home/joerg/temporary/play# export NODE_PATH=myOwnNodePath
root#h2257088:/home/joerg/temporary/play# node
Welcome to Node.js v12.20.0.
Type ".help" for more information.
> module.paths
[
'/home/joerg/temporary/play/repl/node_modules',
'/home/joerg/temporary/play/node_modules',
'/home/joerg/temporary/node_modules',
'/home/joerg/node_modules',
'/home/node_modules',
'/node_modules',
'myOwnNodePath',
'/root/.node_modules',
'/root/.node_libraries',
'/usr/lib/node'
]
>
Here I have three additional entries (which, by the way I want to have (with the obvious change of "/root" to "/home/joerg")):
'myOwnNodePath',
'/root/.node_modules',
'/root/.node_libraries',
What is defining the content of module.paths in a nodejs environment? What can I do, to get the missing entries?
This question is related to (the not answered) question: NODE_PATH has no effect on module.paths or finding modules.
EDIT:
After an
apt-get purge -y nodejs
apt-get install -y nodejs
it works. That is: Identical for both users root and joerg, with the behaviour formerly appearing for root only and hence, as I want to have it. This solves my principal problem, but does not answer the question.

This is actually not a configuration issue. Instead there is some dynamics in putting entries into the module.paths array. On Unix (not on Windows), if the node binary should be treated securely, certain entries stemming from unsecure environment variables are not included in the module.paths array.
More precisely, if the node binary has set-user-ID or set-group-ID or has capabilities, the entries stemming from the environment variables HOME and NODE_PATH will not be included in the module.paths array, which are exactly the three entries mentioned as missing by the question.
To solve my problem I will copy the node binary so that I have two: node for "normal" execution (running javascript scripts) and nodeServer, which will get the capabilities (to use low port numbers), for execution as an http-server.
Even more precisely, if the AT_SECURE entry of the auxiliary vector has a non-zero value (see man getauxval), the three entries
$NODE_MODULES
$HOME/.node_modules
$HOME/.node_libraries
are not included. To quote man getauxval:
Most commonly, a nonzero value of AT_SECURE indicates that the process is executing a set-user-ID or set-group-ID binary (so that its real and effective UIDs or GIDs differ from one another), or that it gained capabilities by executing a binary file that has capabilities. Alternatively, a nonzero value may be triggered by a Linux Security Module.
After reinstalling node the capabilities are not longer set, which explains the observation in the EDIT of the question. Re-doing the
setcap CAP_NET_BIND_SERVICE=+eip /usr/bin/node
will re-introduce the unwanted behaviour.
Evidence comes out of the source for node (version 12 because this is what I use):
In lib/internal/modules/cjs/loader.js:
Module._initPaths = function() {
const homeDir = isWindows ? process.env.USERPROFILE : safeGetenv('HOME');
const nodePath = isWindows ? process.env.NODE_PATH : safeGetenv('NODE_PATH');
...
In src/node_credentials.cc:
bool SafeGetenv(const char* key, std::string* text, Environment* env) {
#if !defined(__CloudABI__) && !defined(_WIN32)
if (per_process::linux_at_secure || getuid() != geteuid() ||
getgid() != getegid())
goto fail;
#endif
...
And in src/node_main.cc:
#if defined(__linux__)
char** envp = environ;
while (*envp++ != nullptr) {}
Elf_auxv_t* auxv = reinterpret_cast<Elf_auxv_t*>(envp);
for (; auxv->a_type != AT_NULL; auxv++) {
if (auxv->a_type == AT_SECURE) {
node::per_process::linux_at_secure = auxv->a_un.a_val;
break;
}
}
#endif

Related

Powershell operating system architecture vs NodeJs process.arch

I am using a NodeJs app which internally calls process.arch to get system architecture and then builds a path based on the result, eg. - path/to/file/x64/file (application is selenium-standalone NPM package).
Now I need to build the same path in PowerShell script so I also need to get system arcitecture. But doing that with f.ex. (Get-WmiObject Win32_Processor).AddressWidth returns result without x - just 64 so my path ends up path/to/file/64/file which is wrong. My current fix is to explicitly add x:
$arch = (Get-WmiObject Win32_Processor).AddressWidth
if ($arch -eq 32) { $arch = 'x32' }
if ($arch -eq 64) { $arch = 'x64' }
Do you consider this as as safe thing to do? Or is there a way from powerShell to get it already with x assigned?
process.arch describes the bitness (or word width, or architecture or whatever you wanna call it) of the current process - because it's entirely possible to run a 32-bit (or even 16-bit) process on a 64-bit operating system.
To figure out whether a running PowerShell process is 32- or 64-bit (regardless of OS architecture), use [Environment]::Is64BitProcess:
$arch = if([Environment]::Is64BitProcess) { 'x64' } else { 'x32' }

How can I install binary file into NixOS

I'm trying to install external binary inside NixOS, using declarative ways. Inside nix-pkg manual, I found such way of getting external binary inside NixOS
{ pkgs ? import <nixpkgs> {} }:
pkgs.stdenv.mkDerivation {
name = "goss";
src = pkgs.fetchurl {
url = "https://github.com/aelsabbahy/goss/releases/download/v0.3.13/goss-linux-amd64";
sha256 = "1q0kfdbifffszikcl0warzmqvsbx4bg19l9a3vv6yww2jvzj4dgb";
};
phases = ["installPhase"];
installPhase = ''
'';
But I'm wondering, what should I add inside InstallPhase, to make this binary being installed inside the system?
This seems to be an open source Go application, so it's preferable to use Nixpkgs' Go support instead, which may be more straightforward than patching a binary.
That said, installPhase is responsible creating the $out path; typically mkdir -p $out/bin followed by cp, make install or similar commands.
So that's not actually installing it into the system; after all Nix derivations are not supposed to have side effects. "Installing" it into the system is the responsibility of NixOS's derivations, as configured by you.
You could say that 'installation' is the combination of modifying the NixOS configuration + switching to the new NixOS. I tend to think about the modification to the configuration only; the build and switch feel like implementation details, even though nixos-rebuild is usually a manual operation.
Example:
installPhase = ''
install -D $src $out/bin/goss
chmod a+x $out/bin/goss
'';
Normally chmod would be done to a local file by the build phase, but we don't really need that phase here.
I have no idea why this was so hard to figure out. Having robust configuration systems is fine, but at the end of the day sometimes you just need to be able to download and expose a single flipping file on the $PATH.
The result of fetchurl is "the unaltered contents of the URL within the Nix store", which is being used for the src. So in installPhase, $src points to the downloaded data, and you just have to copy/install/link that into $out/…..
pkgs.stdenv.mkDerivation {
name = "hello_static";
src = pkgs.fetchurl {
name = "hello_static";
url = "https://raw.githubusercontent.com/ruanyf/simple-bash-scripts/6e837f949010e0f5e9305e629da946de12cc63e8/scripts/hello-world.sh";
sha256 = "sha256:somE27ajbm0TtWv9tyeqTWDW3gbIs6xvlcFS9QS1ZJ0=";
};
phases = [ "installPhase" ];
installPhase = ''
install -D $src $out/bin/hello_static
'';
};

docker, openmpi and unexpected end of /proc/mounts line

I have build environment to run code in a Docker container. One of the components is OpenMPI, which I think is the source of problem or manifest it.
When I run code using MPI I getting message,
Unexpected end of /proc/mounts line `overlay / overlay rw,relatime,lowerdir=/var/lib/docker/overlay2/l/NHW6L2TB73FPMK4A52XDP6SO2V:/var/lib/docker/overlay2/l/MKAGUDHZZTJF4KNSUM73QGVRUD:/var/lib/docker/overlay2/l/4PFRG6M47TX5TYVHKQQO2KCG7Q:/var/lib/docker/overlay2/l/4UR3OEP3IW5ZTADZ6OKT77ZBEU:/var/lib/docker/overlay2/l/LGBMK7HFUCHRTM2MMITMD6ILMG:/var/lib/docker/overlay2/l/ODJ2DJIGYGWRXEJZ6ECSLG7VDJ:/var/lib/docker/overlay2/l/JYQIR5JVEUVQPHEF452BRDVC23:/var/lib/docker/overlay2/l/AUDTRIBKXDZX62ANXO75LD3DW5:/var/lib/docker/overlay2/l/RFFN2MQPDHS2Z'
Unexpected end of /proc/mounts line `KNEJCAQH6YG5S:/var/lib/docker/overlay2/l/7LZSAIYKPQ56QB6GEIB2KZTDQA:/var/lib/docker/overlay2/l/CP2WSFS5347GXQZMXFTPWU4F3J:/var/lib/docker/overlay2/l/SJHIWRVQO5IENQFYDG6R5VF7EB:/var/lib/docker/overlay2/l/ICNNZZ4KB64VEFSKEQZUF7XI63:/var/lib/docker/overlay2/l/SOHRMEBEIIP4MRKRRUWMFTXMU2:/var/lib/docker/overlay2/l/DL4GM7DYQUV4RQE4Z6H5XWU2AB:/var/lib/docker/overlay2/l/JNEAR5ISUKIBKQKKZ6GEH6T6NP:/var/lib/docker/overlay2/l/LIAK7F7Q4SSOJBKBFY4R66J2C3:/var/lib/docker/overlay2/l/MYL6XNGBKKZO5CR3PG3HIB475X:/var/lib/do'
That message is printed for code line
MPI_Init(&argc,&argv);
To make the problem more complex to understand, a warning message is printed only when the host machine is mac os x, for linux host all is ok.
Except for warning message all works fine. I do not know how OpenMPI and docker well enough how this can be fixed.
This is likely due to your /proc/mount file having a line in it greater than 512 characters, causing the hwloc module of OpenMPI to fail to parse it correctly. Docker has a tendency to put very long lines into /proc/mounts. You can see the bug in openmpi-1.10.7/opal/mca/hwloc/hwloc191/hwloc/src/topology-linux.c:1677:
static void
hwloc_find_linux_cpuset_mntpnt(char **cgroup_mntpnt, char **cpuset_mntpnt, int fsroot_fd)
{
#define PROC_MOUNT_LINE_LEN 512
char line[PROC_MOUNT_LINE_LEN];
FILE *fd;
*cgroup_mntpnt = NULL;
*cpuset_mntpnt = NULL;
/* ideally we should use setmntent, getmntent, hasmntopt and endmntent,
* but they do not support fsroot_fd.
*/
fd = hwloc_fopen("/proc/mounts", "r", fsroot_fd);
if (!fd)
return;
This can be fixed by increasing the value of PROC_MOUNT_LINE_LEN, although that should be considered a temporary workaround.
This issue should be fixed in hwloc since 1.11.3 (released 2 years ago). You can either upgrade to OpenMPI 3.0 which contains a hwloc 1.11.7 >= 1.11.3. Or recompile OpenMPI to use an external hwloc instead of the old embedded one.

Custom fact should run after a package is installed

I have a small custom fact in a my php module
Facter.add('php_extension_version') do
setcode do
Facter::Core::Execution.exec("php -i | awk '/^PHP Extension =>/ { print $4}'") || nil
end
end
This obviously requires the php binary to be installed. However, I noticed that all facts are run once before applying the catalog, so this fact is invalid before php installed.
Is there any way of gathering the information after the module is installed? Is there perhaps another way of exposing this information except facter?
Update
I'm using the two facts to determine which of multiple .so files is the right one to install:
if $php_zts_enabled {
$so_name = "newrelic-$php_extension_version.so"
} else {
$so_name = "newrelic-$php_extension_version-zts.so"
}
file {"/usr/lib64/php5/extensions/newrelic.so":
source => "file:///opt/newrelic-php5-$version-linux/agent/x64/$so_name",
owner => root,
group => root,
mode => 0644,
notify => Service['apache'],
require => Exec["extract-php-agent-$version"]
}
The files that are located in the agent/x64 directory can be
newrelic-20060613.so newrelic-20090626-zts.so newrelic-20121212.so newrelic-20131226-zts.so
newrelic-20060613-zts.so newrelic-20100525.so newrelic-20121212-zts.so
newrelic-20090626.so newrelic-20100525-zts.so newrelic-20131226.so
You essentially have only two opportunities to execute code on the node:
As part of a Facter fact. As you are aware, this happens before puppet applies a catalog, so any facts dependent on the results of the puppet run will not be useful until the next run.
As part of a custom provider. You can create a custom type and provider for installing the extensions that checks the node state before deciding what to do. Providers execute on the node, and as long as you know the overall provider lifecycle you can make this happen after the PHP install. However, this is incredibly complex compared to normal puppet modules.
Outside of those options, the normal way of doing this would be to enforce the version and configuration of php within your own manifests, and then pass that information to here. You should already know the version of PHP and its extensions based on what packages you have installed.
I would modify the fact so that it's present only when the binary is present (hence it won't be present at the very first run).
Facter.add('php_extension_version') do
setcode do
if system("which php > /dev/null 2>&1")
Facter::Core::Execution.exec("php -i | awk '/^PHP Extension =>/ { print $4}'") || nil
end
end
end
and then in your manifest you'd wrap the original code in the if
if $php_extension_version {
if $php_zts_enabled {
$so_name = "newrelic-$php_extension_version.so"
} else {
$so_name = "newrelic-$php_extension_version-zts.so"
}
file {"/usr/lib64/php5/extensions/newrelic.so":
source => "file:///opt/newrelic-php5-$version-linux/agent/x64/$so_name",
owner => root,
group => root,
mode => 0644,
notify => Service['apache'],
require => Exec["extract-php-agent-$version"]
}
}

How do I include this directory in the $PATH env var?

I'm building a package for Github's Atom editor and Im running into a challenge trying to get a child process to execute with node js. I'm pretty sure that the problem is that the environment that Atom runs in, doesn't include the path to the mrt script. So when I run this from within my package:
exec = require("child_process").exec
child = undefined
child = exec("/usr/local/bin/mrt add iron-router", { cwd: path },(error, stdout, stderr) -
console.log "stdout: " + stdout
console.log "stderr: " + stderr
console.log "exec error: " + error if error isnt null
return
)
in the console, I get:
Atom has a web inspector built right into it and you can actually see the Paths that atom has included. So when I go to Atom's console and type: process.env.PATH it shows the paths: /usr/bin:/bin:/usr/sbin:/sbin. So I somehow need to make atom aware of that mrt script's path. Anyone know how I might go about doing that?
I also reached out on on Atom's discussion forum yesterday, but have yet to come up with a solution.
Edit:
I should also note that the normal command for excuting the mrt package installer is mrt add package-name but as advised on Atom's discussion forum, I've been using the full path.
Edit 2:
I've creating symlinks to node in my /usr/bin directory, and it's working now. Now I'm trying to get node to create the symlinks for me using fs.symlink but that doesn't seem to be working.
To sum it up, the problem is that Atom uses PATH from where it is launched. Consequently, the path to node and the path to mrt where not included in Atom's path. The solution came to me when someone on the Atom Discussion forum pointed out Atom's Class BufferedNodeProcess.
At the time of Answer there is a slight bug with that class so I was not able to use it - the Github team works fast, I wouldn't be surprised if it was fixed within the next couple days. I was, however, able use some of the code to get Atom's environments. Also, I ended up using node's spawn method instead of execute since that's what BufferedNodeProcess uses. Plus you can read each individual line of the stdout.
options =
cwd: atom.project.getPath()
options.env = Object.create(process.env) unless options.env?
options.env["ATOM_SHELL_INTERNAL_RUN_AS_NODE"] = 1
node = (if process.platform is "darwin" then path.resolve(process.resourcesPath, "..", "Frameworks", "Atom Helper.app", "Contents", "MacOS", "Atom Helper") else process.execPath)
mrt = spawn(node, [
"/usr/local/lib/node_modules/meteorite/bin/mrt.js"
"add"
"iron-router"
], options )
mrt.stdout.on "data", (data) ->
console.log "stdout: " + data
return
mrt.stderr.on "data", (data) ->
console.log "stderr: " + data
return
mrt.on "close", (code) ->
console.log "child process exited with code " + code
return

Resources