In this snippet we’ll learn about using a settings.json file in our Meteor application. We’ll also learn about a more secure way to use a settings file and how we can automate our workflow to be more efficient and prevent mistakes.
TABLE OF CONTENTS
1. What is settings.json?
2. Using settings.json
3. settings.json in development vs production
4. Automating settings.json
5. Takeaways
6. Questions & Comments
Slack
Join The Meteor Chef on Slack
If you get confused or stuck while reading—or just want to talk about Meteor—join us on Slack! Get an invite now.
What is settings.json?
When we’re building applications with Meteor, it’s not uncommon to need to store information like API keys and other configuration information in our application. In an effort to improve security, we can consolidate all of this information into one place, as opposed to leaving it scattered in our application code. Fortunately, Meteor gives us a way to do this by adding a file to the root of our application: settings.json.
Like the name implies, settings.json is nothing more than a plain JSON file. Inside, it looks like this:
SETTINGS.JSON
{
“magicPizzaService”: “Our1234API5678Key”
}
Neat! It’s literally nothing more than a JSON object. How…underhwelming. It’s certainly not sawing people in half, but it’s one of the most important tools we have in our Meteor application.
You may be wondering…“who has access to this?” Smart question. By default, anything we put inside of our object is accessible on the server only. This means that in the example above, we’d only be able to access the value of the magicPizzaService key in our server code. The client would have zero knowledge of its existence. Great!
Accessing values
So, we can store stuff pretty easily in settings.json, but how the heck do we use it? Let’s take a look:
SERVER
var secretPizzaAccessCode = Meteor.settings.magicPizzaService;
That’s it! Now, if we were to do a console.log( secretPizzaAccessCode ) on the server, we’d get “Our1234API5678Key” in return. Nifty. This is all well and good, but what if we want to have our cake and eat it to (read: access values on the client)? Let’s take a look.
Mixing public and private settings
Good news! We can use settings.json for public values to. To pull this off, we need to nest our values inside of our parent object within another object called public:
SETTINGS.JSON
{
“somePrivateKey”: “123456789”,
“public”: {
“somePublicKey”: “987654321”
}
}
See how this works? Again, anything that’s stored in our parent object will be accessible on the server only. Ever. To make sure values we don’t need to be terribly secure with are available on the client, we must nest values inside of the “public”: {} object. If you don’t, well, you just won’t have that slice of carrot cake we promised you if you behaved at Stephanie’s birthday party.
Michelle from Full House yelling
Okay…so this works pretty well. But something just doesn’t feel right about storing private values in the root like that. What about my pretend OCD? Ah, yes. Join me?
SETTINGS.JSON
{
“public”: {
“imARidiculousHumanBeing”: “butAtLeastImClean”
},
“private”: {
“hackTheGibson”: “keyboardCoWbOyS”
},
“imStillServerOnly”: “soDontPanic”
}
Green? Same rules apply. Keep in mind this is accessed like your average JavaScript object, so nesting rules still apply. Pull out that dot notation and shake it like somebody owes you money (I dare you to not misinterpret that):
CLIENT
var ridiculous = Meteor.settings.public.imARidiculousHumanBeing;
// returns “butAtLeastImClean” on the client.
And on the server…
SERVER
var cowboys = Meteor.settings.private.hackTheGibson;
// returns “keyboardCoWbOyS” on the server.
var dontPanic = Meteor.settings.imStillServerOnly;
// returns “soDontPanic” on the server.
Pretty cool. Now, when we’re working in our application, we don’t have to store our super secret API keys inline with the rest of our code. This makes it easy if say, our API keys are compromised and we need to easily reset them and have all of our application code still work.
Using settings.json
Just because we add a settings.json file to our application does not mean Meteor will know to use it. Cute, eh? In order to actually “load” our settings file, we need to tell Meteor about it one of two ways: when we start up our server and as an environment variable.
On startup
Generally reserved for development, we can load our settings.json file when we start our Meteor server:
TERMINAL
meteor –settings settings.json
Cool! Pretty easy, right? Now when our server boots up, Meteor will load in the values in our settings.json file and make them available to either the client or server depending on how our file is organized.
Depending on which hosting service we use in production, we may not have access to the script used to start our application, so this option is out. Fortunately, we have another way to do this: as an environment variable.
As an environment variable
This is one of those “it depends” type of techniques because how we can set Environment Variables comes down to which service provider we use to host our application (e.g. the technique above works with Meteor’s deploy command just fine using meteor deploy myapp.meteor.com –settings settings.json. Generally speaking, though, our PaaS (Platform-as-a-Service_ provider should give us some sort of GUI or command line tool for setting environment variables. The following example showcases how we do this with the Modulus hosting service, so your mileage may vary.
For example, using their command line tool we can set environment variables in Modulus like so:
TERMINAL
modulus env set METEOR_SETTINGS \”$(cat settings.json)\” -p ‘Project Name’
Here, we get a command modulus env set to which we can pass which environment variable we want to set. For Meteor applications, this value will always be METEOR_SETTINGS. Notice that here, we’re not passing a file name, but instead using the bash cat command to “read” the contents of our settings.json file and pass it as a String to our Modulus command. Notice that we pass the name of our project on Modulus, too.
If we’re not terribly comfortable with setting this via the command line, Modulus also lets us set this via the Administration tab of our application’s dashboard on their website:
Example of settings METEOR_SETTINGS in the Modulus dashboard
IT ALL DEPENDS
It bares repeating: this all depends on your hosting platform. Most providers will give you a way to set this either via the command line or a GUI.
Interesting. So realize that we’re not setting METEOR_SETTINGS equal to a file name, but rather, the contents of that file. Got it? So…all of this prompts a bigger question: “how secure is all of this?”
settings.json in development vs production
When we think about our settings.json file, we want to consider it in two contexts: development vs production. More specifically, we should create two separate settings files, one for development and one for production. Why is that?
In terms of security, we want to treat our production application and its settings like the most top secret information in the world. Anybody who has access to this information (in theory) can get access to any of the services whose API keys or other authentication information we store in our app.
In an effort to offset the risk of this information getting out into the wild, we can split our settings.json file into two files: settings-development.json and settings-production.json.
That first file, settings-development.json should only contain values used for testing, or, development. These keys are tied to dummy accounts or “fake apps” that we create on third-party services purely for testing purposes. Because these are (generally speaking, but not always) non-destructive, if they were to leak we don’t have to panic. This file should only contain the keys that are used in development.
The second file, though, settings-production.json, should be treated like pure gold. It is tied to the accounts and third-party services used in production. The one’s tied to our company or our own credit cards. The one’s that unlock a database. The one’s that let all hell break loose. This file should only contain the keys that are used in production.
Sound like gibberish? Let’s look at a practical example:
SETTINGS-DEVELOPMENT.JSON
{
“public”: {
“analytics”: “123456789”,
[…]
},
“private”: {
“github”: “555333222”,
[…]
}
}
Looks familiar. Now our settings-production.json file:
SETTINGS-PRODUCTION.JSON
{
“public”: {
“analytics”: “987654321”,
[…]
},
“private”: {
“github”: “000999888”,
[…]
}
}
Spot the difference? It’s subtle but important. Both files use the exact same key names but contain different values. Think about how we loaded our files earlier…depending on the environment we’re going into (development, production, or perhaps even staging), we want to load either our settings-development.json or settings-production.json file. Because our key names are the same, we shouldn’t have to edit our application code. We rely on our workflow, then, to make sure the right file is being used in the right places!
We’ll look at how to automate this in a little bit, but real quick: time for some real talk.
DO NOT commit your settings-production.json to version control
This is super, super, duper, totally, completely, really, insanely important. When we’re using version control with our application, we want to make sure that we never commit our settings-production.json file. This file is pure gold. Only the people who can absolutely be trusted with it should ever have access to it. As such, it should be passed on a person-to-person basis so you know who has it (or has) in the event of a security leak. Ok?
More specifically, the reason we want to omit our settings-production.json file from version control is a matter of trust, not just for our team, but the services we use to store our code as well. For example, if our settings-production.json file was stored in our GitHub repo, and for some reason GitHub got hacked: wuh oh. Even worse, what if you link your GitHub repository to another service, like a continuous integration tool and they get hacked? Blerg city.
.GITIGNORE
settings-production.json
Here, we show an example of adding our settings-production.json file to our .gitignore file (the file GIT uses to know which files to hide from commits). Consider this your sword. Your rifle. Bake it into your boilerplate and never look back.
This applies to both public and private repositories. It doesn’t matter. Never commit your settings-production.json file. Consider it sacrilege. Ok wise guy, so what if I have a team of 25 developers, each needing deployment access? How do we share these keys?
Pulling out floppy disks like guns
That’s only partially a joke. Ideally, you’re only handing production keys off in person, or through some other means that is absolutely secure. A flash drive. A floppy disk. If you’re really hardcore (or work in a distributed team), you could make use of something like PGP encryption. Just make sure to consider how this works and have a plan before going into production so you don’t make any compromises later.
Automating settings.json
So this whole multiple files and remembering to copy and paste sounds like a pain in the butt. That’s not necessarily a bad thing. This is a very serious matter and while multiple steps can be frustrating, it can help to make us stop and think what we’re doing and prevent serious mistakes. That said, we can automate this a little bit. Again, we’ll use the Modulus hosting platform as an example here.
PACKAGE.JSON
{
“name”: “my-app”,
“version”: “1.0.0”,
“description”: “An app for doing stuff efficiently.”,
“scripts”: {
“start”: “meteor –settings settings-development.json”,
“staging”: “modulus env set METEOR_SETTINGS \”(cat settings-staging.json)\” -p ‘App - Staging’ && modulus deploy -f -p ‘App - Staging’”,
“production”: “modulus env set METEOR_SETTINGS \”(cat settings-production.json)\” -p ‘App - Production’ && modulus deploy -f -p ‘App - Production’”
}
}
What in the hell is this thing? Because our Meteor app is a plain old Node app at its core, we can make use of some nifty Node conventions like the above, a package.json file. More specifically, we can piggyback on the NPM (node package manager—a utility we get when we install Node.js on our system) “scripts” feature, which allows us to define some generic commands to call using the syntax npm run .
This helps us automate our settings workflow by defining some short-hand commands that combine all of the bigger commands we need to write out to toggle between different settings files depending on environment. Confused? Think of this like link shortening for bash commands. Instead of one long string that we have to memorize, we get a neat, tidy command. Sweet!
Above, we can see an example using three different scripts, each calling a different set of commands. Here, we can see different commands representing different environments. So, if we were going into production, we’d call npm run production. Just noodling around on our laptop, we’d do: npm start. Wait, wait, wait…why does that last one not have a run in it?
By default, the NPM scripts feature ships with a handful of commands that we can use. When using these, we can just pass npm . When we use our own (like the staging and production examples above), we just prefix with run: npm run . Right on!
Takeaways
Make use of multiple settings.json files like settings-development.json and settings-production.json to aid in your security process.
Never, ever, ever, really, seriously, dude, I’m not kidding, ever check your settings-production.json file into version control. If you do, zap that ish quick and reset your keys.
Consider automating your environment workflow to protect yourself from forgetfulness.
Watsi