DEV Community

Rowan Merewood
Rowan Merewood

Posted on

Prudent publishing to npm

This post shares a few practices I find helpful for keeping my publishing of packages to npm a bit more isolated from my personal environment. I think there are good principles here, but they may be a bit specific to my project and platform. Please do comment with variants or tips for your own environment. Hey - maybe even do a whole post of your own!

πŸ’Œ Sign up to npm with a separate email

This is good practice for most developer channels - whether it's publishing an Android app in the Play Store, your account for a particular API you rely on, or your npm account. There are a number of reasons for doing this:

  • β›” Accounts get banned by mistake! You as a person on npm is different to the account that owns your packages. If one gets banned, it doesn't affect the other.
  • 🀝 Other people may need access. While an organization is the right solution long-term, sometimes there are emergencies or one-offs and sharing access to the one specific account is definitely better than your personal one!
  • πŸ‘” This email may need to be public. You may want to provide support or perhaps you just get random enquiries. Using a separate address lets you manage that as it's own thing.

No real instructions on this one... source that email address wherever you want!

🎭 Publish using a separate local user

This has more explicit security benefits than using a separate email (which frankly is more about your personal happiness).

Most of the issues here come from other packages getting compromised and injecting malicious code. Two specific incidents here were with some of the ESLint packages including code that stole npm tokens and the event-stream package getting compromised to steal cryptocurrency. Creating an isolated user helps mitigate this by reducing the exposure. So, for example if there is an exploited package that's able to kick in during the deployment process - it won't have access to your personal user. Likewise, an exploited package in your development environment won't have access to your deployment process.

If you're being really conscientious, then you should really ensure all your npm operations run as a separate user but that does start to turn into a lot of overhead.

I'm a Linux user, so these instructions are all based on my Bash shell environment. Please do share if there are tweaks for your own platform!

🐣 Right, let's get our user created:

$ sudo adduser deploy-npm
Enter fullscreen mode Exit fullscreen mode

You'll be prompted to create a password. Obviously use something solid, but we don't need to care what it is. We're going to use sudo when we access it.

πŸ•΅οΈ Now we need to configure the environment for the new deploy-npm user. We'll use sudo to log in to that account:

$ sudo -i -u deploy-npm
Enter fullscreen mode Exit fullscreen mode

The user will need to have access to a version of Node and npm. I use nvm to manage this, so the first thing I'm doing is following their install instructions and getting the LTS version of Node. You can go about this however you want. For example, maybe you already have Node installed system-wide and it's therefore immediately available for the new user.

β˜‘οΈ Verify your set-up just by checking the npm version:

$ npm -v
Enter fullscreen mode Exit fullscreen mode

πŸ‘‹ Now it's time to log in to the account! Unsurprisingly, that done with:

$ npm login
Enter fullscreen mode Exit fullscreen mode

πŸ” If you haven't already enabled two-factor authentication on your account this is an ideal time to do it! This means even if your account is compromised or the tokens stolen the attacker still can't do anything without that second authentication mechanism.

$ npm profile enable-2fa auth-and-writes
Enter fullscreen mode Exit fullscreen mode

πŸ’Ύ We also need a local copy of the repo. Our deploy user isn't going to commit anything and doesn't have SSH credentials set up anyway, so we're just going to use the HTTP version. This assumes your code is on GitHub, but any read-only checkout is what you're aiming for here.

$ git clone https://github.com/someUser/my-package.git
Enter fullscreen mode Exit fullscreen mode

πŸ” The assumption here is that you've already appropriately tagged the repo ready for a release. So, let's change into the directory and checkout the tag.

$ cd my-package
$ git checkout tags/0.0.1
Enter fullscreen mode Exit fullscreen mode

🎁 That's all the set-up done, so now we publish! Assuming you did enable 2FA, you're going to get asked for your confirmation code here.

$ npm-publish
Enter fullscreen mode Exit fullscreen mode

For my use, I've wrapped that up in a script that I can use to publish a given tag. This first one is running as the deploy-npm user and is effectively just the commands we used above. I've saved it as deploy-npm.sh in the user's home directory.

#! /bin/bash
cd &&
source .nvm/nvm.sh &&
cd my-package &&
git fetch &&
git checkout tags/$1 &&
npm publish
Enter fullscreen mode Exit fullscreen mode

Finally, I have a script in my own account that does the appropriate sudo call to run as the deploy-npm user.

#! /bin/bash
sudo -H -s -u deploy-npm /home/deploy-npm/deploy-npm.sh $1
Enter fullscreen mode Exit fullscreen mode

πŸ€– Now, after I've tagged up a release I just run:

$ deploy-npm.sh 0.0.2
Enter fullscreen mode Exit fullscreen mode

If you're still looking to tighten things up, try this post from Liran with a selection of other useful tips.

Top comments (0)