chcon -Rt httpd_sys_content_t /var/www/myapp/code
Setting up Ruby on Rails with Passenger + Nginx in a CentOS 7 VM running on Google Cloud Platform
Perspective
All commands are written (unless explicitly stated) from the perspective of a non-root user with sudo permissions. The intent is to create a user which will run the application we are creating, but that user will not have sudo permissions.
Machine Setup
I started here:
The only real difference was that I select the CentOS 7 OS.
Tool Setup
From here:
sudo yum install -y curl gpg gcc gcc-c++ make
Install RVM:
sudo yum -y install tar which
sudo yum -y install patch libyaml-devel libffi-devel glibc-headers autoconf gcc-c++ glibc-devel readline-devel zlib-devel openssl-devel bzip2 automake libtool bison
sudo gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3 7D2BAF1CF37B13E2069D6956105BD0E739499BDB
curl -sSL https://get.rvm.io | sudo bash -s stable
sudo usermod -a -G rvm `whoami`
Setup RVM secure_path:
if sudo grep -q secure_path /etc/sudoers; then sudo sh -c "echo export rvmsudo_secure_path=1 >> /etc/profile.d/rvm_secure_path.sh" && echo Environment variable installed; fi
Start a new shell and install the latest ruby and bundler:
bash rvm install ruby rvm --default use ruby #Using /home/nik/.rvm/gems/ruby-2.6.3 gem install bundler
Then install Node.js for Ruby on Rails
sudo yum install -y epel-release sudo yum install -y --enablerepo=epel nodejs npm
Nginx+Passenger+Ruby Setup
Then to get nginx + ruby running I followed directions here:
sudo yum install -y epel-release yum-utils sudo yum-config-manager --enable epel sudo yum clean all && sudo yum update -y
Date configuration:
date
# if the output of date is wrong, please follow these instructions to install ntp
sudo yum install -y ntp
sudo chkconfig ntpd on
sudo ntpdate pool.ntp.org
sudo service ntpd start
Install Passenger+Nginx
sudo yum install -y pygpgme curl
sudo curl --fail -sSLo /etc/yum.repos.d/passenger.repo https://oss-binaries.phusionpassenger.com/yum/definitions/el-passenger.repo
sudo yum install -y nginx passenger || sudo yum-config-manager --enable cr && sudo yum install -y nginx passenger
Uncomment the following settings in /etc/nginx/conf.d/passenger.conf:
passenger_root /some-filename/locations.ini; passenger_ruby /usr/bin/ruby; passenger_instance_registry_dir /var/run/passenger-instreg;
Edit the file and then restart the service:
sudo vi /etc/nginx/conf.d/passenger.conf sudo service nginx restart sudo /usr/bin/passenger-config validate-install sudo /usr/sbin/passenger-memory-stats
User Setup
We should not run our app as root because we will give too much power to the app if it gets hacked, and we should not run it as our user, because we may want different setups for different apps, which may all be running on the same instance.
NEWUSER=myappuser
sudo adduser $NEWUSER
sudo mkdir -p ~$NEWUSER/.ssh touch $HOME/.ssh/authorized_keys sudo sh -c "cat $HOME/.ssh/authorized_keys >> ~$NEWUSER/.ssh/authorized_keys" sudo chown -R $NEWUSER: ~$NEWUSER/.ssh sudo chmod 700 ~$NEWUSER/.ssh sudo sh -c "chmod 600 ~$NEWUSER/.ssh/*" sudo usermod -a -G rvm $NEWUSER
Git Project Setup
Setup the project “deploy to” directory:
sudo yum install -y git sudo mkdir -p /var/www/appname sudo chown $NEWUSER: /var/www/appname
Create a project on your SCM service site (GitHub, BitBucket, etc.).
Then setup ssh so you can clone.
sudo su $NEWUSER ssh-keygen -t rsa cat ~/.ssh/id_rsa.pub
Then add the ssh key to the service.
cd /var/www/appname git clone git@<service>.com:<user>/myproject.git ./
I just copied the example, then moved all of that into my repo:
git clone --bare https://github.com/phusion/passenger-ruby-rails-demo.git ./
Run the Project
From here:
rvm use ruby-2.6.3 #Using /usr/local/rvm/gems/ruby-2.6.3
Now install all of the gems:
bundle install --deployment --without development test
Generate the secret for Rails
bundle exec rake secret
Put that output into: config/secrets.yml
chmod 700 config db chmod 600 config/database.yml config/secrets.yml
Now create the DB and tables:
bundle exec rake assets:precompile db:migrate RAILS_ENV=production
This produced:
rails aborted!
LoadError: Error loading the 'sqlite3' Active Record adapter. Missing a gem it depends on? can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile.
...
Caused by:
Gem::LoadError: can't activate sqlite3 (~> 1.3.6), already activated sqlite3-1.4.0. Make sure all dependencies are added to Gemfile.
...
Looks like the Gemfile didn’t specify the dependency for ActiveRecord of < 1.4 and as such mine installed the sqlite3 version of 1.4.0 which is not compatible.
An attempt to add the following to the Gemfile:
gem 'sqlite3', '~> 1.3', '< 1.4'
Will cause:
bundle exec rake assets:precompile db:migrate RAILS_ENV=production
We are testing out a production environment. The normal process is to modify and change this stuff on a development machine and submit the changes for both the Gemfile and the Gemfile.lock to the SCM repo and as such when the production server receives the changes it will get them synchronized.
Because we are testing our setup on a production server for now, we will disable this feature, but we must re-enable it before actually going to production!
To do this we will unset the frozen configuration, but first let’s see how our machine is setup so we can restore it later:
$ bundle config frozen Settings for `frozen` in order of priority. The top value will be used Set for your local app (/var/www/nitrogen/.bundle/config): true Set for the current user (/home/nitrogen/.bundle/config): false
So for now:
bundle config --local frozen false bundle install --deployment --without development test #Installing sqlite3 1.3.13 (was 1.4.0) with native extensions passenger-config about ruby-command
Now update the config file:
- /etc/nginx/conf.d/myapp.conf
server { listen 80; server_name yourserver.com; # Tell Nginx and Passenger where your app's 'public' directory is root /var/www/myapp/code/public; # Turn on Passenger passenger_enabled on; passenger_ruby /path-to-ruby; }
Domain Setup
In GCP under Networking > VPC Network > External IP Addresses > change it to Static
This IP was automatically assigned to my VM. I was able to go to that IP address and see my nginx configuration page.
I then went to my domain host and created an A record to this static IP.
My domain, then resolved to the correct IP address.
Now when I visit the domain instead of seeing the nginx configuration page I see:
500 Internal Server Error
This is because when you connect from the domain it knows and tries to load that site (from the configuration file we just updated). However, while loading the app we hit an error, so let’s see what that was:
sudo tail /var/log/nginx/access.log
Hmmm looks like a permissions issue:
2019/08/07 12:34:00 [alert] 25410#0: *34 Cannot stat '/var/log/nginx/access.log': Permission denied (errno=13); This error means that the Nginx worker process (PID 54321, running as UID 987) does not have permission to access this file. Please read this page to learn how to fix this problem: https://www.phusionpassenger.com/library/admin/nginx/troubleshooting/?a=upon-accessing-the-web-app-nginx-reports-a-permission-denied-error
To verify which user this service is running as (987):
$ grep 987 /etc/passwd nginx:x:987:876:Nginx web server:/var/lib/nginx:/sbin/nologin
Looking at permissions, they look fine:
$ namei -l /var/www/myapp/code/config.ru f: /var/www/myapp/code/config.ru dr-xr-xr-x root root / drwxr-xr-x root root var drwxr-xr-x root root www drwxr-xr-x myappuser myappuser myapp drwxr-xr-x myappuser myappuser code -rw-rw-r-- myappuser myappuser config.ru
It looks like nginx should be able to read the file, just to make sure..
sudo -u nginx tail /var/www/myapp/code/config.ru
This works fine so wtf…
Well it looks like Security Enhanced Linux strikes again. To fix this:
Hello world!
Congratulations, you are running this app in Passenger!
Categories