-
-
Save webaxones/54a9aee13bd9152e900ef30a0fcef3ed to your computer and use it in GitHub Desktop.
# This GitHub workflow will build a WordPress Bedrock site and deploy it to a shared server (french one: O2Switch, but URLs can be adapted) using SSH | |
# Actions secrets are used to store sensitive information: | |
# - SSH_PRIVATE_KEY: The private key used to authenticate with the remote server | |
# - REMOTE_HOST: The hostname of the remote server | |
# - REMOTE_USER: The username used to authenticate with the remote server | |
# - REMOTE_PROD_TARGET: The path on the remote server where the site will be deployed to | |
# - REMOTE_PREPROD_TARGET: The path on the remote server where the site will be deployed to | |
# - URL_ENCODED_PASSWORD: The password used to authenticate with the remote server, URL encoded (e.g. using https://www.urlencoder.org/) | |
# Workflow triggers on pushes to the develop and master branches: | |
# - On the develop branch, the site is deployed to the preprod target | |
# - On the master branch, the site is deployed to the prod target | |
name: Build and Deploy | |
on: | |
push: | |
branches: [ develop, master ] | |
jobs: | |
build-deploy: | |
runs-on: ubuntu-latest | |
steps: | |
- uses: actions/checkout@v4 | |
# Install composer dependencies | |
- id: build-php | |
name: "Install dependencies" | |
uses: shivammathur/setup-php@v2 | |
with: | |
php-version: 8.0 | |
- run: composer install --prefer-dist --no-dev -o | |
# Get the public IP of the runner | |
- name: Public IP | |
id: ip | |
uses: haythem/[email protected] | |
# Whitelist the public IP of the runner on the remote server by adding it to the SSH whitelist and wait 65 seconds for the IP to be whitelisted | |
- shell: bash | |
run: | | |
curl -sX GET 'https://${{ secrets.REMOTE_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.REMOTE_HOST }}:2083/frontend/o2switch/o2switch-ssh-whitelist/index.live.php' | fgrep 'index.live.php' | fgrep 'index.live.php?r=remove&address=' | cut -d '"' -f 2 | while read ipToRemove | |
do | |
curl -sX GET 'https://${{ secrets.REMOTE_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.REMOTE_HOST }}:2083/frontend/o2switch/o2switch-ssh-whitelist/'$ipToRemove > /dev/null 2>&1 | |
done | |
curl -X POST \ | |
-d 'whitelist[address]=${{ steps.ip.outputs.ipv4 }}' \ | |
-d 'whitelist[port]=22' \ | |
'https://${{ secrets.REMOTE_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.REMOTE_HOST }}:2083/frontend/o2switch/o2switch-ssh-whitelist/index.live.php?r=add' > /dev/null 2>&1 | |
curl -sX GET 'https://${{ secrets.REMOTE_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.REMOTE_HOST }}:2083/frontend/o2switch/o2switch-ssh-whitelist/index.live.php' | fgrep -q '${{ steps.ip.outputs.ipv4 }}' && echo "IP whitelisted" | |
sleep 65 | |
# Deploy the develop branch to the preprod target | |
- name: 'Deploy on develop branch' | |
if: ${{ github.ref == 'refs/heads/develop' }} | |
uses: easingthemes/ssh-deploy@main | |
with: | |
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
ARGS: "-rlgoDzvc -i --delete-after" | |
SOURCE: "./" | |
REMOTE_HOST: ${{ secrets.REMOTE_HOST }} | |
REMOTE_USER: ${{ secrets.REMOTE_USER }} | |
TARGET: ${{ secrets.REMOTE_PREPROD_TARGET }} | |
EXCLUDE: "/dist/, /node_modules/, ./auth.json" | |
SCRIPT_BEFORE: | | |
whoami | |
ls -al | |
SCRIPT_AFTER: | | |
whoami | |
ls -al | |
echo $RSYNC_STDOUT | |
# Deploy the master branch to the production target | |
- name: 'Deploy on master branch' | |
if: ${{ github.ref == 'refs/heads/master' }} | |
uses: easingthemes/ssh-deploy@main | |
with: | |
SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} | |
ARGS: "-rlgoDzvc -i --delete-after" | |
SOURCE: "./" | |
REMOTE_HOST: ${{ secrets.REMOTE_HOST }} | |
REMOTE_USER: ${{ secrets.REMOTE_USER }} | |
TARGET: ${{ secrets.REMOTE_PROD_TARGET }} | |
EXCLUDE: "/dist/, /node_modules/, ./auth.json" | |
SCRIPT_BEFORE: | | |
whoami | |
ls -al | |
SCRIPT_AFTER: | | |
whoami | |
ls -al | |
echo $RSYNC_STDOUT |
Bonjour à tous,
J'ai un problème similaire à celui de @AdrienCpr, c'est à dire :
Get actual whitelisted IPs...
jq: parse error: Invalid numeric literal at line 2, column 10
Error: Process completed with exit code 5.
Cependant, lorsque j'essaye d'accéder à l'endpoint https://$LOGIN:$PASSWORD@$HOST:2083/frontend/o2switch/o2switch-ssh-whitelist/index.live.php?r=list
, j'ai simplement la page de login que vous connaissez bien s'affichant sans aucune erreur.
Sachant que j'utilise la 2FA pour accéder à mon espace cPanel, je me demande si ce ne serait pas lié. Sur d'autres services, il est souvent requis de remplacer le mot de passe par un token lorsque la 2FA est activée.
J'ai donc généré un token dans l'outil Manage API Tokens mais c'est pour utiliser l'API de cPanel, or il me semble que les endpoints qu'on essaye de joindre ici ne font pas partie de l'API cPanel.
Un génie parmi-vous pour m'aider ?
Un extrait de mon workflow avec les modifications proposée par @DamChtlv :
# Get the public IP of the runner
- name: Public IP
id: ip
uses: haythem/[email protected]
# Whitelist the public IP of the runner on the remote server by adding it
# to the SSH whitelist and wait 65 seconds for the IP to be whitelisted
- name: Whitelist IP on hosting & delete github old ones (o2switch)
shell: bash
run: |
ENDPOINT='frontend/o2switch/o2switch-ssh-whitelist/index.live.php'
echo "Get actual whitelisted IPs..."
UNIQUE_IPS=$(curl -sX GET "https://${{ secrets.SSH_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=list" | jq -r '.data.list[] | .address' | sort -u)
for address in $UNIQUE_IPS; do
# if [[ $address == "$#{{ secrets.IP_TO_KEEP }}" ]]; then
# echo "Keep this IP, go to the next..."
# continue
# fi
echo "Delete this github IP: $address (in & out)"
curl -sX GET "https://${{ secrets.SSH_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=remove&address=$address&direction=in&port=22" | jq
sleep 3
curl -sX GET "https://${{ secrets.SSH_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=remove&address=$address&direction=out&port=22" | jq
sleep 3
done
echo "All non-whitelisted IPs deleted!"
echo "Attempt to whitelist IP..."
curl -sX POST -d 'whitelist[address]=${{ steps.ip.outputs.ipv4 }}' -d 'whitelist[port]=22' "https://${{ secrets.SSH_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=add" | jq
@d9beuD Oui, c'est très probablement la 2FA qui bloque. Il faudrait essayer d'ajuster les appels en appliquant ce que cPanel recommande ici :
✅ Effectivement, c'était bien ça ! Merci pour l'exemple @madrzejewski. Pour y arriver, j'ai dû :
- Installer de quoi générer un code OTP sur le runner
- uses: tecolicom/actions-use-apt-tools@v1
with:
tools: oathtool gnupg2
- Générer un code OTP
OTP=$(oathtool -b --totp '${{ secrets.O2SWITCH_OTP_SECRET }}')
- Utiliser une authentification Basic
AUTH_STR="Basic $(echo -n '${{ secrets.SSH_USER }}:${{ secrets.SSH_PASSWORD }}' | base64)"
- Utiliser l'authentification sur chaque requête CURL
Il suffit de rajouter -H "Authorization: $AUTH_STR" -H "X-CPANEL-OTP: $OTP"
et d'enlever ${{ secrets.SSH_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@
à chaque requête.
- curl -sX GET "https://${{ secrets.SSH_USER }}:${{ secrets.URL_ENCODED_PASSWORD }}@${{ secrets.SSH_HOST }}:2083/$ENDPOINT?
+ curl -sX GET "https://${{ secrets.SSH_HOST }}:2083/$ENDPOINT?" -H "Authorization: $AUTH_STR" -H "X-CPANEL-OTP: $OTP"
Solution finale
# Get the public IP of the runner
- name: Public IP
id: ip
uses: haythem/[email protected]
- uses: tecolicom/actions-use-apt-tools@v1
with:
tools: oathtool gnupg2
# Whitelist the public IP of the runner on the remote server by adding it
# to the SSH whitelist and wait 65 seconds for the IP to be whitelisted
- name: Whitelist IP on hosting & delete github old ones (o2switch)
shell: bash
run: |
ENDPOINT='frontend/o2switch/o2switch-ssh-whitelist/index.live.php'
OTP=$(oathtool -b --totp '${{ secrets.O2SWITCH_OTP_SECRET }}')
AUTH_STR="Basic $(echo -n '${{ secrets.SSH_USER }}:${{ secrets.SSH_PASSWORD }}' | base64)"
echo "Get actual whitelisted IPs..."
UNIQUE_IPS=$(curl -H "Authorization: $AUTH_STR" -H "X-CPANEL-OTP: $OTP" -sX GET "https://${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=list" | jq -r '.data.list[] | .address' | sort -u)
for address in $UNIQUE_IPS; do
# if [[ $address == "$#{{ secrets.IP_TO_KEEP }}" ]]; then
# echo "Keep this IP, go to the next..."
# continue
# fi
echo "Delete this github IP: $address (in & out)"
curl -H "Authorization: $AUTH_STR" -H "X-CPANEL-OTP: $OTP" -sX GET "https://${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=remove&address=$address&direction=in&port=22" | jq
sleep 3
curl -H "Authorization: $AUTH_STR" -H "X-CPANEL-OTP: $OTP" -sX GET "https://${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=remove&address=$address&direction=out&port=22" | jq
sleep 3
done
echo "All non-whitelisted IPs deleted!"
echo "Attempt to whitelist IP..."
curl -H "Authorization: $AUTH_STR" -H "X-CPANEL-OTP: $OTP" -sX POST -d 'whitelist[address]=${{ steps.ip.outputs.ipv4 }}' -d 'whitelist[port]=22' "https://${{ secrets.SSH_HOST }}:2083/$ENDPOINT?r=add" | jq
Comme c'est un bout de code assez conséquent et peu pratique à maintenir quand on a plusieurs projets en dépendant, j'ai pris l'initiative de publier une action sur le Marketplace de GitHub Actions.
https://github.com/d9beuD/o2switch-whitelisting
Cela fonctionne très bien avec la 2FA et normalement ça devrait être pareil sans. J'apprécierais vraiment si quelqu'un se dévouait pour tester sans 2FA.
Un petit message pour dire qu'on est en train de tester une mise à jour de l'outil d'autorisation SSH. Nous avons étendu l'API de cPanel pour y inclure l'outil.
Qu'est-ce que ça change ?
L'outil s'utilise de la même manière que l'API cPanel. Donc il devient compatible avec le système de Token d'API de cPanel. Cela évite de devoir mettre l'identifiant / mot de passe du compte dans les secrets Github.
Ça veut également dire qu'on peut l'utiliser en mode CLI, ça devient un module supplémentaire de la UAPI cPanel.
À cet instant, cela n'est pas publié sur tous les serveurs. C'est en phase de test. Je l'ai utilisé dans le workflow de notre nouvelle documentation et c'est fonctionnel.
Il reste 2-3 choses à revoir, notamment dans le format des réponses, mais ça devrait être disponible partout prochainement.
On va sans doute garder l'ancienne méthode en complément, pour ne pas casser des workflows déjà utilisés.
Merci pour l'information, je mettrai à jour mon action pour que ce soit prêt une fois la fonctionnalité disponible sur tous les serveurs. Avez-vous un temps estimé à communiquer @madrzejewski ?
Je pense que ça sera déployé d'ici une ou deux semaines. L'outil est prêt, il reste juste 2-3 petits détails à corriger, c'est pas grand chose.
Bonjour @Tatar73
L'erreur semble indiquer un problème avec l'installation de la clé SSH, c'est l'authentification sans mot de passe qui échoue.
La mise en liste blanche fonctionne correctement, ce n'est pas un problème à ce niveau.
Si ce n'était pas le cas, il y aurait un timeout à la place, ça n'arriverait pas jusqu'à l'erreur suivante :
Là, la connexion se fait bien, c'est à l'authentification que ça bloque.
Il faut vérifier que la clé SSH est bien installé et plus particulièrement les permissions sur les différents fichiers (erreurs courantes) :
Il faut aussi vérifier qu'il n'y a pas une protection par mot de passe sur la clé privé, généralement ça bloque aussi.
Pour avoir plus d'information, il est possible d'ajouter un '-vv' ou '-vvv' à la commande SSH échoue et la sortie devrait être bien plus verbeuse. Ça peut aider à trouver le problème.
EDIT: Je n'avais pas vu la partie " error in libcrypto", ça semble indiquer que la clé déployée sur le runner est invalide. Il faut vérifier si elle n'est pas tronquée ou altérée à cause d'un copier/coller foireux par exemple.