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: 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:

Instead of "adduser admin" it was "useradd youruser". In CentOS you need to follow "useradd youruser" with "passwd youruser" Reference: Also note this is a decent article too:

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: 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

  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:
  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:

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: 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:

By default MySQL Server Version 5.1.73 is installed. I need newer than that. I followed this tutorial: (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:

Then I installed git via: yum install git

Then I create a key-pair on the server:
ssh-keygen -t rsa -C ""

Save in default location and skip the passphrase.

Now add your file content to Bitbucket as a deploy key: I like to label my key using machine IP address. To get key:

cat ~/.ssh/

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

git clone

Then I installed a wild card ssl cert by loading the .crt and .key files to the remote machine like so: 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:
$ 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.
;upload_max_filesize = 2M
upload_max_filesize = 100M

;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:

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:

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


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


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

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


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:
# 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 -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
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

Wednesday, January 8, 2014

How to delete outdated provisioning profiles from Xcode 5

You can delete the files directly from ~/Library/MobileDevice/Provisioning Profiles

Open finder, ⌘-Shift-G, and paste in the above path. Restart Xcode afterward.

UIScrollView and Autolayout Demystified

Just finished reading and implementing the described examples in the following link:

Chapter 20. Scroll Views

I was very impressed with this thorough yet easy to read description of how scroll views work.

Thursday, December 12, 2013

Objective C, JSON APIs and a few details worth noting...

Types that can be returned by JSON API:

  • string
  • number
  • boolean
  • null

When parsing JSON with Objective C's NSJSONSerialization class we get the following conversions:

  • string (NSString)
  • number (NSNumber)
  • boolean (NSNumber*)
  • null (NSNull*)

*Note that boolean to NSNumber will NSLog itself as __NSCFBoolean. What?! We just noted above that booleans get parsed as NSNumbers!! Okay, settle, it all does make sense with a little further explanation; __NSCFBoolean is a private class that is used in the NSNumber class cluster. Don't concern yourself with it. Understand that JSON response fields containing a boolean will get parsed as an NSNumber and that to check the parsed value for its meaning (i.e. true/false) you can NOT do if (myBoolean) or if (!myBoolean). Instead, you need to check if ([myBoolean boolValue]) or if (![myBoolean boolValue]) respectively.

*Note that null to NSNull is different than what you might hope for (null to nil). If you send a message to NSNull that it doesn't understand (this is easy to do if you're working with an optional string field), NSNull will crash your app (as opposed to just returning nil). Yikes!

So, how does it all add up? Well, from my experience as a PHP programmer, API developer, and iOS client creator... I'd break it down like this:

Strings, numbers, booleans, and nulls all serve a purpose on the server (both the backend database and API application layer (PHP)). For example, null means a value that hasn't yet been defined. We write queries using null, do special application logic based on it (i.e. send an email where we otherwise wouldn't, etc). It's true that in some cases the null value might even be important to the client, and if so, could be passed on through. However, from my experience, it rarely is. With Objective C being a strongly typed language and the risk of crashing being very real, I've found that I like writing APIs that simplify their output to just strings. 99% of the time, even when receiving a number from the API, I am getting it so I can display it in the UI (i.e. I need it as a string). Therefore the simplicity of working with an all strings API is wonderful and also helps avoid app crashes. I even like to switch my boolean values to strings and just use yes or no. I like the way it reads and keeps the entire API as strings only.

It's all about style. For me, I've found simplifying my API output makes for an easier to use JSON API for my iOS apps.