Setting up a new AMI image for fedora 11
Since its a bit of a pain to create the fedora 11 ami image for use in EC2, I thought I would share a script.
First part of the script sets up the variables used through script.
#!/bin/sh | |
EC2_MODULE="2.6.21.7-2.fc8xen-ec2-v1.0" ## set the ec2 module | |
SCRIPT_DIR="/var/awsscripts" ## scripts to setup the amazon stuff, will be copied to the image | |
IMAGE_DIR="/var/awsimage" ## directory to store the img file and ami image in | |
RELEASEVER=11 | |
ARCH=i386 # x86_64 | |
IMAGE_SIZE=2560 ## set the image size | |
## amazon stuff | |
EC2_HOME=~/.ec2 | |
AWS_USER=<Your account number > | |
AWS_ACCESS_KEY_ID=<Your access key> | |
AWS_SECRET_ACCESS_KEY=<Your secret access key> | |
EC2_PRIVATE_KEY=$EC2_HOME/pk-<Your private key name>.pem | |
EC2_CERT=$EC2_HOME/cert-<Your public key name>.pem | |
BUCKET_NAME=< s3 bucket name to store the image > | |
export EC2_HOME AWS_USER AWS_ACCESS_KEY_ID AWS_SECRET_ACCESS_KEY EC2_PRIVATE_KEY EC2_CERT BUCKET_NAME | |
IMAGE_NAME=$1 ## passed in to the script | |
BASE_NAME="ami-fedora$RELEASEVER-$ARCH_$IMAGE_NAME" | |
IMG_FILE="$BASE_NAME.img" | |
echo "Image file is: $IMG_FILE" | |
echo "Base Name: $BASE_NAME" | |
if [ ! -d "$IMAGE_DIR" ]; then | |
mkdir $IMAGE_DIR | |
fi |
In the above, you need to setup your keys and access keys for amazon. This is used later in the script to publish the image to the s3 bucket. It also assumes you have the ami tools downloaded and installed. This also assumes you are using fedora 11 to build the image (I use parallels with fedora 11 on it).
Now we need to make an img and mount to it and push files to it.
## Setup image | |
dd if=/dev/zero of="$IMAGE_DIR/$IMG_FILE" bs=1M count="$IMAGE_SIZE" | |
/sbin/mke2fs -F -j "$IMAGE_DIR/$IMG_FILE" | |
mkdir "/mnt/$BASE_NAME" | |
mount -o loop "$IMAGE_DIR/$IMG_FILE" "/mnt/$BASE_NAME" | |
mkdir "/mnt/$BASE_NAME/proc"; chmod 555 "/mnt/$BASE_NAME/proc" | |
mkdir "/mnt/$BASE_NAME/etc"; chmod 755 "/mnt/$BASE_NAME/etc" | |
mkdir "/mnt/$BASE_NAME/root"; chmod 750 "/mnt/$BASE_NAME/root" | |
mkdir -p "/mnt/$BASE_NAME/var/lib/rpm"; chmod 755 "/mnt/$BASE_NAME/var/lib/rpm" | |
mkdir -p "/mnt/$BASE_NAME/var/cache" | |
mkdir -p "/mnt/$BASE_NAME/var/log"; chmod 755 "/mnt/$BASE_NAME/var/log" | |
mkdir -p "/mnt/$BASE_NAME/var/lock/rpm"; chmod 755 "/mnt/$BASE_NAME/var/lock/rpm" | |
mkdir -p "/mnt/$BASE_NAME/var/tmp" ; chmod ugo+rwxt "/mnt/$BASE_NAME/var/tmp" | |
touch "/mnt/$BASE_NAME/etc/mtab"; chmod 644 "/mnt/$BASE_NAME/etc/mtab" | |
for i in console null zero urandom; do /sbin/MAKEDEV -d "/mnt/$BASE_NAME/dev" -x $i; done | |
### --------------------------------------------------------- | |
## setup filesystem | |
FS_STAB="/mnt/$BASE_NAME/etc/fstab" | |
echo "/dev/sda1 / ext3 defaults 1 1 " >> $FS_STAB | |
echo "none /dev/pts devpts gid=5,mode=620 0 0 " >> $FS_STAB | |
echo "none /dev/shm tmpfs defaults 0 0 " >> $FS_STAB | |
echo "none /proc proc defaults 0 0 " >> $FS_STAB | |
echo "none /sys sysfs defaults 0 0 " >> $FS_STAB | |
echo "/dev/sda2 /mnt ext3 defaults 1 2 " >> $FS_STAB | |
echo "/dev/sda3 swap swap defaults 0 0 " >> $FS_STAB | |
chmod 644 $FS_STAB |
The above also sets up the fstab file and the directories we will use to add things to.
Next we setup the yum repo files and the start installing the fedora 11 package.
## setup repo | |
rm -f ./yum.all.repo | |
DIR="/etc/yum.repos.d" | |
SUFFIX="repo" | |
for i in "$DIR"/*.$SUFFIX | |
do | |
cat ${i} >> ./yum.all.repo | |
done | |
sedscript="s/\$basearch/$ARCH/g" | |
sed -i $sedscript ./yum.all.repo | |
sedscript="s/\$releasever/$RELEASEVER/g" | |
sed -i $sedscript ./yum.all.repo | |
## setup fedora | |
mount -t proc none "/mnt/$BASE_NAME/proc" | |
yum -c yum.all.repo --installroot="/mnt/$BASE_NAME" -y groupinstall Base | |
RPMS="tar wget gzip bzip2 curl ruby *openssh* kernel-xen kernel-xen-devel" | |
yum -c yum.all.repo --installroot="/mnt/$BASE_NAME" -y install $RPMS |
The above also installs some base rpms. Now for the amazon parts such as the module and tools.
## kernel-xen | |
if [ ! -f "ec2-modules-$EC2_MODULE.tgz" ]; then | |
wget "http://ec2-downloads.s3.amazonaws.com/ec2-modules-$EC2_MODULE.tgz" | |
fi | |
tar zxvf "ec2-modules-$EC2_MODULE.tgz" -C "/mnt/$BASE_NAME" | |
/usr/sbin/chroot "/mnt/$BASE_NAME" cp -R /lib/modules/$EC2_MODULE /lib/modules/2.6.21.7-2.fc8xen | |
/usr/sbin/chroot "/mnt/$BASE_NAME" /sbin/depmod -a | |
wget "http://s3.amazonaws.com/ec2-downloads/ec2-ami-tools.noarch.rpm" | |
cp ec2-ami-tools.noarch.rpm "/mnt/$BASE_NAME/var/awsscripts/" | |
/usr/sbin/chroot "/mnt/$BASE_NAME" rpm -ivh --nosignature /var/awsscripts/ec2-ami-tools.noarch.rpm |
Now we start modifying some of the base system files to work in EC2.
## update ssh file | |
echo " GSSAPIAuthentication yes" >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
echo " Protocol 2" >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
echo " X11Forwarding yes " >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
echo " AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES " >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
echo " AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT " >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
echo " AcceptEnv LC_IDENTIFICATION LC_ALL " >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
echo " PermitRootLogin without-password" >> "/mnt/$BASE_NAME/etc/ssh/sshd_config" | |
## replace networking file | |
echo "NETWORKING=yes" > "/mnt/$BASE_NAME/etc/sysconfig/network" | |
## replace ifcfg-eth0 file | |
"DEVICE=eth0" > "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" | |
"BOOTPROTO=dhcp" >> "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" | |
"ONBOOT=yes" >> "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" | |
"TYPE=Ethernet" >> "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" | |
"USERCTL=yes" >> "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" | |
"PEERDNS=yes" >> "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" | |
"IPV6INIT=no" >> "/mnt/$BASE_NAME/etc/sysconfig/network-scripts/ifcfg-eth0" |
Now for general customization and including of amazon scripts, which are described below.
## Update more packages | |
yum -c yum.all.repo --installroot="/mnt/$BASE_NAME" -y erase cups ## erase printing, can do more customization here as well | |
yum -c yum.all.repo --installroot="/mnt/$BASE_NAME" -y install fuse-devel fuse fuse-encfs fuse-libs fuse-sshfs java-1.6.0-openjdk xen-runtime ruby | |
## update services | |
/usr/sbin/chroot "/mnt/$BASE_NAME" /sbin/chkconfig --level 1345 sshd --add | |
/usr/sbin/chroot "/mnt/$BASE_NAME" /sbin/chkconfig --level 1345 sshd on | |
/usr/sbin/chroot "/mnt/$BASE_NAME" /sbin/chkconfig --level 345 ip6tables off | |
## add scripts to startup | |
cp -R /$SCRIPT_DIR/* "/mnt/$BASE_NAME/$SCRIPT_DIR" | |
chmod +x /mnt/$BASE_NAME/$SCRIPT_DIR/* | |
echo "source /$SCRIPT_DIR/awssetup" >> /mnt/$BASE_NAME/etc/rc.local | |
## wrap everything up | |
yum -c yum.all.repo --installroot="/mnt/$BASE_NAME" clean all | |
sync | |
umount "/mnt/$BASE_NAME/proc" | |
umount "/mnt/$BASE_NAME" |
You can add your own little flare to the above section to talk to chef, puppet, or equivalent to install the rest of the server, its up to you.
Now ship it off to amazon, you’ll have to register it once you are done.
ec2-bundle-image --image "$IMAGE_DIR/$IMG_FILE" --prefix "$BASE_NAME" --cert "$EC2_CERT" --privatekey "$EC2_PRIVATE_KEY" --user "$AWS_USER" --destination "$IMAGE_DIR" --arch "$ARCH" | |
ec2-upload-bundle --manifest "$IMAGE_DIR/$BASE_NAME.manifest.xml" --bucket "$BUCKET_NAME" --access-key "$AWS_ACCESS_KEY_ID" --secret-key "$AWS_SECRET_ACCESS_KEY" |
Below is the amazon setup script (awssetup) that is referenced above and will be executed on startup, taken from an amazon fedora 8 image, mostly verbatim.
#!/bin/sh | |
# $Rev: 45452 $ | |
# This script will be executed *after* all the other init scripts. | |
# You can put your own initialization stuff in here if you don't | |
# want to do the full Sys V style init stuff. | |
echo "-----VERSION : ""\$Rev: 45452 $""-----" | |
# Stuff we want to do once at launch and never again: | |
if [ -f "/root/firstrun" ]; then | |
# Update AMI tools to the latest version: | |
# [ -x "/usr/local/sbin/update-tools.sh" ] && /usr/local/sbin/update-tools.sh | |
# Try to find kernel modules matching current kernel: | |
# [ -x "/usr/local/sbin/update-modules.sh" ] && /usr/local/sbin/update-modules.sh | |
# Some kernels use xvc0 as their serial console device: | |
if [ -c /dev/xvc0 ]; then | |
if ! grep -q 'co:2345:respawn:/sbin/agetty xvc0 9600' /etc/inittab; then | |
echo 'co:2345:respawn:/sbin/agetty xvc0 9600 vt100' >> /etc/inittab | |
echo 'xvc0' >> /etc/securetty | |
kill -1 1 | |
fi | |
fi | |
# Ensure devpts is mounted to prevent ssh hang-ups | |
mount | grep devpts > /dev/null 2>&1 | |
if [ $? -ne 0 ] ; then | |
devpts="none /dev/pts devpts gid=5,mode=620 0 0" | |
( grep -v "\#" /etc/fstab | grep devpts > /dev/null 2>&1 ) || echo $devpts >> /etc/fstab | |
mount -a >/dev/null 2>&1 | |
fi | |
# Randomise the root password as the last operation | |
# We ideally have some more entropy at this stage | |
echo "-----RANDOMISING ROOT PASSWORD-----" |logger -s -t "ec2" | |
dd if=/dev/urandom count=128 2>/dev/null|md5sum|passwd --stdin root >/dev/null 2>&1 | |
rm -f /root/firstrun | |
# Regenerate the host keys at this stage | |
# Having more entropy to work with | |
echo "-----TRIGGERING HOST KEYS REGENERATION-----"|logger -s -t "ec2" | |
echo "Removing existing keys"|logger -s -t "ec2" | |
rm -f /etc/ssh/ssh_host_key.pub /etc/ssh/ssh_host_rsa_key.pub /etc/ssh/ssh_host_dsa_key.pub \ | |
/etc/ssh/ssh_host_key /etc/ssh/ssh_host_rsa_key /etc/ssh/ssh_host_dsa_key | |
echo "Bouncing sshd to force regeneration"|logger -s -t "ec2" | |
/sbin/service sshd restart | |
echo "Setting sshd to start as a service"|logger -s -t "ec2" | |
/sbin/chkconfig --level 2345 sshd on | |
fi | |
touch /var/lock/subsys/local | |
# Get your chosen keypair credentials | |
/var/awsscripts/get-credentials.sh | |
givenhostname=`curl -s http://169.254.169.254/latest/meta-data/local-hostname` | |
echo "HOSTNAME=$givenhostname" >> "/etc/sysconfig/network" | |
# =*Output ssh host keys to console*= | |
[ -f /etc/ssh/ssh_host_key ] || (ssh-keygen -f /etc/ssh/ssh_host_key -t rsa1 -C 'host' -N '' | logger -s -t "ec2") | |
[ -f /etc/ssh/ssh_host_rsa_key ] || (ssh-keygen -f /etc/ssh/ssh_host_rsa_key -t rsa -C 'host' -N '' | logger -s -t "ec2") | |
[ -f /etc/ssh/ssh_host_dsa_key ] || (ssh-keygen -f /etc/ssh/ssh_host_dsa_key -t dsa -C 'host' -N '' | logger -s -t "ec2") | |
echo "-----BEGIN SSH HOST KEY FINGERPRINTS-----" |logger -s -t "ec2" | |
ssh-keygen -l -f /etc/ssh/ssh_host_key.pub |logger -s -t "ec2" | |
ssh-keygen -l -f /etc/ssh/ssh_host_rsa_key.pub |logger -s -t "ec2" | |
ssh-keygen -l -f /etc/ssh/ssh_host_dsa_key.pub |logger -s -t "ec2" | |
echo "-----END SSH HOST KEY FINGERPRINTS-----" |logger -s -t "ec2" |
Here is the script for get-credentials.sh that is called in the above script, which is also found in a base amazon image.
#!/bin/bash | |
# Version : $Rev: 45328 $ | |
# root's public keys | |
PUB_KEY_URI=http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key | |
# PUB_KEY_URI=http://169.254.169.254/1.0/meta-data/public-keys/0/openssh-key | |
PUB_KEY_FROM_HTTP=/tmp/openssh_id.pub | |
PUB_KEY_FROM_EPHEMERAL=/mnt/openssh_id.pub | |
ROOT_AUTHORIZED_KEYS=/root/.ssh/authorized_keys | |
# We need somewhere to put the keys. | |
if [ ! -d /root/.ssh ] ; then | |
mkdir -p /root/.ssh | |
chmod 700 /root/.ssh | |
fi | |
# Fetch credentials... | |
echo "-----CREDENTIALS RETRIEVAL-----"|logger -s -t "ec2" | |
# First try http | |
echo "Attempting to retrieve public key from [$PUB_KEY_URI]"|logger -t "ec2" -s | |
curl --retry 3 --retry-delay 5 --silent --fail -o $PUB_KEY_FROM_HTTP $PUB_KEY_URI | |
if [ $? -eq 0 -a -e $PUB_KEY_FROM_HTTP ] ; then | |
if ! grep -q -f $PUB_KEY_FROM_HTTP $ROOT_AUTHORIZED_KEYS | |
then | |
cat $PUB_KEY_FROM_HTTP >> $ROOT_AUTHORIZED_KEYS | |
echo "New key added to authrozied keys file from parameters"|logger -t "ec2" -s | |
else | |
echo "Already have your key"|logger -t "ec2" -s | |
fi | |
rm -f $PUB_KEY_FROM_HTTP | |
elif [ -e $PUB_KEY_FROM_EPHEMERAL ] ; then | |
echo "Meta-data fetch failed, trying from legacy ephemeral store location [$PUB_KEY_FROM_EPHEMERAL]"|logger -t "ec2" -s | |
# Try back to ephemeral store if http failed. | |
# NOTE: This usage is deprecated and will be removed in the future | |
if ! grep -q -f $PUB_KEY_FROM_EPHEMERAL $ROOT_AUTHORIZED_KEYS | |
then | |
cat $PUB_KEY_FROM_EPHEMERAL >> $ROOT_AUTHORIZED_KEYS | |
echo "New key added to authrozied keys file from ephemeral store"|logger -t "ec2" -s | |
fi | |
chmod 600 $PUB_KEY_FROM_EPHEMERAL | |
fi | |
if [ ! -f $ROOT_AUTHORIZED_KEYS ] | |
then | |
echo "*!*!*! FATAL ERROR *!*!*! No able to find authorized_keys file [$ROOT_AUTHORIZED_KEYS]"|logger -t "ec2" -s | |
else | |
echo "Setting permissions on $ROOT_AUTHORIZED_KEYS to 0600"|logger -t "ec2" -s | |
chmod 600 $ROOT_AUTHORIZED_KEYS | |
fi |
Works with the following:
- Kernel - aki-a71cf9ce
- Ramdisk - ari-a51cf9cc