Script shell (ZSH) pour loguer les adresses IP publique sur macOS

MilesTEG

Administreur
Membre du personnel
6 Septembre 2020
3 100
744
213
Hello par ici,
Je ne sais pas si ça intéressera quelqu'un, mais sait-on jamais.
J'ai de temps à autre, lorsque je suis en 4G sur mon mac, mon IP publique via le partage de connexion qui se fait bannir par Crowdsec et/ou fail2ban sur mon NAS.
Et il m'arrive de voir ces notifications que lorsque je suis rentré chez moi, donc plus avec cette adresse IP.
Et donc j'aimerais vérifier que j'ai bien eu cette adresse IP plus tôt. Sauf que macOS ne logue pas les adresses IP...

J'ai donc écrit un petit script pour loguer les adresses IP que j'obtiens avec mon MBA, et par la même occasion l'interface réseau utilisée pour faire la connexion.

La version la plus à jour sera sur ce GIST : https://gist.github.com/MilesTEG1/b69fad580a508a2c02db1b8213e50fdc
Il y a aussi deux fichiers associés sur le Gist.

Le code ci-dessous est donc celui de la date de création de ce sujet ;) Si j'y pense, je le modifierais si je mets à jour le GIST.

Bash:
#!/bin/zsh
#
#   Version : 1.1.2
#
# Script pour loguer les différentes adresses IP obtenues sur le mac
# Prérequis :
#       - ts via moreutils : brewi moreutils        (ou : brew install moreutils )
#       - ou ets : https://github.com/zmwangx/ets#installation
#       - tee : man tee
#       - logrotate : brewi logrotate       (ou : brew install logrotate )
#
#       -> Tout installer en une seule commande : brewi moreutils logrotate       (ou : brew install moreutils logrotate )
#
# Voir à la fin du script la méthode pour lancer automatiquement ce script toutes les 5 minutes.
# Et aussi pour la partie logrotate.

# ====================================================================================== #
# ================================= Variables à définir ================================ #
# ====================================================================================== #
#
# Chemin absolu d'accès au fichier log dans lequel on veut conserver les IP détectées.
LOG_FILE="/Users/user_lambda/LOGs/ip_log/adresses_ip.log"

# Pour logrotate (à installer avant !), chemins absolus !
LOG_ROTATE_CONF_FILE="/Users/user_lambda/LOGs/logrotate/ip_log.zsh.conf"
LOG_ROTATE_LOG_FILE="/Users/user_lambda/LOGs/logrotate.log"

# Définition des chemins d'accès pour wget et ts et ip (à installer via homebrew)
wget_bin="/opt/homebrew/bin/wget"
ts_bin="/opt/homebrew/bin/ts"
ip_bin="/opt/homebrew/bin/ip"
logrotate_bin="/opt/homebrew/sbin/logrotate"
# --------------------------------------------------------------------------------------

# ====================================================================================== #
# ============================= Définitions des fonctions ============================== #
# ====================================================================================== #

#  command to log     | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"

function check_log_file() {

    # ╔══════════════════════════════════════════════════════════════════════════╗
    # ║ Fonction pour tester si le fichier log existe                            ║
    # ╚══════════════════════════════════════════════════════════════════════════╝
    # Idée : https://github.com/Bleala/Vdirsyncer-DOCKERIZED/blob/main/files/scripts/start.sh

    if [[ ! -e "${LOG_FILE}" ]]; then
        # Create Log File
        # curl --create-dirs --output "${LOG_FILE}" file:///dev/null >/dev/null 2>&1
        touch "${LOG_FILE}"
        # Save exit code
        LOG_FILE_CREATED="${?}"
        # Check if Log File has been created
        if [[ "${LOG_FILE_CREATED}" -ne 0 ]]; then
            # User info
            {
                printf "\nLogfile %s could not be created!" $LOG_FILE
                printf "\nCheck the \"LOG_FILE\" environment variable."
            } 2>&1 | $ts_bin '[%Y-%m-%d %H:%M:%S]'
            # Exit script
            exit 1
        fi
    else
        # Lancement de logrotate
        #   Il faudra avoir préparé en amont le fichier de configuration. Exemple : (à copier coller et à décommenter)
        # /path_to/adresses_ip.log {
        #     daily
        #     rotate 7
        #     minage 1
        #     minsize 171k
        #     nocompress
        #     nodateext
        #     missingok
        #     notifempty
        #     create 644 userAAA staff
        #     su userAAA staff
        # }
        $logrotate_bin "$LOG_ROTATE_CONF_FILE"
    fi
}

function get_interface() {

    # ╔══════════════════════════════════════════════════════════════════════════╗
    # ║ Fonction pour récupérer la ou les interfaces réseau connectées.          ║
    # ║ Donne en premier celle utilisée par défaut.                              ║
    # ╚══════════════════════════════════════════════════════════════════════════╝

    interface_array=()
    # interface_string=$($ip_bin route | grep default | sed -e "s/^.*dev.//" | tr '\n' ' ')
    interface_array=($($ip_bin route | grep default | sed -e "s/^.*dev.//"))

    # DEBUG
    # echo "---- DEBUG"
    # interface_array_lenght=$(( ${#interface_array[@]} ))
    # printf "interface_string = %s.\n" "$interface_string"
    # printf "interface_array = %s.\nlen : %d.\ninterface_array[1] = %s\n" "$interface_array" ${#interface_array[@]} "${interface_array[1]}"
    # echo "---- DEBUG"

    if ((${#interface_array[@]} != 0)); then
        message="\t\tInterface principale utilisée par défaut : ${interface_array[1]}\t"
        if [[ "${interface_array[1]}" =~ ^(ppp[[:digit:]]{1}|.*tun[[:digit:]]{1})$ ]]; then
            # On est sur une connexion VPN là ! ppp0 ou ppp1 etc...
            vpn_active_connection=""
            vpn_active_connection=$(scutil --nc list | grep Connected)
            if [[ -z "${vpn_active_connection}" ]]; then
                printf "\tErreur : La connexion VPN ne semble pas active... Vérifier les connexions et le script ! -- DEBUG ! Fin du script.\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
                exit 1
            elif [[ $vpn_active_connection =~ "^.*(\".*\").*$" ]] ||[[ $vpn_active_connection =~ "^.*(\".*) \[VPN.*$" ]]; then # Regex : https://unix.stackexchange.com/questions/421460/bash-regex-and-https-regex101-com
                if [[ "${match[1]: -1}" == "\"" ]]; then
                    message+="\t Connexion VPN ->  $match[1]\t"
                else
                    message+="\t Connexion VPN ->  $match[1]...\"\t"
                fi
            fi
        fi

        if ((${#interface_array[@]} > 1)); then
            message+="\n\t\t\t( Il y a au moins une autre interface réseaux connectée, mais non utilisée par défaut :"
            for ((i = 2; i < ${#interface_array[@]} + 1; i++)); do
                message+=" ${interface_array[i]}"
                if ((i != ${#interface_array[@]})); then
                    message+=","
                else
                    message+=" )"
                fi
            done
        fi
        printf "$message\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
    else
        printf "\t\tL'interface par défaut n'a pas pu être déterminée.\nSoit il n'y a aucune connexion réseau disponible, soit celle(s) en cours ne sont pas connectées.\nVérifier les connexions réseaux.\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
        return 1
    fi
}

function get_active_connection_and_MAC() {

    # ╔══════════════════════════════════════════════════════════════════════════╗
    # ║ Fonction pour récupérer les interfaces réseaux actives et le nom de      ║
    # ║ ces interfaces.                                                          ║
    # ║ Peut aussi récupérer l'adresse MAC des interfaces, désactivé ici.        ║
    # ╚══════════════════════════════════════════════════════════════════════════╝

    # Idée d'origine : https://gist.github.com/reorx/bec96e0d117855dbe36505c1eaec0ddf
    services=$(networksetup -listnetworkserviceorder | grep 'Hardware Port')
    while read line; do
        sname=$(echo $line | awk -F "(, )|(: )|[)]" '{print $2}')
        sdev=$(echo $line | awk -F "(, )|(: )|[)]" '{print $4}')
        if [ -n "$sdev" ]; then
            ifout="$(ifconfig $sdev 2>/dev/null)"
            echo "$ifout" | grep 'status: active' >/dev/null 2>&1
            rc="$?"
            if [ "$rc" -eq 0 ]; then
                currentservice="$sname"
                currentdevice="$sdev"
                # currentmac=$(echo "$ifout" | awk '/ether/{print $2}')
                # current_values+="$currentservice|$currentdevice|$currentmac"
                current_values+="$currentservice|$currentdevice"
            fi
        fi
    done <<<"$(echo "$services")"

    # DEBUG
    # echo "\${#current_values[@]} = ${#current_values[@]}"
    # echo "$current_values"

    if [ ${#current_values[@]} -ne 0 ]; then
        connection_name="$(echo "${current_values[1]}" | awk -F "\|" '{print $1}')"
        connection_interface="$(echo "${current_values[1]}" | awk -F "\|" '{print $2}')"
        message="\t\tConnexion principale active : « $connection_name » via interface $connection_interface"
        if [[ "${connection_name}" == "Wi-Fi" ]]; then
            message+="  ( SSID = $(networksetup -getairportnetwork $connection_interface | awk -F':' '{ print$2 }' | cut -c 2-) )"
        fi
        printf "${message}\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"


        # for value in "${current_values[@]}"; do
        #     # value1=$(echo "$value" | awk -F "\|" '{print $1}')
        #     printf "\nConnexion principale : « %s » via interface %s\n" "$(echo "$value" | awk -F "\|" '{print $1}')" "$(echo "$value" | awk -F "\|" '{print $2}')"
        #     # printf "Nom de l'interface : %s\t" "$(echo "$value" | awk -F "\|" '{print $2}')"
        #     # printf "Adresse MAC : %s\n" "$(echo "$value" | awk -F "\|" '{print $3}')"
        # done

        for ((i = 2; i < ${#current_values[@]} + 1; i++)); do
            printf "\t\t\tConnexion secondaire : « %s » via interface %s\n" "$(echo "${current_values[i]}" | awk -F "\|" '{print $1}')" "$(echo "${current_values[i]}" | awk -F "\|" '{print $2}')" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
        done

    else
        echo >&2 "Could not find current service"
        return 1
    fi
}

function get_ip() {

    # ╔══════════════════════════════════════════════════════════════════════════╗
    # ║ Fonction pour récupérer l'adresse IP publique.                           ║
    # ╚══════════════════════════════════════════════════════════════════════════╝

    arg1=""
    # ~~~~~ Arguments passed to the function ~~~~ #
    nb_param=$#
    arg1=$1 # Si arg1="no-print" on n'affichera pas les messages.
    # Sinon (arg1=""), on affiche les messages

    # On conserve la valeur de l'adresse IP potentiellement déjà déterminée
    ip_old=$ip

    for ((j = 0; j < 5; j++)); do
        # Là aussi, 5 essais max sur 10s, des fois que la connexion vienne juste de se faire...
        ip=$($wget_bin -qO- ifconfig.me/ip)
        if [[ -n "${ip}" ]]; then
            if [[ "${arg1}" != "no-print" ]]; then
                printf "\t\tAdresse IP publique détectée : %s\n" $ip | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
            fi
            break
        elif [[ -z "${ip}" ]] && [[ j -eq 9 ]]; then
            if [[ "${arg1}" != "no-print" ]]; then
                printf "\t\tL'adresse IP publique ne peut pas être déterminée après 5 essais. Vérifier la connexion internet.\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
            fi
            return 1
        else
            if [[ "${arg1}" != "no-print" ]]; then
                printf "\t\tL'adresse IP publique n'a pas pu être déterminée. Essai n°%d / 5.\n" $((j + 1)) | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
            fi
        fi
        sleep 2
    done

    if [[ "${ip_old}" != "${ip}" ]] && [[ -n "${ip_old}" ]]; then
        printf "\t\t** L'adresse IP ( %s ) a changé depuis sa détection au début du script...\n" "${ip_old}" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
        printf "\t\t** Nouvelle adresse IP publique détectée : %s\n" $ip | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
        return 2
    fi

}

# ====================================================================================== #
# ============================= Partie principale du script ============================ #
# ====================================================================================== #

# ╔══════════════════════════════════════════════════════════════════════════╗
# ║ 0. On vérifie le fichier log et on fait tourner les .log                 ║
# ║ 1. On initialise certaines variables à zéro                              ║
# ║ 2. On récupère l'adresse IP, et si on l'obtient...                       ║
# ║ 3. On récupère les noms et interface de connexions actives               ║
# ║ 4. On récupère l'interface par défaut utilisée.                          ║
# ╚══════════════════════════════════════════════════════════════════════════╝

check_log_file

ip=""
interface_string=""
interface_array=()
current_values=()

printf "\n--- Détection de la connexion active et de l'ip publique ---\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"

get_ip
return_code_IP="$?"

if [ "$return_code_IP" -eq 0 ]; then
    get_active_connection_and_MAC
    return_code_AC="$?"
    if [ "$return_code_AC" -eq 0 ]; then
        sleep 5
        get_interface
        return_code_GI="$?"
        if [ "$return_code_GI" -ne 0 ]; then
            exit 1
        fi
        # On vérigie que l'adresse IP n'a pas changée
        get_ip "no-print"
        return_code_IP2="$?"
        if [ "$return_code_IP2" -eq 2 ]; then
            printf "\t\t** L'adresse IP ayant changée depuis la première détection, il se peut qu'une connexion VPN ait été activée ou désactivée.\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
            printf "\t\t** On relance les tests.\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"
            get_active_connection_and_MAC
            return_code_AC2="$?"
            if [ "$return_code_AC2" -eq 0 ]; then
                sleep 5
                get_interface
                return_code_GI2="$?"
                if [ "$return_code_GI2" -ne 0 ]; then
                    exit 1
                fi
                get_ip
                if [ "$return_code_GI" -ne 0 ]; then
                    exit 1
                fi
            fi

        elif [ "$return_code_IP2" -ne 0 ]; then
            exit 1
        fi
    fi
fi

printf "--- Fin du script ---\n" | $ts_bin '[%Y-%m-%d %H:%M:%S]' | tee -a "${LOG_FILE}"

# ====================================================================================== #
# ================= Méthode pour lancer le script toutes les 5 minutes ================= #
# ====================================================================================== #
#
# Il faut utiliser launchd.
#
# 1. Modification du fichier launched.ip_log.plist
#    Dans ce fichier, il faut changer user_lambda par votre nom d'utilisateur à vous,
#    si vous placez les fichiers dans votre dossier de profile.
#    Il est aussi possible d'ajuster le timing d'exécution. Mon conseil, utiliser :
#    - https://crontab-generator.org/ : pour générer le format crontab du Schedule du lien suivant ;
#    - https://launched.zerowidth.com/ : pour générer le .plist.
#
# 2. Copier le fichier launched.ip_log.plist dans ~/Library/LaunchAgents/
#     cp launched.ip_log.plist ~/Library/LaunchAgents/

# 3. Activer le .plist dans LaunchAgents : ( Voir aide : https://launched.zerowidth.com/help )
#     launchctl load -w ~/Library/LaunchAgents/launched.ip_log.plist
 
#     Il est aussi possible d'utiliser des outils comme lunchy (via homebrew), ou encore
#     LaunchControl (payant) ou encore Lingon X (payant aussi).
#
# ====================================================================================== #
# ================= Pour la rotation du fichier log créé par le script ================= #
# ====================================================================================== #
#
#   Il faudra avoir préparé en amont le fichier de configuration.
#   Exemple : voir le fichier ip_log.zsh.conf
#   Il contient ceci :
#        /path_to/adresses_ip.log {
#            daily
#            rotate 7
#            nocompress
#            nodateext
#            missingok
#            notifempty
#            create 644 userAAA staff
#            su userAAA staff
#        }
#
# ====================================================================================== #
 
Dernière édition:
  • J'aime
Réactions: Dreambox