But this doesn’t seem to make the slightest difference. Extensive trawling has failed to turn up anything relevant, which makes me think it must be something simple - but what?
Just to reassure myself I have the right end of the stick I tried changing ownership of master.key to www-data (don’t worry, my server is not publicly accessible), and yep, now the app server works. So it’s as I thought: Thin needs to be handed the master key somehow. I’m surprised this isn’t documented anywhere, must be a common problem. How do I set the RAILS_MASTER_KEY environment variable for Thin?
I think I’ve solved it. Since the init.d script runs as root it is able to read the master key before spawning Thin, and now passing it as an environment variable works - though I’m not sure why this wasn’t working from the command line. In any case, my init.d/thin now looks like this:
#!/bin/bash
DAEMON=/usr/local/bin/thin
BUNDLE=/usr/local/bin/bundle
CONFIG_PATH=/etc/thin
SCRIPT_NAME=/etc/init.d/thin
# Exit if the package is not installed
[ -x "$DAEMON" ] || exit 0
invoke()
{
CONFIGS=$CONFIG_PATH/*
[ -e "$CONFIG_PATH/$2.yml" ] && CONFIGS=$CONFIG_PATH/$2.yml
for conf in $CONFIGS
do
echo "[$1] $conf"
user=`grep "^user: " $conf|sed -e 's/^user: //g'`
group=`grep "^group: " $conf|sed -e 's/^group: //g'`
# Switch to the app's directory and set permissions
cd `grep "^chdir: " $conf|sed -e 's/^chdir: //g'`
[ -d ./log ] && chown $user:$group ./log
[ -d ./tmp ] && chown -R $user:$group ./tmp
# Read the master key
MASTER_KEY=`cat config/master.key`
# Run with bundler
if [ -e Gemfile ] && grep thin Gemfile > /dev/null; then
RAILS_MASTER_KEY=$MASTER_KEY $BUNDLE exec $DAEMON $1 -d --config=$conf
# Run with system Thin
else
RAILS_MASTER_KEY=$MASTER_KEY $DAEMON $1 -d --config=$conf
fi
done
}
case "$1" in
start)
invoke start $2
;;
stop)
invoke stop $2
;;
restart)
invoke restart $2
;;
*)
echo "Usage: $SCRIPT_NAME {start|stop|restart} [config_name]" >&2
exit 3
;;
esac
And it still works after I’ve taken back ownership of master.key and set its permission to 0600. I’ve also confirmed that I can run bundle exec rails c as myself without any permission errors. I think that’s all I need to do?
This is not a real answer to your question, merely a suggestion:
In production, don’t store critical credentials in files on the server. Rather, put them in environment variables, then launch the services that need the credentials, then delete the values from the environment variables again (so they are not printed out, should the environment variables somehow be dumped one day, due to some potential unknown/unresolved bug somewhere).
Thanks - that’s basically what I’m doing, though the “critical credentials” in this case is the decryption key for credentials.yml.enc. Perhaps I’m misunderstanding something, but I don’t see how it would be possible to put anything into an environment variable without that value being present in a file of some kind?
And then, you’d start your app manually, by feeding the environment variables ENV["DATABASE_USR"] etc. to the process (and then also emptying these variables once everything’s running correctly).
So yes, it does require a manual intervention to start or restart your server, but security always has a “cost” (trade-off: security ↔ convenience).
Ah yes, that’s what I thought. I guess I could live with that, though I’d still want to use the credentials.yml.enc file and pass the key to that instead. My app uses other credentials besides the postgres login, and it would be tedious to have to supply them all manually. I assume the encryption method used is secure enough, though a quick search failed to identify the precise method used?
No need to. What I do is this: For security reasons, I have a “dedicated” lightweigh/minimal $500 notebook which I use exclusively to connect to my server. As soon as the server is up OK, I immediately take it offline and do not use if for anything else, which minimizes exposure to potential online attacks.
So you don’t have to type in all your credentials - you just save the launch command containing all your ENV variables/values in a text file, and then copy/paste it into your ssh shell, each time you need to launch your server. Since this machine is online only a couple of minutes and you use it only for this, you can consider this as-safe-as-it-gets.
(And I don’t think Thin would empty automatically all ENV variables you set, I assume you need to empty them manually. But you could also write a shell script that would contain your server launch command with all the ENV variables, and which would then also have the command to empty those ENV variables at the end - once it has checked that all required services are running OK.)
1 last thing to always remember: Whenever you use/set credentials in shell commands, always be 100% sure that no command history whatsoever is being saved in your shell (this normally requires configuration).