Envía ficheros desde Github a EC2 con actions
En la última entrada comentaba lo que Github nos permite hacer con sus actions, podéis leerlo aquí Github Actions
Hoy os traigo un caso de uso práctico y real para estas actions, que días atrás nos surgió cuando queríamos subir a una máquia EC2 un fichero que se genera y almacena en nuestro repositorio en Github, de manera que cuando ese proceso/action de publicación terminase, ante el evento push en el repo se enviase al servidor EC2.
El diagrama más o menos sería esto:
Ante el evento push del fichero (a parte de las acciones de SAST, auditorías, etc) se va a levantar un runner el cual realizará los siguientes pasos:
1.- Instala y configura AWS Cli Para esto emplea los secretos que contienen los valores para Access Key y Secret Access Key de una identidad IAM creada con el fin de poder habilitar y revokar únicamente accesos en el security group de la instancia EC2. En el repositorio github podéis ver un ejemplo de la política necesaria para realizar estas labores.
2.- Obtiene la dirección publica del runner Cada vez que se ejecute la action será en una máquina distinta dentro del pool de máquinas que pueda tener Github.com, y se presentará a internet con una Ip diferente. Con este sencillo comando sacamos la dirección pública de la máquina runner con la que llegaremos a nuestra instancia EC2
wget -qO- https://ipecho.net/plain
El valor obtenido lo pasamos a una variable de entorno para poder utilizarlo más adelante.
3.- Autorizamos el acceso por el puerto ssh tcp/22 en nuestra instancia EC2 Puesto que la copia del fichero desde nuestro repositorio será por scp debemos poder llegar a la máquina desde el runner (que no tiene ip fija pero ya tenemos su ip púiblica en el paso anterior) y para esto tenemos que permitir el tráfico en el security group de esa instancia, que de otra manera está por defecto no permitido.
aws ec2 authorize-security-group-ingress --group-id "${{ secrets.AWS_SGWEB }}" --protocol tcp --port "${{ secrets.AWS_VMPORT }}" --cidr "${{ env.IP_RUNNER }}/32"
4.- Preparamos el runner Para poder lanzar la conexión scp hacia nuestra instancia EC2 debemos tener la clave ssh en el runner, y configurarla donde corresponde, y todo de manera que no quede rastro en los logs de actividad de la action, así que recurrimos a los secrets de las actions para que aparezca en el log como "***". El código es este, creamos los directorios necesarios, permisos en directorios y ficheros y volcamos el pem.
config_ssh=~/.ssh/config
keyfile=~/.ssh/Key.pem
mkdir ~/.ssh && echo -e "ExitOnForwardFailure=yes\\nStrictHostKeyChecking=no" > ${config_ssh} && chmod 600 ${config_ssh}
echo "${AWSKEY}" > ${keyfile} && chmod 600 ${keyfile}
5.- Descargamos el repositorio sobre el runner Con esta action (estamos usando la action de checkout propia de github) descargamos en el runner el contenido de nuestro repo, de manera que tendremos el fichero/os a copiar sobre nuestra instancia EC2. Es como hacer un git clone reponame de toda la vida
uses: actions/checkout@93ea575cb5d8a053eaa0ac8fa3b40d7e05a33cc8
6.- Copiamos los ficheros de local (runner, repo clonado) hacia la instancia EC2 Un simple scp recursivo hacia un directorio de despliegues, con un usuario en el servidor no privilegiado. Todos los datos que no queramos que se pinten en los logs es mejor pasarlos como secretos, ya que así sólo veremos "***" en el log.
scp -r -i ${keyfile} $GITHUB_WORKSPACE/data/* ${AWSVMUSERNAME}@${AWSVMFQDN}:/${AWSDESTINATION} 2>/dev/null
if: always()
que hará que se ejecute siempre sea cual sea el estado de los pasos anteriores de nuestra action.
if: always()
run: aws ec2 revoke-security-group-ingress --group-id "${{ secrets.AWS_SGWEB }}" --protocol tcp --port "${{ secrets.AWS_VMPORT }}" --cidr "${{ env.IP_RUNNER }}/32"
#!/usr/bin/env bash
user=www-data
group=www-data
origin=/Deployments
target=/var/www/<virtualhost_documentroot>/
# Check if another instance of script is running
[[ $(lsof -t $0| wc -l) > 1 ]] && echo "At least one of $0 is running" && exit
while true
do
#Inotify Trigger
inotifywait -r --exclude "(swp|swx)" -e close_write -t 300 ${origin}
cp -pr ${origin}/* ${target}
chown -R ${user}:${group} ${target}
sleep 30
done
Espero os guste y os sea de utilidad.
Todo el código y documentación de esta entrada la encontráis en: https://github.com/antoniohernan/putfiles2ec2