HOWTO: Build an Objective-C Continuos Integration Server on FreeBSD

HOWTO: Build an Objective-C Continuos Integration Server on FreeBSD
Purpose:
The purpose of this HOWTO is to explain how to build a continuos integration (CI) server that conforms to a jailed server architecture. The CI software we'll be using is /devel/jenkins, the repository we'll be using is /devel/git, the Objective-C runtime framework is /deve/gnustep and finally we'll be using /sysutils/ezjail-admin as our jails management application.

Background:
Why use CI?
A common problem with developing software in teams is actually getting a build ready - one developer manages to build successfully while another not. Why is this? Various reasons can explain this, one of which could be one developer forgetting to upload a file to the repository but regardless of excuses the question becomes how can we successfully release builds and get away from such problems? One of the answers to this question is continuos integration servers - every time a developer submits code to the central repository, the code gets built by the CI server and a pass / fail is determined whether the newest changes successfully run or break the build. If you are interested in such a solution for your project ... this article is for you.

Why use a Jailed Architecture
A common security problem with servers is if one service (example: Nginx, Apache) gets hacked, then the entire computer is considered compromised. One remedy to this situation is locking away each major server process in a separate jail with it's own virtual environment and prevent access from outside it - this is called a jailed architecture. The benefit is if a single server gets hacked, the entire system and all the separate servers remain secure. Therefore in this article, we will demonstrate how to implement such a architecture to build a production quality CI server.

Assumption:
  • You have installed a fresh copy of FreeBSD 9.1 on your computer
  • You have included 'ports' & 'src' during the fresh install.
  • Your computer can connect to the internet.
  • You are an intermediate/advanced UNIX user but I will write assuming you are a rambo-type survivalist newbie who is determined to getting things working
  • You are familiar with 'vi'
  • You are using an Apple Macintosh Computer (Or UNIX-like computer) and a separate computer with FreeBSD installed on it where you are building the CI server.
  • You are familiar with Apple Xcode and develop Objective-C code on it (Or some-sort of Objective-C SDK/IDE)
  • You have a lot of time to dedicate to trying this out!

Setup Jailed Server Architecture
The following series of step will install the jail administration management framework for our architecture. Note: ezjail is a fantastic program, it simplifies a great deal of complex jails modification & scales well with new services as you will observe in this HOWTO:
# cd /usr/ports/sysutils/ezjail
# make install clean
# rehash
# ezjail-admin install

(Note: Step takes a VERY LONG time!)
# ezjail-admin update -b

(Note: Allow jails to use tcp/ip)
# echo 'security.jail.allow_raw_sockets=1' >> /etc/sysctl.conf
# echo 'ezjail_enable="YES"' >> /etc/rc.conf

What's Next:
Now that we created a FreeBSD with a jail system, the idea is that from now on out, whenever we want to add a new 'server' type process to our system, we will create a designated jail for that process and install the server process in that jail. This HOWTO explains how to create two jails and make them interoperable: jenkins and git. If you want to follow this architecture, from now one whenever you want a new 'server' process, try to create a separate process in a jail.
 
Setup a Jailed Git Repository

Setup a Jailed Git Repository
This part explains how to setup a git repository in a jailed environment and make it interoperable with another 'jenkins' jail. Note, this section was written from this HOWTO.

A) Pre-configuration:
We must assign an IP address to our jail. Make sure the IP we assign won't become available to anyone else on the network (Hint: make it static IP or limit DHCP IP range for clients with your firewall/router). Once we are ready, enter the following:
# echo 'ifconfig_re0_alias0="inet 192.168.0.238 netmask 255.255.255.0" # git' >> /etc/rc.conf

Note: If you want to add another IP address for later, don't forget to increment the integer on the 'alias0' value for the new IP address you plan to add.

2) Setup "git" Jail
Create our "git" jail in our system with the IP we assigned it to:
# ezjail-admin create git 192.168.0.238
# ezjail-admin start git
# ezjail-admin console git

Setup ports system in our jailed environment - In summary Ports are not allowed to be setup in our jail, this happens to every jail we create. To remedy this, we may point the ports
in our main/non-jailed ports file to the ports folder inside the jail and get temporary access to the ports tree to setup our jail.
# rm -R /usr/ports
# mkdir /usr/ports
# exit
# mount_nullfs /usr/ports /usr/jails/git/usr/ports

Note:
  • Simply restarting the computer, or using the 'umount' command will result in the linkage being closed.
  • If you want to delete your jail, don't forget to unmount the linage else when you delete the jail, you'll delete your ports folder you have outside the jail!

Enter the "git" jail
# ezjail-admin console git

Configuring "git" jail
# vi /etc/rc.conf

Populate the file with the following:
Code:
# No network interfaces in jails
network_interfaces="lo0 re0"

# Prevent rpc
rpcbind_enable="NO"

# Prevent loads of jails doing their cron jobs at the same time
cron_flags="$cron_flags -J 15"

# Prevent syslog to open sockets
syslogd_flags="-ss"

# Prevent sendmail to try to connect to localhost
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

# Bring up sshd, it takes some time and uses some entropy on first startup
  sshd_enable="YES"

# vi /etc/resolv.conf
[Add the following:]

Code:
search          your_domain.com
nameserver      192.168.0.1

3) Verify 'git' jail can access WAN.
Before we start installing, let's verify that our jail environment can access the internet. Try
this command and if pings are received, we may proceed to the next step. If not, you must
stop and investigate:
# ping google.com

4) Install Supplement Tools:
portupgrade
We will install a port installer application. This tool is very useful when we want to upgrade git
sometime down the road in the future when a newer version becomes available. It's also
incredibly helpful with installing an application from scratch too! Lets install it from the
ports tree:
# cd /usr/ports/ports-mgmt/portupgrade
# make install -DBATCH clean

5) Install Main Tool:
git
Now we will begin installing git using our new fancy tool! Note: (The next series of steps where taken from this link)
# portupgrade -N --batch git

6) Initial Git Setup
# git config --global user.name "first_name last_name"
# git config --global user.email [email]your_email@your_email_domain.com[/email]

7) Setup User
A) Setup Git user + group
# pw groupadd -n git -g 9418
# pw useradd -n git -u 9418 -g git -c git -d /git \
-s /usr/local/libexec/git-core/git-shell -h -


# vi /etc/group
You will see:
git:*:9418:

Note:
If you want to add other user1 and user2 then it will look like:
git:*:9418:user1,user2

8) Setting up directory structure
Create the directory that will be keeping the repositories and set proper permissions:
# mkdir -p /git
# chown git:git /git/
# chmod 755 /git
# mkdir /git/base/
# chown git:git /git/base/
# chmod 775 /git/base/

[Apple Macintosh Computer]
Note: This can be done on any UNIX-like computer in your network, not just Macs. Also it can be done on windows if you have a UNIX-like emulator like cygwin.

9) Setting up Public keys
Go to the ssh folder and generate a key:
# cd ~/.ssh
# ssh-keygen -t rsa -C "your_email@your_email_domain.com"
Do not give it a filename. Just hit enter. 
Creates a public and private key. Will ask for pass phrase but you don’t need one. Just hit enter.
<Copy the data into clipboard>
# vi id_rsa.pub

Note: It is absolutely essential that you do NOT use a passphrase. The Jenkins Git Plugin does not support pass phrases so if set one then it'll be virtually impossible to setup Git with Jenkins.

Jenkins Jail
(Back to the server computer we're setting up!)

For every user that needs commit access to the remote repositories, collect their public SSH keys and put them to the authorized_keys file:
# mkdir /git/.ssh/
# chmod 700 /git/.ssh/
# touch /git/.ssh/authorized_keys
# chmod 600 /git/.ssh/authorized_keys
(Put the public keys into authorized_keys from the clipboard, one per line)
# chown -R git:git /git/.ssh/

11) Starting git-daemon
In order the clients to be able to pull and fetch from the repos we will use the git-daemon(1). Add these lines at the end of your rc.conf file, so that git-daemon is started during boot-time:
# vi /etc/rc.conf

[Insert the following]
Code:
# Enable git-daemon
git_daemon_enable="YES"
git_daemon_directory="/git"
git_daemon_flags="--syslog --base-path=/home/git --detach --reuseaddr"

Now during boot-time the git-daemon will be started as well. To start git-daemon, without rebooting, execute the following command:
# /usr/local/etc/rc.d/git_daemon start

10) Creating a test Git Repositories
Now, let's create some Git repositories for our system.
# mkdir /git/base/test.git

Now, lets initialize them - make them ready for push/pull
# cd /git/base/test.git && git init --bare --shared

Now, let's export these repos and set proper permissions, so we can clone and fetch from them:
# touch /git/base/test.git/git-daemon-export-ok

Go through all the repository folders/files and make them owned by 'git' user.
# chown -R git:git /git

[Apple Macintosh Computer]

12) Pushing content to the repositories (Console)
Suppose you are a commiter and want to add some files to the already existing and still empty repositories. You will then create a local directory, git-init it, add the files to the index, commit and push them to the remote repo. Note, that this operations requires that your public SSH key is already present in the authorized_keys file on the remote server:
# mkdir ~/my-git-repo
cd ~/my-git-repo && git init [/CMD]

Initialized empty Git repository
# git remote add origin [email]git@192.168.0.238:/git/base/test.git[/email]
# echo foo > bar
# git add .
# git commit -m 'initial commit'
# git push origin master

If the commit worked, congratulations you have a jailed Git server running!

13) Cloning the repositories
Now that you want to clone and track the remote repository what you do is this:
# git clone [url=ssh://git@192.168.0.238//git/base/test.git]ssh://git@192.168.0.238//git/base/test.git[/url]

Note: Run this command in console.

14) Pushing contents to the repository (Xcode)
Go to Xcode repositories under 'organizer' and click 'add'. Then add the following link.
# ssh://git@192.168.0.238/git/base/test.git
 
Setup a Jailed Jenkins Server

Setup a Jailed Jenkins Server
1) Pre-configuration:
We must assign an IP address to our jail. Make sure the IP we assign won't become available to anyone else (make it static IP or limit DHCP IP range for clients.). Once we are ready, enter the following:
# echo 'ifconfig_re0_alias0="inet 192.168.0.236 netmask 255.255.255.0" # jenkins+tomcat' >> /etc/rc.conf

Note: If you want to add another IP address for later, don't forget to increment the integer on the 'alias0' value for the new IP address you plan to add.

2) Setup "jenkins" Jail
Create our "jenkins" jail in our system with the IP we assigned it to:
# ezjail-admin create jenkins 192.168.0.236
# ezjail-admin start jenkins
# ezjail-admin console jenkins

Setup ports system in our jailed environment
# rm -R /usr/ports
# mkdir /usr/ports
# exit
# mount_nullfs /usr/ports /usr/jails/jenkins/usr/ports

Go back inside it our new jail
# ezjail-admin console jenkins

Configuring "jenkins" jail
# vi /etc/rc.conf
[Add the following:]

Code:
# No network interfaces in jails
network_interfaces="lo0 re0"

# Prevent rpc
rpcbind_enable="NO"

# Prevent loads of jails doing their cron jobs at the same time
cron_flags="$cron_flags -J 15"

# Prevent syslog to open sockets
syslogd_flags="-ss"

# Prevent sendmail to try to connect to localhost
sendmail_enable="NO"
sendmail_submit_enable="NO"
sendmail_outbound_enable="NO"
sendmail_msp_queue_enable="NO"

# Bring up sshd, it takes some time and uses some entropy on first startup
  sshd_enable="NO"

# vi /etc/resolv.conf
[Add the following:]

Code:
search          your_domain.com
nameserver      192.168.0.1

3) Verify 'jenkins' jail can access WAN
Before we start installing, let's verify that our jail environment can access the internet. Try
this command and if pings are received, we may proceed to the next step. If not, you must
stop and investigate:
# ping google.com

4) Install Supplement Tools:
portupgrade
We will install a port installer application. This tool is very useful when we want to upgrade git
sometime down the road in the future when a newer version becomes available. It's also
incredibly helpful with installing an application from scratch too! Lets install it from the
ports tree:
# cd /usr/ports/ports-mgmt/portupgrade
# make install -DBATCH clean

openjdk7
The java framework that 'jenkins' requires to use:
# portupgrade -N --batch openjdk7

If it doesn't work, try this:
# cd /usr/ports/java/openjdk7

git
In order to access our git repo, we'll need to have an instance of the git client installed. This step might seem counter intuitive but here's the explanation: We are not setting up a git server, we are simply installing the git binary so jenkins server may use the git client to communicate with our git server in the git jail. We do this because the soon-to-be installed Jenkins plugin will require this. So lets begin:
# ezjail-admin console jenkins
# portupgrade -N --batch git
# git config --global user.name "first_name last_name"
# git config --global user.email [email]your_email@your_email_domain.com[/email]
# portupgrade -N --batch git

5) Install Main Tool:
jenkins
Now we will begin installing jenkins using our new fancy tool!
# portupgrade -N --batch jenkins

6) Configure Main Tool
Modify our rc.conf file with the following:
# vi /etc/rc.conf

Populate it with the following contents:
Code:
jenkins_enable="YES"
jenkins_home="/usr/local/jenkins"
jenkins_args="--webroot=${jenkins_home}/war --httpListenAddress=192.168.0.236 --httpPort=8180 --ajp13ListenAddress=192.168.0.236 --ajp13Port=8009 --prefix=/jenkins"
jenkins_java_home="/usr/local/openjdk7"
jenkins_user="jenkins"
jenkins_group="jenkins"
jenkins_log_file="/var/log/jenkins.log"

Note:
  • We manually assigned our IP address to '192.168.0.236'
  • We specified we are using openjdk7. If we want to use another version, here is where we make the changes
  • If jenkins encounters any errors, be sure to read the log found at /var/log/jenkins.log

Start Jenkins Service
Now that we are ready to go. Start up jenkins:
# /usr/local/etc/rc.d/jenkins start

[Apple Macintosh Computer]

Using your default web-browser, enter the following
http://192.168.0.236:8180/jenkins/

If you see something on the screen then you have successfully setup a jenkins server!
 
Integrate the Git Jail with the Jenkins Jail

Integrate the Git Jail with the Jenkins Jail

The next series of steps will involve establishing a public-private key authentication between the two jails so the two servers may communicate with each other.

[Jenkins Jail]
Exit the git jail and enter the jenkins jail:
# ezjail-admin console jenkins

Generate our Public-Private keys...
# ssh-keygen -t rsa -C "your_email@your_email_domain.com"
Do not give it a filename. Just hit enter.
Creates a public and private key. Will ask for pass phrase but you don’t need one. Just hit enter.
(Again, absolutely no pass phrase!)

We have successfully created our Public-Private keys. The next step in a FreeBSD system is we need to copy the
keys into the 'jenkins' home user directory. The next steps explain how to set it up.
# mkdir /usr/local/jenkins/.ssh
# chown jenkins /usr/local/jenkins/.ssh
# cp ~/.ssh/* /usr/local/jenkins/.ssh
# chown jenkins /usr/local/jenkins/.ssh/*
# chmod 600 /usr/local/jenkins/.ssh/*

We have successfully set the Public-Private keys for the jenkins user, now lets add the Jenkins' public key to the 'git' jail so both servers may authenticate successfully.
Let's load up the public key we will share with our 'git' jail.
# vi ~/.ssh/id_rsa.pub

Note: Copy the data into clipboard….

[Git Jail]
Load up our 'git' jail.
# exit
# ezjail-admin console git

Add our public key from Jenkins into our git. (Note: Paste from our clipboard the saved id_rsa.pub contents)
# vi /git/.ssh/authorized_keys

Modify the following file to contain the following:
# vi /etc/ssh/sshd_config

Must contain:
Code:
RSAAuthentication yes
PubkeyAuthentication yes
ChallengeResponseAuthentication no
PasswordAuthentication no
UsePAM no

Note: What did we just do? We basically told our SSH system that we will no longer be using passwords for authentication but public-private key authentication instead.
(Possibly add root,jenkins to /etc/group if error)

Now restart ezjail-admin
# exit
# ezjail-admin restart;

[Apple Macintosh Computer]
Verify Git Works
Lets 'try' to clone a copy of our test repo. (Note: Accept RSA key). Simply load up the terminal in
Code:
Application->Utilities->Terminal

Then go to your home directory:
# cd ~/

Once you're there, you should be able to make a copy of the repository. An empty folder should exist afterwards. If so, congradulations your git setup works with your mac.
# git clone [url=ssh://git@192.168.0.238//git/base/test.git]ssh://git@192.168.0.238//git/base/test.git[/url]

Verify Jenkins Work
Load up Safari and go to the following url: http://192.168.0.236:8180
It should load up Jenkins.

Verify Git+Jenkins Work
Click:
Code:
Manage Jenkins ->Manage Plugins ->Available

Once completed, install the following plugins:
  • git
  • git-client
  • git-server
  • clone-scm

Note: If nothing appears in the 'Available' section, simply download these manually from this link and install each one manually in the "Advanced" section.

Next is we will make sure our git client is accessible by jenkins. Click:
Code:
Manage Jenkins ->Configure System -> JDK

Enter the following:
Code:
name= openjdk7
JAVA_HOME= /usr/local/openjdk7

Then click:
Code:
Manage Jenkins ->Configure System -> Git Installation
Enter the following:
Code:
name= git
installation directory= /usr/local/bin/git

If you don't see any red text with errors, then congratulations your Jenkins and Git repository jails have now been successfully setup!! From this point on you may stop reading as you have CI server is connected to a repository. Read the manual on Jenkins and figure out what you want to do next: Java, C++, C, etc, etc. Read online how to build project specific to your language or what you want to do. If you are interested in building Objective-C… keep reading….

What's next:
The next post demonstrates how to setup an objective-c continuous integration server. You might want to read this if you're planning of building projects besides objective-c as well!
 
Objective-C CI Server Setup

Objective-C CI Server Setup
We are going to install GNUStep in our 'jenkins' folder. We will then build a simple program in Apple Xcode 4.xx and build it in our Jenkins CI server.

Let's install 'GNUStep'. The following instructions where taken from this link.

Install GNUstep:
# portupgrade -N --batch gnustep

(Note: The 'batch' works the same way with the portupgrade application. So if you have other things to do, feel free to leave the installation unattended.)

Let's install the Objective-C 2.0 Runtime framework upgrade as this is the framework that you find on the Apple platform:
# portupgrade -N --batch libobjc2

Now we need to set it up with our user.
# vi ~/.profile

Append the file with the following line:
Code:
GNUSTEP_SYSTEM_TOOLS=/usr/local/GNUstep/System/Tools
  export GNUSTEP_SYSTEM_TOOLS

[Apple Macinosh Computer]
Open up Xcode:
Code:
Applications -> Xcode -> Create a new Xcode project -> Application -> Command Line Tool

Then fill in the following:
Product Name: exampleapp
Type: Fondation
Use Automatic Reference Counting: Disabled
Directory: Select the directory where you saved your 'test' directory.

Then click OK in which case the project should initialize. Afterwords rename the file 'main.m' to 'exampleapp.m'.

Check in the code. By going to the console and typing in the following:
# mkdir ~/my-git-repo
# cd ~/my-git-repo
# git add .
# git commit -m 'Objective-C Sample Code'
# git push origin master

Next load up Safari and load up Jenkins:
http://192.168.0.236:8180/jenkins/

Click:
Code:
New Job
Name it: Objective-C Test
And select: Build a free-style software project
Click Ok. Next fill in the following:

Project name:
Code:
Objective-C Test

Description:
Code:
Simple test

Git
Repository URL:
Code:
ssh://git@192.168.0.238//git/base/test.git

Execute shell
Command:
Code:
#!/bin/sh
echo "*********building************"
cd exampleapp
cd exampleapp

echo 'GNUSTEP_MAKEFILES=/usr/local/GNUstep/System/Library/Makefiles

include $(GNUSTEP_MAKEFILES)/common.make

TOOL_NAME = exampleapp
exampleapp_OBJC_FILES = exampleapp.m

include $(GNUSTEP_MAKEFILES)/tool.make' > GNUmakefile

chmod u+x GNUmakefile
echo GNUmakefile

gmake

Then click:
Code:
Apply -> Save

Afterwards click on the 'Schedule a build'. Everything should build successfully from that point! My congratulations to you if it does. If you encounter any problems, don't worry, simple load the console and start debugging. Here is what you need to do for debugging. Go to the main workspace for our job and start tinkering around:
# ezjail-admin console jenkins
# cd /usr/local/jenkins/jobs/Objective-C Test/workspace/exampleapp/exampleapp
# gmake

Then read the error log and try to get it working.
 
Back
Top