Monday, December 15, 2014

Thoughts on setting up a CentOS 7 box

# VARIABLES:
# my_ip_address: 23.253.55.25
# my_first_not_root_user: admin
# my_ssh_port: 4972
# my_bitbucket_project_1_owner_username: abovemarket
# my_bitbucket_project_1_name: logs.abovemarket.com
# my_bitbucket_project_2_owner_username: abovemarket
# my_bitbucket_project_2_name: new.abovemarket.com
# my_server_admin_email_address: john.erck@abovemarket.com
# my_local_path_to_wildcard_crt: ~/Business/Above\ Market/SSL/STAR_abovemarket_com/STAR_abovemarket_com.crt
# my_local_path_to_wildcard_ca_bundle: ~/Business/Above\ Market/SSL/STAR_abovemarket_com/STAR_abovemarket_com.ca-bundle
# my_local_path_to_wildcard_pem: ~/Business/Above\ Market/SSL/STAR_abovemarket_com.pem
# my_local_path_to_wildcard_key: ~/Business/Above\ Market/SSL/STAR_abovemarket_com.key
# my_remote_filename_for_wildcard_crt: STAR_abovemarket_com.crt
# my_remote_filename_for_ca_bundle: STAR_abovemarket_com.ca-bundle
# my_remote_filename_for_pem: STAR_abovemarket_com.pem
# my_remote_filename_for_key: STAR_abovemarket_com.key

# Create new CentOS 7 box, then:

ssh root@my_ip_address
passwd
useradd my_first_not_root_user
passwd my_first_not_root_user
visudo # Add "my_first_not_root_user ALL=(ALL) ALL" after "root"
nano /etc/ssh/sshd_config # Update "Port" to my_ssh_port
systemctl restart sshd.service
vim myfirewall

# myfirewall TEMPLATE TEXT OPEN

#!/bin/bash
#
# iptables example configuration script
#
# Flush all current rules from iptables
#
iptables -F
#
#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# 
#
#  Accepts all established inbound connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 
# 
#  Allows all outbound traffic
#  You can modify this to only allow certain traffic
iptables -A OUTPUT -j ACCEPT
# 
# 
# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 
# 
#  Allows SSH connections
#
# THE -dport NUMBER IS THE SAME ONE YOU SET UP IN THE SSHD_CONFIG FILE
#
iptables -A INPUT -p tcp -m state --state NEW --dport my_ssh_port -j ACCEPT
# 
# 
# Allow ping
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
# 
# 
# log iptables denied calls
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# 
# 
# Reject all other inbound - default deny unless explicitly allowed policy
iptables -A INPUT -j REJECT
iptables -A FORWARD -j REJECT
#
#
# Save settings
#
/sbin/service iptables save
#
# List rules
#
iptables -L -v
#

# myfirewall TEMPLATE TEXT CLOSE

chmod +x myfirewall
./myfirewall
yum update
yum install httpd # Apache
yum install mysql # For release purposes needed on app server
yum install php php-mysql # The mother ship
yum install php-gd # Needed for app server image processing functions to work
yum install git
yum install mod_ssl openssl
systemctl enable httpd.service # So that it will automatically start after a reboot
exit
scp -P my_ssh_port ~/.ssh/id_rsa.pub root@my_ip_address:my_machine_id_rsa.pub
ssh -p my_ssh_port root@my_ip_address
cat my_machine_id_rsa.pub >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
restorecon -Rv ~/.ssh  # Ensure the correct SELinux contexts are set
exit
scp -P my_ssh_port ~/.ssh/id_rsa.pub admin@my_ip_address:my_machine_id_rsa.pub
ssh -p my_ssh_port admin@my_ip_address
cat my_machine_id_rsa.pub >> ~/.ssh/authorized_keys
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
restorecon -Rv ~/.ssh  # Ensure the correct SELinux contexts are set
ssh-keygen -t rsa -C "my_server_admin_email_address"
cat /home/my_first_not_root_user/.ssh/id_rsa.pub

# Then, go to: https://bitbucket.org/my_bitbucket_project_1_owner_username/my_bitbucket_project_1_name/admin/deploy-keys
# and add the key as "my_first_not_root_user@my_ip_address"

# Then, go to: https://bitbucket.org/my_bitbucket_project_2_owner_username/my_bitbucket_project_2_name/admin/deploy-keys
# and add the key as "my_first_not_root_user@my_ip_address"

exit

# Copy your SSL certificate file and the certificate bundle file to your Apache server.
# You should already have a key file on the server from when you generated your certificate
# request. If not, transfer that too.

scp -P my_ssh_port my_local_path_to_wildcard_crt root@my_ip_address:my_remote_filename_for_wildcard_crt
scp -P my_ssh_port my_local_path_to_wildcard_ca_bundle root@my_ip_address:my_remote_filename_for_ca_bundle
scp -P my_ssh_port my_local_path_to_wildcard_pem root@my_ip_address:my_remote_filename_for_pem
scp -P my_ssh_port my_local_path_to_wildcard_key root@my_ip_address:my_remote_filename_for_key
ssh -p my_ssh_port root@my_ip_address
mv my_remote_filename_for_wildcard_crt /etc/pki/tls/certs/my_remote_filename_for_wildcard_crt
mv my_remote_filename_for_ca_bundle /etc/pki/tls/certs/my_remote_filename_for_ca_bundle
mv my_remote_filename_for_pem /etc/pki/tls/private/my_remote_filename_for_pem
mv my_remote_filename_for_key /etc/pki/tls/private/my_remote_filename_for_key
vim +/SSLCertificateFile /etc/httpd/conf.d/ssl.conf

# Update file like so:
SSLCertificateFile /etc/pki/tls/certs/my_remote_filename_for_wildcard_crt
SSLCertificateKeyFile /etc/pki/tls/private/my_remote_filename_for_key
SSLCACertificateFile /etc/pki/tls/certs/my_remote_filename_for_ca_bundle

systemctl restart httpd.service
mkdir -p /home/admin/my_bitbucket_project_1_name
mkdir -p /home/admin/my_bitbucket_project_2_name
chown -R my_first_not_root_user:my_first_not_root_user /home/admin/my_bitbucket_project_1_name
chown -R my_first_not_root_user:my_first_not_root_user /home/admin/my_bitbucket_project_2_name

su my_first_not_root_user
cd /home/admin/my_bitbucket_project_1_name
git clone git@bitbucket.org:my_bitbucket_project_1_owner_username/my_bitbucket_project_1_name.git .
cd /home/admin/my_bitbucket_project_2_name
git clone git@bitbucket.org:my_bitbucket_project_2_owner_username/my_bitbucket_project_2_name.git .
exit
mkdir /etc/httpd/sites-available
mkdir /etc/httpd/sites-enabled
vim /etc/httpd/conf/httpd.conf

# Add the following line to the end of the file:
IncludeOptional sites-enabled/*.conf

vim /etc/httpd/sites-available/my_bitbucket_project_1_name.conf

# Add the following text:
<VirtualHost *:80>
    ServerName www.my_bitbucket_project_1_name
    ServerAlias my_bitbucket_project_1_name
    DocumentRoot /home/admin/my_bitbucket_project_1_name/www
    ErrorLog /home/admin/my_bitbucket_project_1_name_error.log
    CustomLog /home/admin/my_bitbucket_project_1_name_requests.log combined
</VirtualHost>

<VirtualHost *:443>
    ServerName www.my_bitbucket_project_1_name
    ServerAlias my_bitbucket_project_1_name
    DocumentRoot /home/admin/my_bitbucket_project_1_name/www
    ErrorLog /home/admin/my_bitbucket_project_1_name_error.log
    CustomLog /home/admin/my_bitbucket_project_1_name_requests.log combined
</VirtualHost>

vim /etc/httpd/sites-available/my_bitbucket_project_2_name.conf

# Add the following text:
<VirtualHost *:80>
    ServerName www.my_bitbucket_project_2_name
    ServerAlias my_bitbucket_project_2_name
    DocumentRoot /home/admin/my_bitbucket_project_2_name/www
    ErrorLog /home/admin/my_bitbucket_project_2_name_error.log
    CustomLog /home/admin/my_bitbucket_project_2_name_requests.log combined
</VirtualHost>

<VirtualHost *:443>
    ServerName www.my_bitbucket_project_2_name
    ServerAlias my_bitbucket_project_2_name
    DocumentRoot /home/admin/my_bitbucket_project_2_name/www
    ErrorLog /home/admin/my_bitbucket_project_2_name_error.log
    CustomLog /home/admin/my_bitbucket_project_2_name_requests.log combined
</VirtualHost>

ln -s /etc/httpd/sites-available/my_bitbucket_project_1_name.conf /etc/httpd/sites-enabled/my_bitbucket_project_1_name.conf
ln -s /etc/httpd/sites-available/my_bitbucket_project_2_name.conf /etc/httpd/sites-enabled/my_bitbucket_project_2_name.conf
apachectl restart

# Make sure you've mapped your DNS records to point to my_ip_address for each of
# the project names/domains you setup.

# Boom, you're done.

Thursday, October 9, 2014

How does one go about creating a softlink?

ln -s ~/Dropbox/secure/project/_development.yaml clitools/_development.yaml
ln -s ~/Dropbox/secure/project/_local.yaml clitools/_local.yaml
ln -s ~/Dropbox/secure/project/_production.yaml clitools/_production.yaml
ln -s ~/Dropbox/secure/project/_release.yaml clitools/_release.yaml

Saturday, April 26, 2014

What should I do after ordering a CentOS 6.5 server?

Okay, so I know how to do this on a Debian 7 box. However, I've never done server setup on a CentOS machine, until now. Here's a post I did on how to setup a Debian 7 box: http://oneqonea.blogspot.com/2013/04/how-do-i-set-up-next-generation.html. This post is based on that one. I'm following the other post as a conceptual guide and using this one to capture my translation process.

ssh root@youripaddress

Changing the root user's password was the same between Debian and CentOS:
passwd

Instead of "adduser admin" it was "useradd youruser". In CentOS you need to follow "useradd youruser" with "passwd youruser" Reference: https://www.centos.org/docs/5/html/5.1/Deployment_Guide/s2-users-add.html Also note this is a decent article too: https://www.digitalocean.com/community/articles/initial-server-setup-with-centos-6

visudo process was the same between Debian and CentOS (i.e. making youruser have sudo privileges):
youruser    ALL=(ALL)    ALL

Updating the ssh port number was the same between Debian and CentOS. However, Debian has PasswordAuthentication commented out (set to no) while CentOS defaults to PasswordAuthentication yes.

vim /etc/ssh/sshd_config
"Update your port number to something unique such as 7919" Save the file (ZZ).

How to restart the ssh service varied slightly: http://www.cyberciti.biz/faq/howto-restart-ssh/. The CentOS way is: /etc/init.d/sshd restart

In terms of how to configure iptables. Things were different. I implemented the same rules but the steps were as follows (inspired by http://wiki.centos.org/HowTos/Network/IPTables):

  1. I logged in as the root user
  2. I created a file called myfirewall and gave it the execute bit: chmod +x myfirewall
  3. vim myfirewall and copy and paste (with ssh port edits that sync with /etc/ssh/sshd_config edits) the following text into it and then save and close: http://oneqonea.blogspot.com/2014/04/myfirewall-file.html
  4. As the root user run: ./myfirewall Then, boom. You're locked and loaded.
    1. Clears rules then resets them. This executes the iptables init script, which runs /sbin/iptables-save and writes the current iptables configuration to /etc/sysconfig/iptables. Upon reboot, the iptables init script reapplies the rules saved in /etc/sysconfig/iptables by using the /sbin/iptables-restore command.
If you're still logged in as root, instead of "aptidude update" run "yum update" (add sudo if you're not root). Reference: https://www.centos.org/forums/viewtopic.php?t=39652

Okay so the next steps vary slightly depending on whether or not we're setting up an app/web server or a database server. App/web gets apache and php. Database gets mysql.

Here's good article for getting Apache, PHP, and MySQL installed: https://www.digitalocean.com/community/articles/how-to-install-linux-apache-mysql-php-lamp-stack-on-centos-6. Install the right software given the type of machine you're setting up. Note that if you're setting up an app server that runs an app that connects to a mysql database, that you'll need to install mysql on the app server even if you're connecting to a remote mysql database (for release purposes).

App server command brief:
yum install httpd # Already installed
yum install mysql # For release purposes needed on app server
yum install php php-mysql # The mother ship
yum install php-gd # Needed for app server image processing functions to work

Note, after you install MySQL run: service mysqld start

By default PHP Version 5.3.3 is installed. I need newer than that. I followed this tutorial: http://webees.me/how-to-install-php-5-4-and-mysql-5-5-in-centos-6-4-via-yum/

By default MySQL Server Version 5.1.73 is installed. I need newer than that. I followed this tutorial: http://webees.me/how-to-install-php-5-4-and-mysql-5-5-in-centos-6-4-via-yum/ (my app requires 5.5 or higher for utf8 character reasons).

Okay, so if you're setting up a server that uses PHP's imagecreatefromjpeg function then you need gd. Since the php we just installed doesn't have gd we run the following command to add it:

yum --enablerepo=epel,remi,rpmforge install gd gd-devel php-gd
/etc/init.d/httpd restart

After doing all that I setup ssh keys (and optionally turn password access off) for both machines. I followed this tutorial: http://wiki.centos.org/HowTos/Network/SecuringSSH#head-9c5717fe7f9bb26332c9d67571200f8c1e4324bc

Then I installed git via: yum install git

Then I create a key-pair on the server:
ssh-keygen -t rsa -C "your_email@example.com"

Save in default location and skip the passphrase.

Now add your id_rsa.pub file content to Bitbucket as a deploy key: https://bitbucket.org/acctowner/projectname/admin/deploy-keys. I like to label my key using machine IP address. To get key:

cat ~/.ssh/id_rsa.pub

After doing that I git cloned project source files from their Bitbucket home to a folder within admin's home directory:

git clone git@bitbucket.org:acctowner/projectname.git

Then I installed a wild card ssl cert by loading the .crt and .key files to the remote machine like so: http://wiki.centos.org/HowTos/Https. Note that I skipped part three and didn't do anything special with virtual hosts. Command recap:
$ sudo yum install mod_ssl openssl
$ exit
$ scp -P 1234 ~/your/cert/on/your/comp/STAR_domain_com/STAR_domain_com.crt root@youripaddress:STAR_domain_com.crt
$ scp -P 1234 ~/your/cert/on/your/comp/STAR_domain_com/STAR_domain_com.key root@youripaddress:STAR_domain_com.key
$ ssh -p 1234 root@youripaddress
$ mv STAR_domain_com.crt /etc/pki/tls/certs/STAR_domain_com.crt
$ mv STAR_domain_com.key /etc/pki/tls/private/STAR_domain_com.key
$ vim +/SSLCertificateFile /etc/httpd/conf.d/ssl.conf # Update certificate paths
$ /etc/init.d/httpd restart

Now on to setting up the apache hosting environment:
sudo vim /etc/httpd/conf/httpd.conf

#DocumentRoot "/var/www/html"
DocumentRoot "/home/youruser/yourproject/www"

#<Directory "/var/www/html">
<Directory "/home/youruser/yourproject/www">

# For CodeIgniter, faster than .htaccess file
RewriteEngine On
RewriteBase /
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php?/$1 [L]

sudo /etc/init.d/httpd restart

You will experience a 403 Forbidden error on refresh.

I found the steps I needed to follow here: https://drupal.org/node/244924
$ cd /home/youruser/yourproject
$ sudo chown -R youruser:apache .
$ sudo find . -type d -exec chmod u=rwx,g=rx,o= '{}' \;
$ sudo find . -type f -exec chmod u=rw,g=r,o= '{}' \;
$ sudo chmod 711 /home/youruser

Then 770 the dirs you need apache to be able to write to (temp, runtime, logs, etc).

chmod 770 yourproject/application/logs/
To enable CodeIgniter app to be able to write files to the app's default log dir.

chmod 770 yourproject/application/runtime
chmod 770 yourproject/application/runtime/img
chmod 770 yourproject/www/images/local_cdn
A reminder for myself to enable app specific write dirs.

sudo vim /etc/php.ini

Then update php's memory limit from its default of 128M to something bigger like so:
sudo vim /etc/php.ini
;memory_limit = 128M
;
;johnerck says: free -m prints "Mem: free 6290" so I figure we can alloc 4000MB
memory_limit = 4000M

; Maximum allowed size for uploaded files.
; http://php.net/upload-max-filesize
;upload_max_filesize = 2M
upload_max_filesize = 100M

; http://php.net/post-max-size
;post_max_size = 8M
post_max_size = 100M

;error_log = php_errors.log
error_log = /home/youruser/php_error.log

Save the file (ZZ).

Now create the log file we just referenced in our php.ini edits:
vim /home/youruser/php_error.log
/*Add some content such as "php_error.log" and save (ZZ)*/
Make sure the log file just created is 664 and "sudo chown youruser:apache php_error.log"

sudo /etc/init.d/httpd restart

Ok, we are for sure linked in with php errors!! It's been tested!! For example, if there is a syntax error in your php code, such as a missing semicolon, php will log the error to /home/youruser/php_error.log. You can tail -f /home/youruser/php_error.log and then refresh the browser on a page with a known missing semicolon and you will see the error write to the terminal (and filesystem). This is great.

If you're hosting a CodeIgniter app like I am then you'll want to check the following:
Make sure $config['log_threshold'] is >= 1 (so that it does actually log to the file system).

Note that in addition to the php error logs we just finished setting up, Apache also has an error log that can be viewed here: sudo cat /var/log/httpd/error_log

Next I needed to setup clitools and deploy with secure config file to fix the following error message from our API codebase: "The application environment is not set correctly.". I installed clitools, filled out the _*.yaml files and then ran a clitools/releaseto production.

I then needed to get the database server further setup:
http://www.rackspace.com/knowledge_center/article/installing-mysql-server-on-centos

Where they were doing "'demouser'@'localhost'" I was doing "'myuser'@'%'" so it can access remotely. I like this guy's recommendation regarding performance and iptables: http://www.noelherrick.com/blog/creating-users-granting-permissions-in-mysql

For a simple localhost setup you'll likely run the following commands in this order:

mysql -uroot -p

SELECT User, Host, Password FROM mysql.user; -- Shows current state

CREATE DATABASE demodb;

INSERT INTO mysql.user (User,Host,Password) VALUES('demodbuser','localhost',PASSWORD('yourfancypassword'));

FLUSH PRIVILEGES;

SELECT User, Host, Password FROM mysql.user; -- Shows current state

GRANT ALL PRIVILEGES ON demodb.* to demodbuser@localhost;

FLUSH PRIVILEGES;

SHOW GRANTS FOR 'demodbuser'@'localhost'; -- Shows current state

Now I'm going to see if I can get the two boxes to talk to each other over the SoftLayer private network. Okay, yup, they're talking.

What does my default CentOS iptables firewall file look like?

CentOS myfirewall template file:
#!/bin/bash
#
# iptables example configuration script
#
# Flush all current rules from iptables
#
iptables -F
#
#  Allows all loopback (lo0) traffic and drop all traffic to 127/8 that doesn't use lo0
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT
# 
#
#  Accepts all established inbound connections
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
# 
# 
#  Allows all outbound traffic
#  You can modify this to only allow certain traffic
iptables -A OUTPUT -j ACCEPT
# 
# 
# Allows HTTP and HTTPS connections from anywhere (the normal ports for websites)
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
# 
# 
#  Allows SSH connections
#
# THE -dport NUMBER IS THE SAME ONE YOU SET UP IN THE SSHD_CONFIG FILE
#
iptables -A INPUT -p tcp -m state --state NEW --dport 7921 -j ACCEPT
# 
# 
# Allow ping
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
# 
# 
# log iptables denied calls
iptables -A INPUT -m limit --limit 5/min -j LOG --log-prefix "iptables denied: " --log-level 7
# 
# 
# Reject all other inbound - default deny unless explicitly allowed policy
iptables -A INPUT -j REJECT
iptables -A FORWARD -j REJECT
#
#
# Save settings
#
/sbin/service iptables save
#
# List rules
#
iptables -L -v
#