Aller au contenu

Copy certificat let's encrypt existant d'un nas source vers un autre nas cible

Featured Replies

Posté(e)

Bonjour à Tous,

Désolé de pas être très actif sur le forum par manque de temps en particulier.
Mais je m'en remet à vos connaissances en la matières car je ne suis pas très compétent en commande Linux.

Mon objectif est d'utiliser le planificateur de tâches du Synology pour la tâche suivante : Copy certificat let's encrypt existant d'un Nas source vers un autre Nas cible.
J'ai 3 Nas Synology dont le 1er a un certificat "Let's Encrypt" qui se renouvelle régulièrement.
Jusqu'à présent, je suis obligé de copier manuellement le dernier certificat à jour du 1er vers les 2 autres.
Reprenez moi si je dis une bêtise, mais actuellement (sur les 2 autres NAS), il n'est pas possible de renouvellement (en mode auto tous les 3 mois) pour un même nom de domaine : Let's Encrypt ne le permet pas.

J'ai donc demandé à une IA de me créer un script pour effectuer cette action mais je n'ai aucune certitude de son bon fonctionnement, pire créer des erreurs ou disfonctionnement du Nas cible.
Ce script est ajouter sur le NAS source qui a le renouvellement automatique fonctionnel.
Il vérifie si un changement de version de certificat a eu lieu sur le Nas source, et seulement si, effectuer les vérifications sur le Nas cible pour renouvellement, redémarre les services et contrôle si toutes les tâches se sont appliquées sans erreurs.
Chose importante, ce script impose l'ouverture du service SSH sur les NAS. Je ne suis pas très chaud à faire cela pour les raisons évidentes de sécurité.
S'il est possible de modifier ce besoin ou bien de le sécurisé, je suis à votre écoute.

Je vous fourni le script ci-dessous, en espérant qu'une personne attentive et compétente prendra le temps de vérifier et me conseiller.
Je suis preneur de tout autre script existant pour cette tâche si déjà existante.
Vous remerciant par avance à ceux qui me lirons.
PS : ce qui est en couleur est à personnaliser selon votre environnement

En cas d'erreur, ne pas hésiter à me le faire savoir

Cordialement

****************************************************************************************************************

#!/bin/bash

# ============================================================

# "VOTRE_NOM DE DOMAINE" certificate synchronisation - V3

#

# Source:

# NAS 1

#

# Destination:

# IP_NAS_CIBLE

#

# DSM 7.2.2-72806 Update 6 (version du DSM à préciser - Idéalement identique aux 2 Nas)

#

# Fonctions:

# - auto-détection certificat via INFO

# - détection renouvellement SHA256

# - transfert SSH/SCP

# - import SYNO.Core.Certificate

# - prise en compte restart_httpd

# - vérification certificat installé

# - vérification certificat HTTPS réellement présenté

# - journalisation

# - notification DSM en cas d'erreur

#

# ============================================================

set -u

# ============================================================

# CONFIGURATION

# ============================================================

CERT_NAME="VOTRE_NOM_DE_DOMAINE"

NAS2="192.168.26.156"

NAS2_USER="root"

ARCHIVE_BASE="/usr/syno/etc/certificate/_archive"

INFO_FILE="${ARCHIVE_BASE}/INFO"

LOG_FILE="/var/log/VOTRE_NOM_DE_DOMAINE-cert-sync.log"

LOG_MAX_SIZE=2097152

STATE_DIR="/usr/local/etc"

STATE_FILE="${STATE_DIR}/VOTRE_NOM_DE_DOMAINE-cert-sync.sha256"

REMOTE_TMP="/tmp/VOTRE_NOM_DE_DOMAINE-cert-sync"

# HTTPS à vérifier sur NAS 2

HTTPS_HOST="IP_NAS_CIBLE"

HTTPS_PORT="PORT_NAS_CIBLE"

# Nombre de tentatives après import

VERIFY_RETRIES=12

# secondes entre deux tentatives

VERIFY_DELAY=5

# SSH

SSH_OPTS="

-o BatchMode=yes

-o ConnectTimeout=10

-o ServerAliveInterval=10

-o ServerAliveCountMax=3

"

# ============================================================

# LOG

# ============================================================

log()

{

echo "$(date '+%Y-%m-%d %H:%M:%S') | $*" >> "${LOG_FILE}"

}

# ============================================================

# NOTIFICATION

# ============================================================

notify()

{

TITLE="$1"

MESSAGE="$2"

# --------------------------------------------------------

# Notification DSM.

#

# synodsmnotify est conservé comme notification locale.

# Selon DSM/configuration, les chaînes libres peuvent être

# ignorées car DSM 7 documente normalement des clés I18N.

# --------------------------------------------------------

if [ -x /usr/syno/bin/synodsmnotify ]; then

/usr/syno/bin/synodsmnotify \

@administrators \

"${TITLE}" \

"${MESSAGE}" \

>/dev/null 2>&1 || true

fi

}

# ============================================================

# ERREUR

# ============================================================

error_exit()

{

MSG="$1"

log "============================================================"

log "ERREUR | ${MSG}"

log "============================================================"

notify \

"Certificat ${CERT_NAME}" \

"Erreur synchronisation NAS 2 : ${MSG}"

exit 1

}

# ============================================================

# ROTATION LOG

# ============================================================

mkdir -p "$(dirname "${LOG_FILE}")"

if [ -f "${LOG_FILE}" ]; then

LOG_SIZE=$(wc -c < "${LOG_FILE}" 2>/dev/null || echo 0)

if [ "${LOG_SIZE}" -ge "${LOG_MAX_SIZE}" ]; then

mv "${LOG_FILE}" "${LOG_FILE}.1" 2>/dev/null || true

fi

fi

touch "${LOG_FILE}" || exit 1

chmod 600 "${LOG_FILE}"

# ============================================================

# DEBUT

# ============================================================

log "============================================================"

log "Début synchronisation ${CERT_NAME}"

log "============================================================"

# ============================================================

# ROOT

# ============================================================

if [ "$(id -u)" != "0" ]; then

error_exit "Le script doit être exécuté en root."

fi

# ============================================================

# VERIFICATION COMMANDES

# ============================================================

for CMD in jq openssl sha256sum ssh scp timeout; do

if ! command -v "${CMD}" >/dev/null 2>&1; then

error_exit "Commande absente : ${CMD}"

fi

done

# ============================================================

# AUTO-DETECTION CERTIFICAT NAS 1

# ============================================================

if [ ! -f "${INFO_FILE}" ]; then

error_exit "INFO introuvable : ${INFO_FILE}"

fi

SOURCE_CERT_ID=$(

jq -r \

--arg CERT "${CERT_NAME}" \

'

to_entries[]

| select(

any(

.value.services[]?;

.display_name == $CERT

)

)

| .key

' "${INFO_FILE}" |

head -n 1

)

if [ -z "${SOURCE_CERT_ID}" ] ||

[ "${SOURCE_CERT_ID}" = "null" ]; then

error_exit \

"Certificat ${CERT_NAME} introuvable sur NAS 1."

fi

SOURCE_DIR="${ARCHIVE_BASE}/${SOURCE_CERT_ID}"

log "Certificat NAS 1 : ${SOURCE_CERT_ID}"

log "Répertoire : ${SOURCE_DIR}"

# ============================================================

# VERIFICATION FICHIERS SOURCE

# ============================================================

for FILE in cert.pem chain.pem privkey.pem; do

if [ ! -s "${SOURCE_DIR}/${FILE}" ]; then

error_exit \

"Fichier source absent ou vide : ${FILE}"

fi

done

# ============================================================

# VALIDATION CERTIFICAT

# ============================================================

if ! openssl x509 \

-in "${SOURCE_DIR}/cert.pem" \

-noout >/dev/null 2>&1; then

error_exit "Certificat source invalide."

fi

SOURCE_SUBJECT=$(

openssl x509 \

-in "${SOURCE_DIR}/cert.pem" \

-noout -subject

)

SOURCE_ISSUER=$(

openssl x509 \

-in "${SOURCE_DIR}/cert.pem" \

-noout -issuer

)

SOURCE_ENDDATE=$(

openssl x509 \

-in "${SOURCE_DIR}/cert.pem" \

-noout -enddate

)

SOURCE_SERIAL=$(

openssl x509 \

-in "${SOURCE_DIR}/cert.pem" \

-noout -serial

)

log "Sujet : ${SOURCE_SUBJECT}"

log "Émetteur : ${SOURCE_ISSUER}"

log "Expiration : ${SOURCE_ENDDATE}"

log "Serial : ${SOURCE_SERIAL}"

# ============================================================

# EMPREINTE

# ============================================================

CURRENT_HASH=$(

{

sha256sum "${SOURCE_DIR}/cert.pem"

sha256sum "${SOURCE_DIR}/chain.pem"

sha256sum "${SOURCE_DIR}/privkey.pem"

} |

sha256sum |

awk '{print $1}'

)

log "Empreinte actuelle : ${CURRENT_HASH}"

# ============================================================

# DETECTION RENOUVELLEMENT

# ============================================================

if [ -f "${STATE_FILE}" ]; then

OLD_HASH=$(cat "${STATE_FILE}" 2>/dev/null)

log "Empreinte précédente : ${OLD_HASH}"

if [ "${CURRENT_HASH}" = "${OLD_HASH}" ]; then

log "Aucun changement détecté."

log "Fin."

log "============================================================"

exit 0

fi

log "Nouveau certificat détecté."

else

log "Première exécution : aucune empreinte précédente."

fi

# ============================================================

# SSH NAS 2

# ============================================================

log "Connexion SSH vers ${NAS2}..."

if ! ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"echo SSH_OK" 2>/dev/null |

grep -q "SSH_OK"; then

error_exit \

"Connexion SSH impossible vers ${NAS2}."

fi

log "SSH OK."

# ============================================================

# AUTO-DETECTION CERTIFICAT NAS 2

# ============================================================

REMOTE_CERT_ID=$(

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"jq -r --arg CERT '${CERT_NAME}' '

to_entries[]

| select(

any(

.value.services[]?;

.display_name == \$CERT

)

)

| .key

' /usr/syno/etc/certificate/_archive/INFO" \

2>/dev/null |

head -n 1

)

if [ -z "${REMOTE_CERT_ID}" ] ||

[ "${REMOTE_CERT_ID}" = "null" ]; then

error_exit \

"Certificat ${CERT_NAME} introuvable sur NAS 2."

fi

REMOTE_CERT_DIR="${ARCHIVE_BASE}/${REMOTE_CERT_ID}"

log "Certificat NAS 2 : ${REMOTE_CERT_ID}"

log "Répertoire : ${REMOTE_CERT_DIR}"

# ============================================================

# PREPARATION TEMPORAIRE

# ============================================================

log "Préparation transfert..."

if ! ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"rm -rf '${REMOTE_TMP}' &&

mkdir -m 700 '${REMOTE_TMP}'"; then

error_exit \

"Impossible de créer le répertoire temporaire NAS 2."

fi

# ============================================================

# NETTOYAGE DISTANT EN CAS D'ERREUR

# ============================================================

cleanup_remote()

{

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"rm -rf '${REMOTE_TMP}'" \

>/dev/null 2>&1 || true

}

# ============================================================

# COPIE

# ============================================================

log "Copie cert.pem..."

if ! scp ${SSH_OPTS} \

"${SOURCE_DIR}/cert.pem" \

"${NAS2_USER}@${NAS2}:${REMOTE_TMP}/cert.pem"; then

cleanup_remote

error_exit "Copie cert.pem échouée."

fi

log "Copie chain.pem..."

if ! scp ${SSH_OPTS} \

"${SOURCE_DIR}/chain.pem" \

"${NAS2_USER}@${NAS2}:${REMOTE_TMP}/chain.pem"; then

cleanup_remote

error_exit "Copie chain.pem échouée."

fi

log "Copie privkey.pem..."

if ! scp ${SSH_OPTS} \

"${SOURCE_DIR}/privkey.pem" \

"${NAS2_USER}@${NAS2}:${REMOTE_TMP}/privkey.pem"; then

cleanup_remote

error_exit "Copie privkey.pem échouée."

fi

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"chmod 600 '${REMOTE_TMP}/privkey.pem'"

log "Copie terminée."

# ============================================================

# IMPORT DSM

# ============================================================

log "Import certificat dans DSM..."

REMOTE_RESULT=$(

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

bash <<REMOTE_SCRIPT

set -u

CERT_ID="${REMOTE_CERT_ID}"

TMP="${REMOTE_TMP}"

for FILE in cert.pem chain.pem privkey.pem; do

if [ ! -s "\${TMP}/\${FILE}" ]; then

echo '{"success":false,"error":"Fichier manquant"}'

rm -rf "\${TMP}"

exit 1

fi

done

# Validation certificat

if ! openssl x509 \

-in "\${TMP}/cert.pem" \

-noout >/dev/null 2>&1; then

echo '{"success":false,"error":"Certificat invalide"}'

rm -rf "\${TMP}"

exit 1

fi

# ------------------------------------------------------------

# Import DSM

# ------------------------------------------------------------

RESULT=\$(

/usr/syno/bin/synowebapi \

--exec-fastwebapi \

api="SYNO.Core.Certificate" \

method="import" \

version="1" \

key_tmp="\"\${TMP}/privkey.pem\"" \

cert_tmp="\"\${TMP}/cert.pem\"" \

inter_cert_tmp="\"\${TMP}/chain.pem\"" \

id="\"\${CERT_ID}\"" \

desc="\"${CERT_NAME}\"" \

2>&1

)

RET=\$?

echo "\${RESULT}"

rm -rf "\${TMP}"

exit \${RET}

REMOTE_SCRIPT

)

REMOTE_RET=$?

log "Réponse API : ${REMOTE_RESULT}"

if [ "${REMOTE_RET}" -ne 0 ]; then

error_exit \

"API DSM : code retour ${REMOTE_RET}."

fi

# ============================================================

# RESULTAT API

# ============================================================

API_SUCCESS=$(

echo "${REMOTE_RESULT}" |

jq -r '.success // false' 2>/dev/null

)

if [ "${API_SUCCESS}" != "true" ]; then

error_exit \

"DSM n'a pas confirmé l'import."

fi

log "Import DSM réussi."

# ============================================================

# DETECTION RESTART HTTPD

# ============================================================

RESTART_HTTPD=$(

echo "${REMOTE_RESULT}" |

jq -r '.data.restart_httpd // .restart_httpd // false' \

2>/dev/null

)

log "restart_httpd : ${RESTART_HTTPD}"

# ============================================================

# SI DSM DEMANDE RESTART HTTP

# ============================================================

if [ "${RESTART_HTTPD}" = "true" ]; then

log "DSM demande un redémarrage/rechargement HTTP."

# --------------------------------------------------------

# On ne tue PAS nginx directement.

#

# On laisse DSM gérer ses services.

# --------------------------------------------------------

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"/usr/syno/bin/synowebapi \

--exec-fastwebapi \

api='SYNO.Core.Web.DSM' \

method='restart' \

version='1' \

>/dev/null 2>&1 || true" \

>/dev/null 2>&1 || true

log "Commande de rechargement HTTP envoyée."

else

log "DSM n'indique pas de redémarrage HTTP nécessaire."

fi

# ============================================================

# ATTENTE DU SERVICE HTTPS

# ============================================================

log "Attente disponibilité HTTPS NAS 2..."

HTTPS_READY="false"

for i in $(seq 1 "${VERIFY_RETRIES}"); do

if timeout 5 bash -c \

"</dev/tcp/${HTTPS_HOST}/${HTTPS_PORT}" \

>/dev/null 2>&1; then

HTTPS_READY="true"

log "HTTPS disponible après ${i} tentative(s)."

break

fi

log "HTTPS indisponible - tentative ${i}/${VERIFY_RETRIES}"

sleep "${VERIFY_DELAY}"

done

if [ "${HTTPS_READY}" != "true" ]; then

error_exit \

"HTTPS ${HTTPS_HOST}:${HTTPS_PORT} ne répond pas après import."

fi

# ============================================================

# VERIFICATION CERTIFICAT DANS ARCHIVE DSM

# ============================================================

log "Vérification certificat dans _archive..."

REMOTE_SERIAL=$(

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"openssl x509 \

-in '${REMOTE_CERT_DIR}/cert.pem' \

-noout -serial" \

2>/dev/null

)

if [ "${REMOTE_SERIAL}" != "${SOURCE_SERIAL}" ]; then

error_exit \

"Serial du certificat installé différent."

fi

log "Serial _archive : OK."

# ============================================================

# VERIFICATION SHA256 CERTIFICAT PUBLIC

# ============================================================

SOURCE_CERT_HASH=$(

sha256sum "${SOURCE_DIR}/cert.pem" |

awk '{print $1}'

)

REMOTE_CERT_HASH=$(

ssh ${SSH_OPTS} \

"${NAS2_USER}@${NAS2}" \

"sha256sum '${REMOTE_CERT_DIR}/cert.pem'" |

awk '{print $1}'

)

if [ "${SOURCE_CERT_HASH}" != "${REMOTE_CERT_HASH}" ]; then

error_exit \

"SHA256 du certificat _archive différent."

fi

log "SHA256 _archive : OK."

# ============================================================

# VERIFICATION CERTIFICAT PRESENTE PAR HTTPS

#

# On interroge le NAS 2 directement.

#

# -servername = VOTRE_NOM_DE_DOMAINE.me

# permet de tester le certificat du bon virtual host.

# ============================================================

log "Vérification certificat réellement présenté par HTTPS..."

REMOTE_HTTPS_SERIAL=$(

echo |

timeout 15 openssl s_client \

-connect "${HTTPS_HOST}:${HTTPS_PORT}" \

-servername "${CERT_NAME}" \

-showcerts \

2>/dev/null |

openssl x509 \

-noout \

-serial 2>/dev/null

)

if [ -z "${REMOTE_HTTPS_SERIAL}" ]; then

error_exit \

"Impossible de récupérer le certificat présenté par HTTPS."

fi

log "Serial HTTPS : ${REMOTE_HTTPS_SERIAL}"

if [ "${REMOTE_HTTPS_SERIAL}" != "${SOURCE_SERIAL}" ]; then

error_exit \

"Le certificat présenté par HTTPS ne correspond pas au certificat source."

fi

log "Certificat HTTPS : OK."

# ============================================================

# VERIFICATION DATE D'EXPIRATION HTTPS

# ============================================================

REMOTE_HTTPS_ENDDATE=$(

echo |

timeout 15 openssl s_client \

-connect "${HTTPS_HOST}:${HTTPS_PORT}" \

-servername "${CERT_NAME}" \

2>/dev/null |

openssl x509 \

-noout \

-enddate 2>/dev/null

)

log "Expiration HTTPS : ${REMOTE_HTTPS_ENDDATE}"

# ============================================================

# SAUVEGARDE ETAT

# ============================================================

mkdir -p "${STATE_DIR}"

echo "${CURRENT_HASH}" > "${STATE_FILE}"

chmod 600 "${STATE_FILE}"

log "Nouvelle empreinte enregistrée."

# ============================================================

# SUCCES

# ============================================================

log "============================================================"

log "SUCCES"

log "Certificat ${CERT_NAME} synchronisé vers NAS 2."

log "Certificat DSM : ${REMOTE_CERT_ID}"

log "Serial : ${SOURCE_SERIAL}"

log "HTTPS : vérifié"

log "============================================================"

notify \

"Certificat ${CERT_NAME}" \

"Certificat renouvelé et vérifié sur le NAS 2."

exit 0

Modifié par philou65

Posté(e)
il y a 10 minutes, philou65 a dit :

il n'est pas possible de renouvellement (en mode auto tous les 3 mois) pour un même nom de domaine : Let's Encrypt ne le permet pas.

Ah bon ? Je fais ça sans problème sur plusieurs NAS. Avez-vous au moins essayé ?

Rejoindre la conversation

Vous pouvez publier maintenant et vous inscrire plus tard. Si vous avez un compte, connectez-vous maintenant pour publier avec votre compte.

Invité
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
Répondre à ce sujet…

Qui est en ligne (Afficher la liste complète)

  • Il n’y a aucun utilisateur enregistré actuellement en ligne

Account

Navigation

Rechercher

Rechercher

Configure browser push notifications

Chrome (Android)
  1. Tap the lock icon next to the address bar.
  2. Tap Permissions → Notifications.
  3. Adjust your preference.
Chrome (Desktop)
  1. Click the padlock icon in the address bar.
  2. Select Site settings.
  3. Find Notifications and adjust your preference.