RoR: Shell/System commands with write property not working in production mode? - ruby-on-rails

System("ls")
System("pwd")
Both these commands just work fine in both production & development mode on the same server.
However System("mkdir test") or any other command that involves creating a new file/dir does not go through in production mode, but works just fine in development mode. Any ideas here?
My guess is it has something to do with permissions but not sure where.

On your server you should have a user different from root for security reasons. Than this user should be added to sudoers list:
https://askubuntu.com/questions/7477/how-can-i-add-a-new-user-as-sudoer-using-the-command-line
now, depending where you want to create this folder, if it's in your app folder where your user has permissions to read/write, (search chmod 755 and chown to set the owner of the folder, better use chown -R to apply this to all subfolders), after this you'll be able to create that folder with:
System("mkdir test")
but only in folders where your user has access to read/write.
If you want to create the test folder in some other path where you need to use sudo you'll have to run:
System("sudo mkdir test")
normally this is running in a background and you won't be there to write the password, so you'll have to add your command to not require password while running sudo, with NOPASSWD directive you can do that:
https://askubuntu.com/questions/159007/how-do-i-run-specific-sudo-commands-without-a-password
sudo visudo -f /etc/sudoers #!important visudo, read in the upper link more about it before trying this.
after doing all this you'll be able to create a folder in your path using:
System("sudo mkdir test")
without requiring a password.

Related

How to set right permission to Linux file?

I wanted to know how can I set right permission for my file /log/production.log? Everyone is saying just use chmod or chown but no one explains what I should wright after these commands. I am beginner and would appreciate if you could explain.
In my particular example I have rails app on production server where I need to set permission to production.log file in /var/www/my_app/log/ directory.
Here is what documentation is asking from me:
By default, Phusion Passenger runs Rails applications as the owner of
config.ru. So the log file can only be written to if that user has
write permission to the log file. Please chmod or chown your log file
accordingly.
Hope you can help. Thanks.
Try chmod 0660 production.log and take a look at this explanation/diagram of chmod.
chmod allows change the permissions of a file or a directory. Exists three basic permissions (read,write,execute) for three differents groups (owner,group,other).
chown allows change who is the owner of a file or a directory.
I recommend you use chmod 640. Looking the syntax of chmod here you're defining the production.log's owner (usually root) can read and write this file. If you want, you can give read-access for all users of the same group of the owner. But you shouldn't offer permissions for other people, even less in a production environment.
I would create a deploy user for your application, say myapp (doesn't particularly matter what the name is). The use this user to deploy/manage your application. Assuming username myapp
chown -R myapp:myapp /var/www/my_app
and then restart nginx/passenger. This will cause passenger to run as the myapp user, and allow it to write logs under the logs directory. (Also make sure that you don't have /var/www as the docroot, accessible outside of passenger as it can cause information leakage)
another option, if the server isn't shared, is that you can run as the www user. so
chown -R www:www /var/www/my_app
which should allow the process to write to your logs.

How to add accounts to Jenkins without the web interface?

I want to automate the entire installation of Jenkins, given a list of user names I want to be able to create user accounts for each. The only method I've read to set up user accounts is here:
https://wiki.jenkins-ci.org/display/JENKINS/Standard+Security+Setup
Tried seeing if there was an option to configure with command line at:
https://localhost:8080/cli/
But does not seem to the be case.
Is it possible to add user accounts without using the web interface? More specifically a method that is scriptable.
My last resort is to do raw post requests but hoping there is a nicer way.
Yes of course, it is possible to script provisioning for jenkins. But not with the cli tool alone.
I guess you want to use "Jenkins own user database" with Project Matrix Authorization Strategy.
Steps to prepare provisioning:
Configure your Jenkins manually (enable security, add rolls and at least one user)
Shutdown your jenkins (to let him write all in-memory changes to disk)
Copy $JENKINS_HOME/config.xml to your provisioning script (as as seed data)
Copy $JENKINS_HOME/users/ (as seed data)
Get the cli tool: cd /tmp; wget -nv http://localhost:8080/jnlpJars/jenkins-cli.jar
If you do not want to have static seed data (one config.xml for each user) you can generate a (users/username/)config.xml using a bash script or a more advanced tool. But for simplicity sake you can take users/username1/config.xml as a template. Replace relevant data with a placeholder e.g. "PLACEHOLDER_FULLNAME" for full user name.
e.g.:
change
"<fullName>sample full username</fullName>"
to
"<fullName>PLACEHOLDER_FULLNAME</fullName>"
In your provisioning script, iterate over all users. For each user, replace each placeholder with the correct value.
e.g.
cp $SEED_DATA/templates/user/config.xml /tmp/config.xml
sed -e "s/\${PLACEHOLDER_USERNAME}/1/" -e "s/\${ChuckNorris}/dog/" /tmp/config.xml
sed -e "s/\${PLACEHOLDER_EMAIL}/1/" -e "s/\${he#findsyou.com}/dog/" /tmp/config.xml
...
mkdir -p $SEED_DATA/users/$USERNAME/
cp /tmp/config.xml $SEED_DATA/users/$USERNAME/config.xml
When you want to use generated users config.xml please generate for each user some permission settings in $JENKINS_HOME/config.xml:
<authorizationStrategy class="hudson.security.ProjectMatrixAuthorizationStrategy">
...
<permission>hudson.model.View.Create:username1</permission>
<permission>hudson.model.View.Delete:username1</permission>
<permission>hudson.model.View.Read:username1</permission>
...
<permission>hudson.model.View.Create:username2</permission>
<permission>hudson.model.View.Delete:username2</permission>
<permission>hudson.model.View.Read:username2</permission>
...
</authorizationStrategy>
Provisioning steps:
Install jenkins as you did before & maybe dynamic config generator (see above)
cp $SEED_DATA/config.xml $JENKINS_HOME/
cp -R $SEED_DATA/users/ $JENKINS_HOME/
chown -R "jenkins:jenkins" $JENKINS_HOME/users/ (maybe optional)
cd /tmp; java -jar jenkins-cli.jar -s http://localhost:8080/ reload-configuration

Handling shared/tmp in a Ruby on Rails project when the deploy user is different than the run user

I have two users on my server, an Ubuntu 12.04 virtual server that I manage myself:
projectx is used to deploy the application and is the user/group for most files in /var/www/projectx
projectx_rails and it's used to run the Rails application. That way, the running rails application doesn't have access to modify the source code.
Some directories, like public/uploads, are configured to belong to projectx_rails:projectx_rails, so that the rails app can write the uploaded files.
My problem comes to the directory tmp. This directory is located in /var/www/projectx/shared and linked to each release in the usual capistrano way of handling releases. The problem is that some files created during deployment are then not writable by the running rails app and files created by the rails app are not writable by the deployment process.
Is there a way to handle this? Having all the files there belong to projectx_rails:projectx_rails and be group writable would be good enough, but I'm not sure how to trigger this.
I'm using: Capistrano 3, Rails 3.2, Ruby 2.1.2, Unicorn 4.8.3, nginx.
Well, this is my theory. It is obviously hard to test on my end, so consider it conjecture.
First: make a group that both users belong to. Like projectx_shared.
Second: make this group the group owner of the tmp directory:
chown projectx_rails:projectx_shared tmp
Third: set the setgid bit on this directory:
chmod g+s tmp
Now, the group owner of files added to tmp should be set to projectx_shared automatically. I think this will apply to capistrano tasks as well.
I'm assuming when you deploy, files already get rw-rw-r-- permissions automatically. If not, you'll need to set your UMASK to 002 in your, e.g. .bashrc as well.
Let me know if it works...
May be use ACL for shared files? The only thing that, enable ACL support in fstab.
setfacl -m d:u:projectx:rwx,u:projectx:rwx,\
d:u:projectx_rails:rwx,u:projectx_rails:rwx /var/www/projectx/shared/tmp
You can run commands on the remote machine through capistrano. You could run a directory owner change after, lets say, symlinking the application.
In your deploy.rb file, add a callback for it:
after 'deploy:create_symlink' do
run "chown -R projectx_rails:projectx_rails #{current_release}/tmp"
end
My current solution is to have this task:
namespace :deploy do
desc "Fix permissions"
task :fix_permissions do
on roles(:app) do
execute "sudo chown -R projectx_rails:projectx_rails #{shared_path}/tmp"
execute "sudo chmod ug+rwX,o+rw #{shared_path}/tmp"
end
end
end
and run it both at the beginning and the end of my deployment:
after "deploy:started", "deploy:fix_permissions"
before "deploy:restart", "deploy:fix_permissions"
and to make it work I had to add this to my sudoers:
projectx ALL=NOPASSWD: /bin/chown -R projectx_rails\:projectx_rails /var/www/projectx/shared/tmp
projectx ALL=NOPASSWD: /bin/chmod ug+rwX\,o+rw /var/www/projectx/shared/tmp
which makes me rather uncomfortable.
1) ensure both projectx and projectx_rails are members of the group projectx
2) add this to deploy:
task :change_tmp_pems do
run "chmod -Rf 775 #{shared_path}/tmp"
end
after "deploy:started", :change_tmp_pems
the -f will silently fail / skip any files it doesn't have access to, so that wont be an issue.
4 lines of code, pretty succinct.
Dont messa about with chown as it requires sudo normally and is unnecessary.

Non-privileged, non-root, user to start or restart webserver server such as nginx without root or sudo

I'm using capistrano to deploy a rails web app. I want to give the deploy user on the webserver as few privileges as I can. I was able to do everything I need to do as a non-privileged user except restart the webserver.
I'm doing this on an ubuntu server, but this problem is not specific to my use case (rails, capistrano, deployment), and I've seen a lot of approaches to this problem that seem to involve poor security practices. Wondering whether others can vet my solution and advise whether it's secure?
First, not necessary, but I have no idea why /etc/init.d/nginx would need any (even read) access by other users. If they need to read it, make them become root (by sudo or other means), so I:
chmod 750 /etc/init.d/nginx
Since the ownership is owner root, group root (or can be set such with chown root:root /etc/init.d/nginx) only root, or a user properly sudo'ed, can read, change or run /etc/init.d/nginx, and I'm not going to give my deploy user any such broad rights. Instead, I'm only going to give the deploy user the specific sudo right to run the control script /etc/init.d/nginx. They will not be able to run an editor to edit it, because they will only have the ability to execute that script. That means that if a someone gets access to my box as the deploy user, they can restart and stop, etc, the nginx process, but they cannot do more, like change the script to do lots of other, evil things.
Specifically, I'm doing this:
visudo
visudo is a specific tool used to edit the sudoers file, and you have to have sudoer privileges to access it.
Using visudo, I add:
# Give deploy the right to control nginx
deploy ALL=NOPASSWD: /etc/init.d/nginx
Check the sudo man page, but as I understand this, the first column is the user being given the sudo rights, in this case, “deploy”. The ALL gives deploy access from all types of terminals/logins (for example, over ssh). The end, /etc/init.d/nginx, ONLY gives the deploy user root access to run /etc/init.d/nginx (and in this case, the NOPASSWD means without a password, which I need for an unattended deployment). The deploy user cannot edit the script to make it evil, they would need FULL sudo access to do that. In fact, no one can unless they have root access, in which case there's a bigger problem. (I tested that the user deploy could not edit the script after doing this, and so should you!)
What do you folks think? Does this work? Are there better ways to do this? My question is similar to this and this, but provides more explanation than I found there, sorry if it's too duplicative, if so, I'll delete it, though I'm also asking for different approaches.
The best practice is to use /etc/sudoers.d/myuser
The /etc/sudoers.d/ folder can contain multiple files that allow users to call stuff using sudo without being root.
The file usually contains a user and a list of commands that the user can run without having to specify a password. Such as
sudo service nginx restart
Note that we are running the command using sudo. Without the sudo the sudoers.d/myuser file will never be used.
An example of such a file is
myuser ALL=(ALL) NOPASSWD: /usr/sbin/service nginx start,/usr/sbin/service nginx stop,/usr/sbin/service nginx restart
This will allow the myuser user to call all start, stop and restart for the nginx service.
You could add another line with another service or continue to append them to the comma separated list, for more items to control.
Also make shure you have run the command below to secure things
chmod 0440 /etc/sudoers.d/myuser
This is also the way I start and stop services my own created upstart scripts that live in /etc/init
It can be worth checking that out if you want to be able to run your own services easily.
Instructions:
In all commands, replace myuser with the name of your user that you want to use to start, restart, and stop nginx without sudo.
Open sudoers file for your user:
$ sudo visudo -f /etc/sudoers.d/myuser
Editor will open. There you paste the following line:
$ myusername ALL=(ALL) NOPASSWD: /usr/sbin/service nginx start,/usr/sbin/service nginx stop,/usr/sbin/service nginx restart
Save by hitting ctrl+o. It will ask where you want to save, simply press enter to confirm the default. Then exit out of the editor with ctrl+x.

Reloading nginx config from within a rails app

I have a multi-app system running on a centOS box, that consists of our main app and a deployer app. when a client wants a new instance of our app, they use our deployer, fill in some info and the new install is created on our server. the issue i am having is that i can't get nginx to reload it's config file automatically. so after the deploy when visiting the new app we receive a 404 until i reload manually.
I've tried a few different ways including chmod /opt/nginx/sbin/nginx to 777, chmod the install script and deployer app to 777,
the script goes like this:
#create install directory -- works correctly
#copy files over -- works correctly
#run install script
##-- and then at this point i've tried multiple lines, including:
system("nginx -s reload") ## this works manually
system("/etc/init.d/nginx reload") ## this works manually
i've followed directions here: Restart nginx without sudo? to create a script to run without a sudo password and then tried this:
system("sudo /var/www/vhosts/deployer/lib/nginx_reload")
nothing seems to work, i'm assuming this is a permissions error, but maybe i'm wrong, if anyone could point me in any direction, that would be very helpful since i've been trying to figure this out for a few days too long and i'm fresh out of new ideas
sudo /etc/init.d/nginx reload

Resources