Skip to content

Instantly share code, notes, and snippets.

@itzg
Last active February 15, 2022 21:01
Show Gist options
  • Save itzg/2c0a8701ae87a032efaf to your computer and use it in GitHub Desktop.
Save itzg/2c0a8701ae87a032efaf to your computer and use it in GitHub Desktop.
My own simplified wrapper script to create certs and start CoreOS with QEMU/KVM
#!/bin/bash
set -e
KEY_BITS=2048
CERT_DURATION="-days 1825"
init() {
is_redo=
while [ $# -gt 0 ]; do
case $1 in
-redo)
is_redo=true
shift 1 ;;
*)
echo "Usage: $0 init [ -redo ]"
exit 1 ;;
esac
done
if [ "$is_redo" = true ]; then
rm -f ca-key.pem ca.pem
fi
openssl req -new -x509 $CERT_DURATION -extensions v3_ca -keyout ca-key.pem -out ca.pem
chmod -c 400 ca-key.pem
chmod -c 444 ca.pem
echo "
Created CA cert andkey
"
}
create() {
is_redo=
is_server=
is_client=
if [ $# = 0 ]; then
echo "
NOTE: use -h to see extra options to use when creating certs
"
fi
while [ $# -gt 0 ]; do
case $1 in
-name)
name=$2
shift 2 ;;
-cn)
cn=$2
shift 2 ;;
-alt)
subjectAltName=$2
shift 2 ;;
-client)
is_client=true
shift 1 ;;
-server)
is_server=true
shift 1 ;;
-redo)
is_redo=true
shift 1 ;;
*)
echo "Usage: $0 create -name NAME -cn SUBJECT [-alt SUBJECT_ALT_NAME] [-server] [-client] [-redo]"
exit 1
esac
done
if [ -z "$name" ]; then
read -p "Name: " name
fi
if [ -z "$cn" ]; then
read -p "Subject canonical name: " cn
fi
if [ "$is_redo" = true ]; then
rm -f ${name}-{key,cert}.pem
fi
cnf_file="extfile.$$.cnf"
trap "rm -f $cnf_file" EXIT
touch $cnf_file
if [ ! -f ${name}-cert.pem ]; then
openssl req -subj "/CN=$cn" -new -nodes -keyout ${name}-key.pem -out ${name}.csr
echo >> $cnf_file <<END
subjectKeyIdentifier = hash
authorityKeyIdentifier = keyid
END
if [ -n "$subjectAltName" ]; then
if [[ ! $subjectAltName =~ ((email|URI|DNS|RID|IP|dirName|otherName):) ]]; then
echo "
ERROR: subjectAltName is missing types. See http://bit.ly/subjectAltName
" > /dev/stderr
exit 2
fi
echo "subjectAltName = $subjectAltName" >> $cnf_file
fi
if [ "$is_client" = true -a "$is_server" = true ]; then
echo "extendedKeyUsage = serverAuth,clientAuth" >> $cnf_file
elif [ "$is_client" = true ]; then
echo "extendedKeyUsage = clientAuth" >> $cnf_file
elif [ "$is_server" = true ]; then
echo "extendedKeyUsage = serverAuth" >> $cnf_file
fi
if [ -f $cnf_file ]; then
ext_arg="-extfile $cnf_file"
fi
openssl x509 -req $CERT_DURATION -in ${name}.csr -CA ca.pem -CAkey ca-key.pem \
-CAcreateserial -out ${name}-cert.pem $ext_arg
rm ${name}.csr
chmod -c 444 ${name}-cert.pem
fi
echo "
Created key and cert for $name
"
}
ssh() {
name=
host=
idfile=
user="core"
# Transfer to /tmp by default since $HOME might be read only filesystem
remote_path="/tmp"
if [ $# = 0 ]; then
echo "
NOTE: use -h to see extra options to use when transferring certs
"
fi
while [ $# -gt 0 ]; do
case $1 in
-host)
host=$2
shift 2 ;;
-user)
user=$2
shift 2 ;;
-path)
remote_path=$2
shift 2 ;;
-name)
name=$2
shift 2 ;;
-id)
idfile=$2
shift 2 ;;
*)
echo "Usage: $0 ssh -name NAME -host REMOTE_HOST [ -user $user ] [ -path $remote_path ] [ -id IDFILE ]"
exit 1
esac
done
if [ -z "$name" ]; then
read -p "Name: " name
fi
if [ ! -f ${name}-cert.pem ]; then
create -name $name
fi
if [ -z "$host" ]; then
host=$(openssl x509 -in ${name}-cert.pem -noout -subject -nameopt oneline | awk '{print $4}')
read -p "Remote host [$host]: " ans
host=${ans:-$host}
fi
if [ -n "$idfile" ]; then
id_args="-i $idfile"
fi
if scp $id_args ca.pem ${name}-key.pem ${name}-cert.pem ${user}@${host}:${remote_path} ; then
echo "
Transferred cert and key for $name and CA cert to ${remote_path} on ${user}@${host}
"
fi
}
bundle() {
name=
while [ $# -gt 0 ]; do
case $1 in
-name)
name=$2
shift 2 ;;
*)
echo "Usage: $0 bundle -name NAME"
exit 1
esac
done
if [ -z "$name" ]; then
read -p "Name: " name
fi
if [ ! -f ${name}-cert.pem ]; then
create -name $name
fi
out=/tmp/${name}-certs.tgz
tar zcf $out ca.pem ${name}-key.pem ${name}-cert.pem
chmod -c 400 $out
echo "
Bundled certificate files in $out
"
}
mkdir -p .certs
cd .certs
case $1 in
init|create|ssh|bundle)
cmd="$1"
shift
$cmd $@
;;
*)
echo "Usage: $0 init|create|ssh|bundle"
;;
esac
#cloud-config
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAkzIkq5Ez6lgyUZAtAEd+36SrP0cdP7K87hcl65Gt/c3K0BIqfJIBXcBV9D3EsJIJhaLu0jgXVxO3pPIdIlheY4NAYim9CjF9BDCbuCF5bxZDSXTvedv3+tBiu+qCblXsGzf7LFv8r0yP1rxVnISc0Vi3/r65ywu/gIGVvntRv1/GeztwYQwC4Jti2Lr90ZVnmQ1edTVKkP+CAJX6YyeKMwHNmutjaBSdMmZfG09HapPN8PDX2XSQoRirfUhemuc+hfI+AMGP/sjj5gXLn+JqKTpR9jjfSJ6Fz5RBxg0xL0tUBKSWfFAy5XZXa07gAOb5ODp+fPoqeOt9SL4eLAwiXQ== coreos-access
coreos:
units:
- name: 00-static.network
runtime: true
content: |
[Match]
Name=eth0
[Network]
DNS=$dns
Address=$address_net
Gateway=$gateway
[Route]
Destination=169.254.0.0/16
Scope=link
- name: 01-link-local.network
runtime: true
content: |
[Match]
Name=eth1
[Network]
Address=169.254.169.254
LinkLocalAddressing=no
IPv4LLRoute=true
- name: format-ephemeral.service
command: start
content: |
[Unit]
Description=Formats the ephemeral drive
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/wipefs -f /dev/vdb
ExecStart=/usr/sbin/mkfs.btrfs -f /dev/vdb
- name: var-lib-docker.mount
command: start
content: |
[Unit]
Description=Mount ephemeral to /var/lib/docker
Requires=format-ephemeral.service
After=format-ephemeral.service
Before=docker.service
[Mount]
What=/dev/vdb
Where=/var/lib/docker
Type=btrfs
hostname: $name
#cloud-config
ssh_authorized_keys:
- ssh-rsa AAAAB3NzaC1yc2EAAAABJQAAAQEAkzIkq5Ez6lgyUZAtAEd+36SrP0cdP7K87hcl65Gt/c3K0BIqfJIBXcBV9D3EsJIJhaLu0jgXVxO3pPIdIlheY4NAYim9CjF9BDCbuCF5bxZDSXTvedv3+tBiu+qCblXsGzf7LFv8r0yP1rxVnISc0Vi3/r65ywu/gIGVvntRv1/GeztwYQwC4Jti2Lr90ZVnmQ1edTVKkP+CAJX6YyeKMwHNmutjaBSdMmZfG09HapPN8PDX2XSQoRirfUhemuc+hfI+AMGP/sjj5gXLn+JqKTpR9jjfSJ6Fz5RBxg0xL0tUBKSWfFAy5XZXa07gAOb5ODp+fPoqeOt9SL4eLAwiXQ== coreos-access
coreos:
units:
- name: 00-static.network
runtime: true
content: |
[Match]
Name=eth0
[Network]
DNS=$dns
Address=$address_net
Gateway=$gateway
[Route]
Destination=169.254.0.0/16
Scope=link
- name: format-ephemeral.service
command: start
content: |
[Unit]
Description=Formats the ephemeral drive
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/sbin/wipefs -f /dev/vdb
ExecStart=/usr/sbin/mkfs.btrfs -f /dev/vdb
- name: var-lib-docker.mount
command: start
content: |
[Unit]
Description=Mount ephemeral to /var/lib/docker
Requires=format-ephemeral.service
After=format-ephemeral.service
Before=docker.service
[Mount]
What=/dev/vdb
Where=/var/lib/docker
Type=btrfs
hostname: $name
#!/bin/bash
# Requires:
# - libguestfs-tools
CHANNEL=alpha
RAND_ID=$(openssl rand -hex 4)
MAC_ADDR=$(echo $RAND_ID| awk '{printf "52:54:%02s:%02s:%02s:%02s", substr($0,1,2), substr($0,3,2), substr($0,5,2), substr($0,7,2)}')
CONFIG_FILE="cloud-config.yml"
NAME="coreos-${RAND_ID}"
MEM="1024M"
SMP=2
IMG=coreos_production_qemu_image.img
networking() {
case $1 in
allocate)
if [ ! -f ips.db ]; then
read -p "IP Pool Prefix: " prefix
read -p "IP Pool Start : " startVal
read -p "IP Pool End : " endVal
for (( i = $startVal ; i <= $endVal ; ++i )); do
echo "${prefix}${i}" >> ips.db
done
if [ -n "$SUDO_USER" ]; then
chown ${SUDO_USER}: ips.db
fi
fi
awk -f - ips.db > /tmp/ips.db.$$.selected 2> /tmp/ips.db.$$.remainder <<END
NR == 1 { print > "/dev/stdout" }
NR > 1 { print > "/dev/stderr" }
END
# Preserves ownership
cp /tmp/ips.db.$$.remainder ips.db
ip=$(cat /tmp/ips.db.$$.selected)
rm /tmp/ips.db.$$.*
export ADDRESS=$ip
export ADDRESS_NET=$ip/24
export PUBLIC_IPV4=$ip
export DNS=209.18.47.61
export GATEWAY=192.168.0.1
;;
release)
echo $2 >> ips.db
;;
esac
}
storage() {
case $1 in
allocate)
lvcreate --size $2 --name $RAND_ID vg_qemu
STORAGE_LV=vg_qemu/$RAND_ID
#parted -s /dev/${STORAGE_LV} mklabel gpt
#parted -s /dev/${STORAGE_LV} mkpart primary 0 "100%" 2> /dev/null
;;
release)
lvremove -f $2
;;
esac
}
teardown() {
config_drive=$1
address_to_release=$2
rm -rf $config_drive
networking release $address_to_release
}
while [ $# -gt 0 ]; do
case "$1" in
-storage)
if [ $# -lt 2 ]; then
echo "-storage requires ephemeral storage size"
exit 1
fi
STORAGE_SIZE=$2
shift 2 ;;
-channel)
if [ $# -lt 2 ]; then
echo "-channel requires channel name"
exit 1
fi
CHANNEL=$2
shift 2 ;;
-name)
if [ $# -lt 2 ]; then
echo "-name requires the VM name"
exit 1
fi
NAME=$2
shift 2 ;;
-extra-net)
EXTRA_NET=true
shift 1 ;;
-cloud-config)
if [ $# -lt 2 ]; then
echo "-cloud-config requires the cloud-config YML path"
exit 1
fi
CONFIG_FILE=$2
shift 2 ;;
--)
shift
break ;;
*)
break ;;
esac
done
set -e
if [ ! -f $IMG ]; then
wget http://${CHANNEL}.release.core-os.net/amd64-usr/current/${IMG}.bz2 -O - | bzcat > $IMG
if [ -n "$SUDO_USER" ]; then
chown ${SUDO_USER}: $IMG
fi
fi
if [ ! -f ${CONFIG_FILE} ]; then
echo "The user data file ${CONFIG_FILE} does not exist."
echo "Place there or pass path as first argument."
exit 1
fi
networking allocate
TEARDOWN="$TEARDOWN networking release $ADDRESS ;"
if [ -n "$STORAGE_SIZE" ]; then
storage allocate $STORAGE_SIZE
TEARDOWN="$TEARDOWN storage release $STORAGE_LV ;"
fi
NODE_IMG="${NAME}-${IMG}"
cp $IMG $NODE_IMG
TEARDOWN="$TEARDOWN rm -f $NODE_IMG ;"
CONFIG_DRIVE=$(mktemp -t -d coreos-configdrive.XXXXXXXXXX)
TEARDOWN="$TEARDOWN rm -rf $CONFIG_DRIVE ;"
trap "$TEARDOWN" EXIT
mkdir -p "${CONFIG_DRIVE}/openstack/latest"
sed -f - ${CONFIG_FILE} > ${CONFIG_DRIVE}/openstack/latest/user_data <<END
s#\$address_net#$ADDRESS_NET#;
s/\$public_ipv4/$PUBLIC_IPV4/;
s/\$private_ipv4/$PUBLIC_IPV4/;
s/\$gateway/$GATEWAY/;
s/\$dns/$DNS/;
s/\$name/$NAME/;
END
#cat ${CONFIG_DRIVE}/openstack/latest/user_data
TAP_DEV=tap${RAND_ID}
QEMU=qemu-system-x86_64
IMG_ARGS="-drive if=virtio,file=${NODE_IMG}"
CFG_ARGS="-fsdev local,id=conf,security_model=none,readonly,path=${CONFIG_DRIVE} -device virtio-9p-pci,fsdev=conf,mount_tag=config-2"
MACHINE_ARGS="-machine accel=kvm -cpu host -smp ${SMP}"
DISPLAY_ARGS="-nographic"
NET_ARGS="-device virtio-net,netdev=net0,mac=${MAC_ADDR} -netdev tap,id=net0,ifname=tap${RAND_ID}"
if [ "$EXTRA_NET" = true ]; then
RAND_ID2=$(openssl rand -hex 4)
MAC_ADDR2=$(echo $RAND_ID2| awk '{printf "52:54:%02s:%02s:%02s:%02s", substr($0,1,2), substr($0,3,2), substr($0,5,2), substr($0,7,2)}')
NET_ARGS="$NET_ARGS -device virtio-net,netdev=net1,mac=${MAC_ADDR2} -netdev tap,id=net1,ifname=tap${RAND_ID2}"
fi
if [ -n "$STORAGE_LV" ]; then
STORAGE_ARGS="-drive file=/dev/${STORAGE_LV},if=virtio,index=1"
fi
echo "
Launching $NAME with ID $RAND_ID...
"
set -x
$QEMU -name "$NAME" -m $MEM $IMG_ARGS $CFG_ARGS $NET_ARGS $STORAGE_ARGS $MACHINE_ARGS $DISPLAY_ARGS
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment