#!/bin/bash -ex

# Build a new Centos8 install on EBS volume in a chroot
# Run from RHEL8 or CentOS8 instance - eg: ami-0c322300a1dd5dc79 in us-east-1 (RHEL 8 official image)
# Script expects a second EBS volume, I add them as /dev/sdf in the console
# When the script completes, turn the second EBS volume into your new AMI through the console.

# Adjust the section below to match the device names you're using. Defaults are for an m5.large

# m5 series requires the updated device names
DEVICE=/dev/nvme1n1
ROOTFS=/rootfs
TMPDIR=/tmp

# I build in the ENA drivers from amazon - https://github.com/amzn/amzn-drivers
ENA_VER=2.1.2
ENA_COMMIT=c8f9f32

# Need a package for setting up disk
yum install -y gdisk

# Need sgdisk to setup the partitions properly
sgdisk -og ${DEVICE}
sgdisk -n 128:2048:4095 -c 128:"BIOS Boot Partition" -t 128:ef02 ${DEVICE}
ENDSECTOR=$(sgdisk -E ${DEVICE})
sgdisk -n 1:4096:${ENDSECTOR} -c 1:"Linux" -t 1:8300 ${DEVICE}

mkfs.xfs -L root ${DEVICE}p1
mkdir -p ${ROOTFS}
mount ${DEVICE}p1 ${ROOTFS}
mkdir ${ROOTFS}/{dev,proc}

# Setup bindmounts
mount -t proc none ${ROOTFS}/proc
mount --bind /dev ${ROOTFS}/dev

### Basic CentOS Install
# Grab close mirror
CENT_MIRROR=$(curl -s "http://mirrorlist.centos.org/?release=8&arch=x86_64&repo=baseos" | sort -R | head -1)
# Grab current release
CENT_REL=$(echo ${CENT_MIRROR} | rev | cut -f4 -d'/' | rev | sed 's/\./-/')
rpm --root=${ROOTFS} --initdb
# This is hardcoded for the moment.
rpm --root=${ROOTFS} -ivh \
  ${CENT_MIRROR}Packages/centos-release-8.0-0.1905.0.9.el8.x86_64.rpm


# Install necessary packages
yum --installroot=${ROOTFS} --nogpgcheck -y groupinstall "Minimal Install"
yum --installroot=${ROOTFS} --nogpgcheck -y install openssh-server grub2 acpid tuned kernel drpm epel-release

# Install helpful packages for everyone
yum --installroot=${ROOTFS} --nogpgcheck -y install \
  cloud-init \
  cloud-utils-growpart \
  dracut-config-generic \
  dracut-norescue \
  dkms \
  dstat \
  ethtool \
  gdisk \
  htop \
  jq \
  lsof \
  make \
  @python36 \
  net-tools \
  nmap-ncat \
  chrony \
  openssl \
  psmisc \
  strace \
  sysstat \
  tmux \
  unzip \
  wget \
  yum-utils

# Clean out old Firmware and packages I don't use
yum --installroot=${ROOTFS} -C -y remove \
  aic94xx-firmware \
  alsa-firmware \
  alsa-lib \
  alsa-tools-firmware \
  biosdevname \
  dracut-config-rescue \
  iprutils \
  ivtv-firmware \
  iwl100-firmware \
  iwl1000-firmware \
  iwl105-firmware \
  iwl135-firmware \
  iwl2000-firmware \
  iwl2030-firmware \
  iwl3160-firmware \
  iwl3945-firmware \
  iwl4965-firmware \
  iwl5000-firmware \
  iwl5150-firmware \
  iwl6000-firmware \
  iwl6000g2a-firmware \
  iwl6000g2b-firmware \
  iwl6050-firmware \
  iwl7260-firmware \
  libertas-sd8686-firmware \
  libertas-sd8787-firmware \
  libertas-usb8388-firmware \
  linux-firmware \
  plymouth


# Create homedir for root
cp -a /etc/skel/.bash* ${ROOTFS}/root

# RHEL7 AMI KS thievery
sed -i '/^#NAutoVTs=.*/ a\
NAutoVTs=0' ${ROOTFS}/etc/systemd/logind.conf

echo xen-netfront >> ${ROOTFS}/etc/modules-load.d/xen-netfront.conf

## Networking setup
cat > ${ROOTFS}/etc/hosts << END
127.0.0.1   localhost localhost.localdomain localhost4 localhost4.localdomain4
::1         localhost localhost.localdomain localhost6 localhost6.localdomain6
END
touch ${ROOTFS}/etc/resolv.conf
mkdir -p ${ROOTFS}/etc/sysconfig/network-scripts
cat > ${ROOTFS}/etc/sysconfig/network << END
NETWORKING=yes
NOZEROCONF=yes
END
cat > ${ROOTFS}/etc/sysconfig/network-scripts/ifcfg-eth0  << END
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=dhcp
IPV6INIT=yes
DHCPV6C=yes
DHCPV6C_OPTIONS=-nw
END

# Add timestamps to history commands
cat > ${ROOTFS}/etc/profile.d/history.sh << END
export HISTTIMEFORMAT="%d/%m/%y %T "
END

cp /usr/share/zoneinfo/UTC ${ROOTFS}/etc/localtime

echo 'ZONE="UTC"' > ${ROOTFS}/etc/sysconfig/clock

# fstab
# Need the UUID of our device
ROOTUUID=$(blkid -o export ${DEVICE}p1 -s UUID | grep ^UUID)
cat > ${ROOTFS}/etc/fstab << END
${ROOTUUID}         /         xfs       defaults,relatime               1 1
tmpfs               /dev/shm  tmpfs     defaults,nosuid,nodev,noexec    0 0
devpts              /dev/pts  devpts    gid=5,mode=620                  0 0
sysfs               /sys      sysfs     defaults                        0 0
proc                /proc     proc      defaults                        0 0
END

#grub config taken from /etc/sysconfig/grub on RHEL7 AMI
cat > ${ROOTFS}/etc/default/grub << END
GRUB_TIMEOUT=0
GRUB_DEFAULT=saved
GRUB_DISABLE_SUBMENU=true
GRUB_TERMINAL_OUTPUT="console"
GRUB_CMDLINE_LINUX="crashkernel=auto console=ttyS0,115200n8 console=tty0 net.ifnames=0 tsc=reliable clocksource=tsc"
GRUB_DISABLE_RECOVERY="true"
END
echo 'RUN_FIRSTBOOT=NO' > ${ROOTFS}/etc/sysconfig/firstboot

# Make sure we have the right resources mounted
BINDMNTS="sys etc/hosts etc/resolv.conf"
for d in $BINDMNTS ; do
  mount --bind /${d} ${ROOTFS}/${d}
done

# Install grub2
chroot ${ROOTFS} grub2-mkconfig -o /boot/grub2/grub.cfg
chroot ${ROOTFS} grub2-install $DEVICE
chroot ${ROOTFS} chmod og-rwx /boot/grub2/grub.cfg
# Install cloud-init from epel
chroot ${ROOTFS} systemctl mask tmp.mount

# Configure cloud-init
cat > ${ROOTFS}/etc/cloud/cloud.cfg << END
users:
 - default

disable_root: 1
ssh_pwauth:   0

mount_default_fields: [~, ~, 'auto', 'defaults,nofail', '0', '2']
resize_rootfs_tmp: /dev
ssh_svcname: sshd
ssh_deletekeys:  0
ssh_genkeytypes:  [ 'rsa', 'ecdsa', 'ed25519' ]
syslog_fix_perms: ~
preserve_hostname: true


cloud_init_modules:
 - migrator
 - bootcmd
 - write-files
 - growpart
 - resizefs
 - set_hostname
 - update_hostname
 - update_etc_hosts
 - rsyslog
 - users-groups
 - ssh

cloud_config_modules:
 - mounts
 - locale
 - set-passwords
 - yum-add-repo
 - package-update-upgrade-install
 - timezone
 - disable-ec2-metadata
 - runcmd

cloud_final_modules:
 - rightscale_userdata
 - scripts-per-once
 - scripts-per-boot
 - scripts-per-instance
 - scripts-user
 - ssh-authkey-fingerprints
 - keys-to-console
 - phone-home
 - final-message

system_info:
  default_user:
    name: centos
    lock_passwd: true
    gecos: Cloud User
    groups: [wheel, adm, systemd-journal]
    sudo: ["ALL=(ALL) NOPASSWD:ALL"]
    shell: /bin/bash
  distro: rhel
  paths:
    cloud_dir: /var/lib/cloud
    templates_dir: /etc/cloud/templates
  ssh_svcname: sshd

mounts:
 - [ ephemeral0, /media/ephemeral0 ]
 - [ ephemeral1, /media/ephemeral1 ]
 - [ swap, none, swap, sw, "0", "0" ]

datasource_list: [ Ec2, None ]

# vim:syntax=yaml
END

# Setup cloud-init logging
cat > ${ROOTFS}/etc/cloud/cloud.cfg.d/05_logging.cfg << END
## This yaml formated config file handles setting
## logger information.  The values that are necessary to be set
## are seen at the bottom.  The top '_log' are only used to remove
## redundency in a syslog and fallback-to-file case.
##
## The 'log_cfgs' entry defines a list of logger configs
## Each entry in the list is tried, and the first one that
## works is used.  If a log_cfg list entry is an array, it will
## be joined with '\n'.
_log:
 - &log_base |
   [loggers]
   keys=root,cloudinit

   [handlers]
   keys=consoleHandler,cloudLogHandler

   [formatters]
   keys=simpleFormatter,arg0Formatter

   [logger_root]
   level=DEBUG
   handlers=consoleHandler,cloudLogHandler

   [logger_cloudinit]
   level=DEBUG
   qualname=cloudinit
   handlers=
   propagate=1

   [handler_consoleHandler]
   class=StreamHandler
   level=WARNING
   formatter=arg0Formatter
   args=(sys.stderr,)

   [formatter_arg0Formatter]
   format=%(asctime)s - %(filename)s[%(levelname)s]: %(message)s

   [formatter_simpleFormatter]
   format=[CLOUDINIT] %(filename)s[%(levelname)s]: %(message)s
 - &log_file |
   [handler_cloudLogHandler]
   class=FileHandler
   level=DEBUG
   formatter=arg0Formatter
   args=('/var/log/cloud-init.log',)
 - &log_syslog |
   [handler_cloudLogHandler]
   class=handlers.SysLogHandler
   level=DEBUG
   formatter=simpleFormatter
   args=("/dev/log", handlers.SysLogHandler.LOG_USER)

log_cfgs:
# These will be joined into a string that defines the configuration
 - [ *log_base, *log_syslog ]
# These will be joined into a string that defines the configuration
 - [ *log_base, *log_file ]
# A file path can also be used
# - /etc/log.conf

# this tells cloud-init to redirect its stdout and stderr to
# 'tee -a /var/log/cloud-init-output.log' so the user can see output
# there without needing to look on the console.
output: {all: '| tee -a /var/log/cloud-init-output.log'}
END


# Setup services
chroot ${ROOTFS} systemctl enable sshd.service
chroot ${ROOTFS} systemctl enable cloud-init.service
chroot ${ROOTFS} systemctl enable chronyd.service
chroot ${ROOTFS} systemctl disable firewalld.service

# From RHEL
echo 'install_items+=" sgdisk "' > ${ROOTFS}/etc/dracut.conf.d/sgdisk.conf

# Disable dracut rescue kernels if dracut-config-rescue is reinstalled
echo 'dracut_rescue_image="no"' > ${ROOTFS}/etc/dracut.conf.d/02-rescue.conf

# Chrony Config
cat > ${ROOTFS}/etc/chrony.conf << END
# Amazon Local Time Service
server 169.254.169.123 prefer iburst

# Use public servers from the pool.ntp.org project.
# Please consider joining the pool (http://www.pool.ntp.org/join.html).
server 0.centos.pool.ntp.org iburst
server 1.centos.pool.ntp.org iburst
server 2.centos.pool.ntp.org iburst
server 3.centos.pool.ntp.org iburst

# Record the rate at which the system clock gains/losses time.
driftfile /var/lib/chrony/drift

# Allow the system clock to be stepped in the first three updates
# if its offset is larger than 1 second.
makestep 1.0 3

# Enable kernel synchronization of the real-time clock (RTC).
rtcsync

# Specify directory for log files.
logdir /var/log/chrony
END

cat > ${ROOTFS}/etc/sysconfig/chronyd << END
# Command-line options for chronyd
OPTIONS=" -u chrony"
END

cat > ${ROOTFS}/etc/issue << END
Welcome to CentOS, 3rd Rock from the Sun
END

# SSH Config
cat > ${ROOTFS}/etc/ssh/sshd_config << END
# Minimized sshd_config
Protocol 2
LogLevel INFO
MaxAuthTries 4
IgnoreRhosts yes
HostbasedAuthentication no
PermitRootLogin no
PermitEmptyPasswords no
PermitUserEnvironment no
Ciphers aes256-ctr,aes192-ctr,aes128-ctr
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com
ClientAliveInterval 300
ClientAliveCountMax 0
LoginGraceTime 60
Banner /etc/issue
HostKey /etc/ssh/ssh_host_rsa_key
HostKey /etc/ssh/ssh_host_ecdsa_key
HostKey /etc/ssh/ssh_host_ed25519_key
SyslogFacility AUTHPRIV
AuthorizedKeysFile .ssh/authorized_keys
PasswordAuthentication no
ChallengeResponseAuthentication no
GSSAPIAuthentication yes
GSSAPICleanupCredentials no
UsePAM yes
X11Forwarding no
UsePrivilegeSeparation sandbox  # Default for new installations.
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL LANGUAGE
AcceptEnv XMODIFIERS
Subsystem sftp /usr/libexec/openssh/sftp-server
END

cat > ${ROOTFS}/etc/sysctl.d/10-linux-network.conf << END
## sysctl settings for controller
net.core.somaxconn=1024
net.ipv4.ip_local_port_range=2048 65535
net.core.rmem_default = 425984
net.core.wmem_default = 425984
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_max_syn_backlog = 16384
net.ipv4.tcp_wmem = 4096 12582912 16777216
net.ipv4.tcp_rmem = 4096 12582912 16777216
net.core.netdev_max_backlog = 1024
net.core.rps_sock_flow_entries = 32768
net.ipv4.tcp_fin_timeout = 30
END
cat > ${ROOTFS}/etc/sysctl.d/11-CIS-network.conf << END
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.conf.all.log_martians = 1
net.ipv4.conf.default.log_martians = 1
net.ipv6.conf.all.accept_ra = 0
net.ipv6.conf.default.accept_ra = 0
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
END

# CIS Block unused filesystems
cat > ${ROOTFS}/etc/modprobe.d/CIS.conf << END
install cramfs /bin/true
install freevxfs /bin/true
install jffs2 /bin/true
install hfs /bin/true
install hfsplus /bin/true
install squashfs /bin/true
install udf /bin/true
install vfat /bin/true
END

# CIS Update create mask for rsyslogd
cat > ${ROOTFS}/etc/rsyslog.d/cis.conf << END
$umask 0000
$FileCreateMmode 0640
END

# CIS pam settings
cat > ${ROOTFS}/etc/pam.d/su << END
#%PAM-1.0
auth            sufficient      pam_rootok.so
# Uncomment the following line to implicitly trust users in the "wheel" group.
#auth           sufficient      pam_wheel.so trust use_uid
# Uncomment the following line to require a user to be in the "wheel" group.
auth            required        pam_wheel.so use_uid
auth            substack        system-auth
auth            include         postlogin
account         sufficient      pam_succeed_if.so uid = 0 use_uid quiet
account         include         system-auth
password        include         system-auth
session         include         system-auth
session         include         postlogin
session         optional        pam_xauth.so
END

# Tighten up some permissions
chroot ${ROOTFS} chmod og-rwx /etc/crontab
chroot ${ROOTFS} chmod og-rwx /etc/cron.hourly
chroot ${ROOTFS} chmod og-rwx /etc/cron.daily
chroot ${ROOTFS} chmod og-rwx /etc/cron.weekly
chroot ${ROOTFS} chmod og-rwx /etc/cron.monthly
chroot ${ROOTFS} chmod og-rwx /etc/cron.d

# Add additional AWS drivers
# Need to build this thing on its own and install
KVER=$(chroot ${ROOTFS} rpm -q kernel | sed -e 's/^kernel-//')
yum --installroot=${ROOTFS} --nogpgcheck -y install kernel-devel-${KVER}
# Enable Amazon ENA
# Create an archive file locally from git first
yum -y install git
mkdir -p ${TMPDIR}/ena
git clone https://github.com/amzn/amzn-drivers.git ${TMPDIR}/ena
cd ${TMPDIR}/ena
git archive --prefix ena-${ENA_VER}/ ${ENA_COMMIT} | tar xC ${ROOTFS}/usr/src
cat > ${ROOTFS}/usr/src/ena-${ENA_VER}/dkms.conf << END
PACKAGE_NAME="ena"
PACKAGE_VERSION="${ENA_VER}"
CLEAN="make -C kernel/linux/ena clean"
MAKE="make -C kernel/linux/ena/ BUILD_KERNEL=\${kernelver}"
BUILT_MODULE_NAME[0]="ena"
BUILT_MODULE_LOCATION="kernel/linux/ena"
DEST_MODULE_LOCATION[0]="/updates"
DEST_MODULE_NAME[0]="ena"
AUTOINSTALL="yes"
END
chroot ${ROOTFS} dkms add -m ena -v ${ENA_VER}
chroot ${ROOTFS} dkms build -m ena -v ${ENA_VER} -k ${KVER}
chroot ${ROOTFS} dkms install -m ena -v ${ENA_VER} -k ${KVER}
yum --installroot=${ROOTFS} clean all
chroot ${ROOTFS} depmod ${KVER}

# Fix all the selinux file contexts
chroot ${ROOTFS} setfiles -v /etc/selinux/targeted/contexts/files/file_contexts /


# We're done!
for d in $BINDMNTS ; do
  umount ${ROOTFS}/${d}
done
umount ${ROOTFS}/dev
umount ${ROOTFS}/proc
# Hush, I'm old.
sync;sync;sync
umount ${ROOTFS}