Slingshot fails in production mode

In trying to determine why my Slingshot uploads to Amazon S3 were failing in my deployed app, I built a completely new app from scratch that only does this one thing: upload files from the client to S3. And it works fine in development. But as soon as I turn the --production flag on, it fails to upload with a 403 error.

The code is exactly the same, only the flag on meteor run is different.

I’m not doing anything tricky. I’m using the example code to do everything; just modified it to use my bucket on S3. It can’t be CORS because it works fine in development mode.

If you want to look at the code, it’s here:
https://github.com/michael-mccrickard/newgeo

I assume you pass in the AWS credentials through the command line when in production mode? Your code here only sets those vars in development mode

The credentials are in settings.json when I run locally and they are environment vars on Heroku. I’ve put in console.log() statements to check and make sure they getting set right.

From reading the forums, I can tell plenty of people use this package for uploads in apps that they have in production right now. What does the --production flag do other than minimize your code for you?

It also changes your node env, and Meteor.isProduction/Development flags. The issue on Heroku is potentially different, but the fact that running locally with/without --production suggests that your environment variables are NOT getting set locally

Well, when I put a console.log() to check on the ID var, it shows up in the console, regardless of the --production flag. Is this not a legitimate test?

Hmm, it would seem to be, can you console.log process.env and obfuscate the result of AWS_SECRET_ACCESS_KEY and AWS_ACCESS_KEY_ID and post the results with/without --production?

without the production flag:

… and with the production flag

I should probably also have told you to black out your mongo URL :frowning: sorry about that, thought it would be hooked up to a local DB. I assume your AWS key’s are the same in both?

Also, it looks like I was wrong about --production changing the NODE_ENV too - its still development.

That’s OK, I saw that, too, but I was too lazy to go back and fix it. It’s a free sandbox, so I’ll be replacing it anyway if I actually deploy this thing for real.

I was curious about NODE_ENV, too. I assume that this works on the command line:

meteor --settings settings.json --production

This is the first time I’ve used two flags for this command. I assume this is the way to do it?

Yes, this is correct - I can’t really think what else the problem could be. Can you look in your .meteor/local file and locate the compiled code for this? I know I’ve had problems before with the minifier being a little aggressive (particularly around Meteor.isDevelopment vs Meteor.isProduction. Similarly, where do you pass the access credentials into Slingshot?

This is app.js from the server subdirectory. Slingshot gets it’s credentials in the Slingshot.createDirective() call, a few lines below the console.log(process.env).

var require = meteorInstall({"server":{"main.js":function(require,exports,module){

///////////////////////////////////////////////////////////////////////////////
//                                                                           //
// server/main.js                                                            //
//                                                                           //
///////////////////////////////////////////////////////////////////////////////
                                                                             //
let Meteor;
module.watch(require("meteor/meteor"), {
  Meteor(v) {
    Meteor = v;
  }

}, 0);

if (Meteor.isDevelopment) {
  process.env.AWS_ACCESS_KEY_ID = Meteor.settings.AWS_ACCESS_KEY_ID;
  process.env.AWS_SECRET_ACCESS_KEY = Meteor.settings.AWS_SECRET_ACCESS_KEY;
}

console.log(process.env);
Slingshot.fileRestrictions("ghUserFeaturedPic", {
  allowedFileTypes: ["image/png", "image/jpeg", "image/jpg", "image/gif"],
  maxSize: 10 * 1024 * 1024 // 10 MB (use null for unlimited).

});
Slingshot.createDirective("ghUserFeaturedPic", Slingshot.S3Storage, {
  bucket: "gh-resource",
  acl: "public-read",
  AWSAccessKeyId: process.env.AWS_ACCESS_KEY_ID,
  AWSSecretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
  region: "us-east-1",
  authorize: function () {
    return true;
  },
  key: function (file) {
    //Store file into the right directory 
    return "ghUserFeaturedPic/" + file.name;
  }
});
Meteor.startup(() => {// code to run on server at startup
});
///////////////////////////////////////////////////////////////////////////////

}}},{
  "extensions": [
    ".js",
    ".json"
  ]
});
var exports = require("/server/main.js");
//# sourceURL=meteor://💻app/app/app.js
//# sourceMappingURL=data:application/json;charset=utf8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm1ldGVvcjovL/CfkrthcHAvc2VydmVyL21haW4uanMiXSwibmFtZXMiOlsiTWV0ZW9yIiwibW9kdWxlIiwid2F0Y2giLCJyZXF1aXJlIiwidiIsImlzRGV2ZWxvcG1lbnQiLCJwcm9jZXNzIiwiZW52IiwiQVdTX0FDQ0VTU19LRVlfSUQiLCJzZXR0aW5ncyIsIkFXU19TRUNSRVRfQUNDRVNTX0tFWSIsImNvbnNvbGUiLCJsb2ciLCJTbGluZ3Nob3QiLCJmaWxlUmVzdHJpY3Rpb25zIiwiYWxsb3dlZEZpbGVUeXBlcyIsIm1heFNpemUiLCJjcmVhdGVEaXJlY3RpdmUiLCJTM1N0b3JhZ2UiLCJidWNrZXQiLCJhY2wiLCJBV1NBY2Nlc3NLZXlJZCIsIkFXU1NlY3JldEFjY2Vzc0tleSIsInJlZ2lvbiIsImF1dGhvcml6ZSIsImtleSIsImZpbGUiLCJuYW1lIiwic3RhcnR1cCJdLCJtYXBwaW5ncyI6Ijs7Ozs7Ozs7QUFBQSxJQUFJQSxNQUFKO0FBQVdDLE9BQU9DLEtBQVAsQ0FBYUMsUUFBUSxlQUFSLENBQWIsRUFBc0M7QUFBQ0gsU0FBT0ksQ0FBUCxFQUFTO0FBQUNKLGFBQU9JLENBQVA7QUFBUzs7QUFBcEIsQ0FBdEMsRUFBNEQsQ0FBNUQ7O0FBRVgsSUFBSUosT0FBT0ssYUFBWCxFQUEwQjtBQUV0QkMsVUFBUUMsR0FBUixDQUFZQyxpQkFBWixHQUFnQ1IsT0FBT1MsUUFBUCxDQUFnQkQsaUJBQWhEO0FBQ0FGLFVBQVFDLEdBQVIsQ0FBWUcscUJBQVosR0FBb0NWLE9BQU9TLFFBQVAsQ0FBZ0JDLHFCQUFwRDtBQUVIOztBQUVEQyxRQUFRQyxHQUFSLENBQVlOLFFBQVFDLEdBQXBCO0FBRUFNLFVBQVVDLGdCQUFWLENBQTJCLG1CQUEzQixFQUFnRDtBQUM5Q0Msb0JBQWtCLENBQUMsV0FBRCxFQUFjLFlBQWQsRUFBNEIsV0FBNUIsRUFBeUMsV0FBekMsQ0FENEI7QUFFOUNDLFdBQVMsS0FBSyxJQUFMLEdBQVksSUFGeUIsQ0FFcEI7O0FBRm9CLENBQWhEO0FBS0FILFVBQVVJLGVBQVYsQ0FBMEIsbUJBQTFCLEVBQStDSixVQUFVSyxTQUF6RCxFQUFvRTtBQUNsRUMsVUFBUSxhQUQwRDtBQUdsRUMsT0FBSyxhQUg2RDtBQUtsRUMsa0JBQWdCZixRQUFRQyxHQUFSLENBQVlDLGlCQUxzQztBQU1sRWMsc0JBQW9CaEIsUUFBUUMsR0FBUixDQUFZRyxxQkFOa0M7QUFPbEVhLFVBQVEsV0FQMEQ7QUFRbEVDLGFBQVcsWUFBWTtBQUVyQixXQUFPLElBQVA7QUFDRCxHQVhpRTtBQWFsRUMsT0FBSyxVQUFVQyxJQUFWLEVBQWdCO0FBRW5CO0FBRUEsV0FBTyx1QkFBdUJBLEtBQUtDLElBQW5DO0FBQ0Q7QUFsQmlFLENBQXBFO0FBcUJBM0IsT0FBTzRCLE9BQVAsQ0FBZSxNQUFNLENBQ25CO0FBR0QsQ0FKRCxFIiwiZmlsZSI6Ii9hcHAuanMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBNZXRlb3IgfSBmcm9tICdtZXRlb3IvbWV0ZW9yJztcblxuaWYgKE1ldGVvci5pc0RldmVsb3BtZW50KSB7XG4gIFxuICAgIHByb2Nlc3MuZW52LkFXU19BQ0NFU1NfS0VZX0lEID0gTWV0ZW9yLnNldHRpbmdzLkFXU19BQ0NFU1NfS0VZX0lEO1xuICAgIHByb2Nlc3MuZW52LkFXU19TRUNSRVRfQUNDRVNTX0tFWSA9IE1ldGVvci5zZXR0aW5ncy5BV1NfU0VDUkVUX0FDQ0VTU19LRVk7XG5cbn1cblxuY29uc29sZS5sb2cocHJvY2Vzcy5lbnYpXG5cblNsaW5nc2hvdC5maWxlUmVzdHJpY3Rpb25zKFwiZ2hVc2VyRmVhdHVyZWRQaWNcIiwge1xuICBhbGxvd2VkRmlsZVR5cGVzOiBbXCJpbWFnZS9wbmdcIiwgXCJpbWFnZS9qcGVnXCIsIFwiaW1hZ2UvanBnXCIsIFwiaW1hZ2UvZ2lmXCJdLFxuICBtYXhTaXplOiAxMCAqIDEwMjQgKiAxMDI0IC8vIDEwIE1CICh1c2UgbnVsbCBmb3IgdW5saW1pdGVkKS5cbn0pO1xuXG5TbGluZ3Nob3QuY3JlYXRlRGlyZWN0aXZlKFwiZ2hVc2VyRmVhdHVyZWRQaWNcIiwgU2xpbmdzaG90LlMzU3RvcmFnZSwge1xuICBidWNrZXQ6IFwiZ2gtcmVzb3VyY2VcIixcblxuICBhY2w6IFwicHVibGljLXJlYWRcIixcblxuICBBV1NBY2Nlc3NLZXlJZDogcHJvY2Vzcy5lbnYuQVdTX0FDQ0VTU19LRVlfSUQsXG4gIEFXU1NlY3JldEFjY2Vzc0tleTogcHJvY2Vzcy5lbnYuQVdTX1NFQ1JFVF9BQ0NFU1NfS0VZLFxuICByZWdpb246IFwidXMtZWFzdC0xXCIsXG4gIGF1dGhvcml6ZTogZnVuY3Rpb24gKCkge1xuXG4gICAgcmV0dXJuIHRydWU7XG4gIH0sXG5cbiAga2V5OiBmdW5jdGlvbiAoZmlsZSkge1xuXG4gICAgLy9TdG9yZSBmaWxlIGludG8gdGhlIHJpZ2h0IGRpcmVjdG9yeSBcblxuICAgIHJldHVybiBcImdoVXNlckZlYXR1cmVkUGljL1wiICsgZmlsZS5uYW1lO1xuICB9XG59KTtcblxuTWV0ZW9yLnN0YXJ0dXAoKCkgPT4ge1xuICAvLyBjb2RlIHRvIHJ1biBvbiBzZXJ2ZXIgYXQgc3RhcnR1cFxuXG5cbn0pO1xuIl19

Now I’ve discovered that Meteor.isDevelopment is evaluating to true, even when I use the --production flag! So I commented that block out, thinking it might be wiping out those process.env values, and I ran it with --production again, but everything is just the same. Credentials are there when I check them with console.log() and the upload still fails with 403.

Is there any way to check and see what credentials that HTTP call is actually passing? I know you get 403 errors when the credentials are bad.

Can you try hardcoding the AWS values? Rather than relying on env vars (just for testing) - I assume the machine you’re using is the same with/without --production?

Great idea! I should have thought of that. Unfortunately, I’m still getting a 403.

In running out of ideas :frowning: do you have logging turned on for your AWS bucket?

I really appreciate all your time and ideas. I’ve learned some new debugging techniques here. I do have logging turned on for that bucket. It’s so full of entries that I can’t find the ones I need. So I just reset it to dump to a new folder. I’ve run the app here and tried some uploads which failed. Then I checked AWS, but the log messages aren’t there yet. They are not instantaneous — in fact, Amazon says there’s no way to know when any particular log message will actually be delivered!

In any case, I’ll keep checking it. And of course, I’m all ears if you have any more ideas. Thanks again!

I just copied your repo and added .settings.json with my AWS credentials and ran

meteor --settings .settings.json --production

and was able to successfully upload images to the bucket. I used the default CORS config provided in the slingshot doc.

Alternatively, you could use Meteor-Files however I think the file must pass through the server before going to s3 with this package.

Thanks very much for doing that, jonathanredford. So its not my code, but something with my configuration. Or my machine. What else could it be? Are you on a Mac? Which browser do you use?

The log messages just say: Access denied. So helpful.