Pagina 1 di 1

[RISOLTO] Pacchetto per configurare una rete Wi-Fi su Windows, Linux e macOS

Pubblicato: 7 gennaio 2026 - 13:09
di bastien30
Buongiorno,

Ecco un pacchetto per configurare una rete Wi-Fi su Windows (con netsh), Linux (con nmcli) e MacOS (con networksetup).

Potrebbe essere notevolmente migliorato, ma così com'è, funziona sulla mia flotta :-)

setup.py:

Codice: Seleziona tutto

# -*- coding: utf-8 -*-
from setuphelpers import *
import platform

# Wifi settings
wifi_ssid = r'MY-WIFI-NETWORK'
wifi_password = r'my-wifi-password'
hidden_ssid = False

# Globally enable verbose mode
all_verbose = False

def install():
    if platform.system() == 'Windows':
        set_wifi_profile_windows(ssid=wifi_ssid, password=wifi_password, hidden=hidden_ssid, verbose=all_verbose)
    elif platform.system() == 'Darwin':
        set_wifi_profile_macos(ssid=wifi_ssid, password=wifi_password, verbose=all_verbose)
    elif platform.system() == 'Linux':
        set_wifi_profile_linux(ssid=wifi_ssid, password=wifi_password, hidden=hidden_ssid, verbose=all_verbose)
    else:
        error('Operating System not supported !')

def uninstall():
    if platform.system() == 'Windows':
        remove_wifi_profile_windows(ssid=wifi_ssid, verbose=all_verbose)
    elif platform.system() == 'Darwin':
        remove_wifi_profile_macos(ssid=wifi_ssid, verbose=all_verbose)
    elif platform.system() == 'Linux':
        remove_wifi_profile_linux(ssid=wifi_ssid, verbose=all_verbose)
    else:
        error('Operating System not supported !')


#### MACOS WIFI FUNCTIONS

def set_wifi_profile_macos(ssid, password, verbose=False):
    r"""Configure and apply WIFI profile on MacOS using networksetup

    Args:
        ssid: network SSID to connect
        password: wifi pre-shared key
        verbose: if set to True, display commands used (default: False)
        
    Notes: 
      - networksetup does not care about hidden network, this method works in all cases
      - networksetup does not care about authentication and encryption modes, it always use the most secure option possible
      - the configured SSID will be set to connect automatically by default

    For more informations see https://www.hexnode.com/mobile-device-management/help/scripts-to-manage-wi-fi-network-settings-on-macos-devices/
    """
    # Remove existings profiles first
    remove_wifi_profile_macos(ssid, verbose=verbose)
    time.sleep(2)

    print(r'Creating or updating WLAN profile %s...' % ssid)
    cmd = r'sudo networksetup -setairportnetwork en0 %s %s' % (ssid, password)
    if verbose:
        print(r'Command used: %s' % cmd)
    run(cmd)

def remove_wifi_profile_macos(ssid, verbose=False):
    r"""Remove WIFI profile if exists on MacOS using networksetup

    Args:
        ssid: profile SSID to remove
        verbose: if set to True, display command used
    """
    print(r'Removing WLAN profile %s if exists...' % ssid)
    # Note: WIFI stay connected after removal but can't reconnect after reboot or disconnection
    cmd = r'sudo networksetup -removepreferredwirelessnetwork en0 %s ' % ssid
    if verbose:
        print(r'Command used: %s' % cmd)
    run_notfatal(cmd)


#### WINDOWS FUNCTIONS

def set_wifi_profile_windows(ssid, password, authentication_mode="WPA2PSK", encryption_mode="AES", connection_mode="auto", hidden=False, verbose=False):
    r"""Configure and apply WIFI profile on Windows using netsh

    Args:
        ssid: network SSID to connect
        password: wifi pre-shared key
        authentication_mode: authentication method to use for connection (default: WPA2PSK)
        encryption_mode: encryption type to use for connection (default: AES)
        connection_mode: wether to enable wifi automatic connection, can be 'auto' or 'manual' (default: auto)
        hidden: if set to True, configure a hidden network (default: False)
        verbose: if set to True, display generated XML and commands used (default: False)
        
    For more informations see https://learn.microsoft.com/fr-fr/uwp/schemas/mobilebroadbandschema/wlan/schema-root
    """
    import tempfile

    if connection_mode not in ("auto", "manuel"):
        error("Error: connection_mode can be 'auto' or 'manual' only !")
    
    xml_template = """<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>{tmpl_ssid}</name>
    <SSIDConfig>
        <SSID>
            <name>{tmpl_ssid}</name>
        </SSID>
        <nonBroadcast>{tmpl_hidden}</nonBroadcast>
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>{tmpl_connection_mode}</connectionMode>
    <MSM>
        <security>
            <authEncryption>
                <authentication>{tmpl_authentication_mode}</authentication>
                <encryption>{tmpl_encryption_mode}</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
            <sharedKey>
                <keyType>passPhrase</keyType>
                <protected>false</protected>
                <keyMaterial>{tmpl_password}</keyMaterial>
            </sharedKey>
        </security>
    </MSM>
</WLANProfile>"""

    if hidden:
        hidden_value = r'true'
    else:
        hidden_value = r'false'

    # Replace variables in template
    xml_data = xml_template.format(
        tmpl_ssid = ssid,
        tmpl_hidden = hidden_value,
        tmpl_connection_mode = connection_mode,
        tmpl_authentication_mode = authentication_mode,
        tmpl_encryption_mode = encryption_mode,
        tmpl_password = password
    )

    # Remove existings profiles first
    remove_wifi_profile_windows(ssid, verbose=verbose)  
    time.sleep(2)

    # Use try/finally to ensure XML file is always deleted
    try:
        # Use temporary file
        with tempfile.NamedTemporaryFile(mode="w+", suffix=".xml", delete=False, encoding="utf-8") as tmpfile:
            tmpfile.write(xml_data)
            tmpfile.flush()  # force write on disk
            xml_file = tmpfile.name

            if verbose:
                tmpfile.seek(0)
                print("XML content:\n%s" % tmpfile.read())

        print(r'Creating or updating WLAN profile %s...' % ssid)
        cmd = r'netsh wlan add profile filename=%s user=all' % xml_file
        if verbose:
            print(r'Command used: %s' % cmd)
        run(cmd)
    finally:
        remove_file(xml_file)

def remove_wifi_profile_windows(ssid, verbose=False):
    r"""Remove WIFI profile if exists on Windows using netsh

    Args:
        ssid: profile SSID to remove
        verbose: if set to True, display command used
    """
    print(r'Removing WLAN profile %s if exists...' % ssid)
    cmd = r'netsh wlan delete profile name= %s' % ssid
    if verbose:
        print(r'Command used: %s' % cmd)
    run_notfatal(cmd)


#### LINUX FUNCTIONS

def set_wifi_profile_linux(ssid, password, hidden=False, verbose=False):
    r"""Configure and apply WIFI profile on Linux using nmcli

    Args:
        ssid: network SSID to connect
        password: wifi pre-shared key
        hidden: if set to True, configure a hidden network (default: False)
        verbose: if set to True, display commands used (default: False)

        Note: 
          - nmcli does not care about authentication and encryption modes, it always use the most secure option possible
          - the configured SSID will be set to connect automatically by default

        For more informations see https://networkmanager.dev/docs/api/latest/nmcli.html
    """
    if hidden:
        hidden_value = r'yes'
    else:
        hidden_value = r'no'

    print(r'Creating or updating WLAN profile %s...' % ssid)

    # Remove existings profiles first
    remove_wifi_profile_linux(ssid, verbose=verbose)
    time.sleep(2)

    # Get interface name
    cmd = "nmcli d | grep wifi | grep -v p2p | awk \'{print $1}\'"
    if verbose:
        print(r'Getting wifi interface name with command: %s' % cmd)
    ifname = run(cmd).strip()
    if ifname == "":
        error(r'Error getting wifi interface name !')

    # Add connection
    cmd = r'nmcli con add type wifi con-name %s ssid "%s" ifname %s' % (ssid, ssid, ifname)
    if verbose:
        print(r'Creating wifi connection with command: %s' % cmd)
    run(cmd)

    # Try to connect (sometimes nmcli needs time to be able to interact with previously created connection)
    cmd = r'nmcli dev wifi con "%s" name "%s" password "%s" hidden %s' % (ssid, ssid, password, hidden_value)
    if verbose:
        print(r'Connecting to wifi network with command: %s' % cmd)
    try_nb = 12
    sleep_duration = 5
    for i in range(1, try_nb):
        time.sleep(sleep_duration)
        try:
            print(r'Trying to connect... %s/%s ' % (i, try_nb), end="")
            run(cmd)
        except:
            print(r'FAILED')
            continue
        else:
            print(r'SUCCESS')
            return True
    error(r'Error: cannot connect to wifi network %s after %s attempts !' % (ssid, try_nb))

def remove_wifi_profile_linux(ssid, verbose=False):
    r"""Remove WIFI profile if exists on Linux using nmcli

    Args:
        ssid: profile SSID to remove
        verbose: if set to True, display command used
    """
    print(r'Removing WLAN profile %s if exists...' % ssid)
    cmds = [r'nmcli con delete "%s" ' % ssid,
            r'nmcli con delete "Auto %s" ' % ssid] # Linux Mint create a "Auto <SSID>" connection when configured through GUI, ensure it's deleted too
    if verbose:
        print(r'Commands used: %s' % cmds)
    for cmd in cmds:
        run_notfatal(cmd)

Re: [RISOLTO] Pacchetto per configurare una rete Wi-Fi su Windows, Linux e macOS

Pubblicato: 8 gennaio 2026 - 19:09
di vcardon
Ciao bastien30

, grazie per il tuo pacchetto. Ti suggerisco un miglioramento basato su https://www.wapt.fr/fr/doc/wapt-create- ... se-feature.

Infatti, le password nei pacchetti WAPT sono dati sensibili e riservati, e i pacchetti WAPT sono visibili pubblicamente per impostazione predefinita.

Il suggerimento presente nel link è un ottimo metodo per proteggere le informazioni sensibili contenute in un pacchetto WAPT.

Re: [RISOLTO] Pacchetto per configurare una rete Wi-Fi su Windows, Linux e macOS

Pubblicato: 8 gennaio 2026 - 22:16
di bastien30
Ciao Vincent,

Per essere sincero, il mio pacchetto integra questo meccanismo, ma lo baso su una versione precedente di questo pacchetto che ho leggermente modificato.
Sebbene funzionale, è sicuramente meno ottimizzata rispetto all'ultima versione disponibile sullo store.
Lo uso in altri pacchetti che ho creato qualche tempo fa... così è più veloce copiare e incollare quando mi serve :rotolo:

Ecco la versione completa del colpo di stato!

setup.py:

Codice: Seleziona tutto

# -*- coding: utf-8 -*-
from setuphelpers import *
import base64
import platform

wifi_ssid = r'MY-WIFI-NETWORK'
hidden_ssid = False
encrypted_txt_file = makepath(r'certs', r'encrypt-txt.json')

all_verbose = False

def install():
    # Decrypt WIFI password if hosts_uuid exists in encrypted txt file
    encryptlist = json_load_file(encrypted_txt_file)
    if WAPT.host_uuid in encryptlist:
        host_key = WAPT.get_host_key()
        encrypted_txt = host_key.decrypt(base64.b64decode(encryptlist[WAPT.host_uuid])).decode('utf-8')
    else:
        print('Host UUID %s not found in %s, nothing to do !' % (WAPT.host_uuid, encrypted_txt_file))
        return 0

    if platform.system() == 'Windows':
        set_wifi_profile_windows(ssid=wifi_ssid, password=encrypted_txt, hidden=hidden_ssid, verbose=all_verbose)
    elif platform.system() == 'Darwin':
        set_wifi_profile_macos(ssid=wifi_ssid, password=encrypted_txt, verbose=all_verbose)
    elif platform.system() == 'Linux':
        set_wifi_profile_linux(ssid=wifi_ssid, password=encrypted_txt, hidden=hidden_ssid, verbose=all_verbose)
    else:
        error('Operating System not supported !')

def uninstall():
    if platform.system() == 'Windows':
        remove_wifi_profile_windows(ssid=wifi_ssid, verbose=all_verbose)
    elif platform.system() == 'Darwin':
        remove_wifi_profile_macos(ssid=wifi_ssid, verbose=all_verbose)
    elif platform.system() == 'Linux':
        remove_wifi_profile_linux(ssid=wifi_ssid, verbose=all_verbose)
    else:
        error('Operating System not supported !')


#### MACOS WIFI FUNCTIONS

def set_wifi_profile_macos(ssid, password, verbose=False):
    r"""Configure and apply WIFI profile on MacOS using networksetup

    Args:
        ssid: network SSID to connect
        password: wifi pre-shared key
        verbose: if set to True, display generated XML and command used (default: False)
        
    Notes: 
      - networksetup does not care about hidden network, this method works in all cases
      - networksetup does not care about authentication and encryption modes, it always use the most secure option possible
      - the configured SSID will be set to connect automatically by default

    For more informations see https://www.hexnode.com/mobile-device-management/help/scripts-to-manage-wi-fi-network-settings-on-macos-devices/
    """
    # Remove existings profiles first
    remove_wifi_profile_macos(ssid, verbose=verbose)
    time.sleep(2)

    print(r'Creating or updating WLAN profile %s...' % ssid)
    cmd = r'sudo networksetup -setairportnetwork en0 %s %s' % (ssid, password)
    if verbose:
        print(r'Command used: %s' % cmd)
    run(cmd)

def remove_wifi_profile_macos(ssid, verbose=False):
    r"""Remove WIFI profile if exists on MacOS using networksetup

    Args:
        ssid: profile SSID to remove
        verbose: if set to True, display command used
    """
    print(r'Removing WLAN profile %s if exists...' % ssid)
    # Note: WIFI stay connected after removal but can't reconnect after reboot or disconnection
    cmd = r'sudo networksetup -removepreferredwirelessnetwork en0 %s ' % ssid
    if verbose:
        print(r'Command used: %s' % cmd)
    run_notfatal(cmd)


#### WINDOWS FUNCTIONS

def set_wifi_profile_windows(ssid, password, authentication_mode="WPA2PSK", encryption_mode="AES", connection_mode="auto", hidden=False, verbose=False):
    r"""Configure and apply WIFI profile on Windows using netsh

    Args:
        ssid: network SSID to connect
        password: wifi pre-shared key
        authentication_mode: authentication method to use for connection (default: WPA2PSK)
        encryption_mode: encryption type to use for connection (default: AES)
        connection_mode: wether to enable wifi automatic connection, can be 'auto' or 'manual' (default: auto)
        hidden: if set to True, configure a hidden network (default: False)
        verbose: if set to True, display generated XML and command used (default: False)
        
    For more informations see https://learn.microsoft.com/fr-fr/uwp/schemas/mobilebroadbandschema/wlan/schema-root
    """
    import tempfile

    if connection_mode not in ("auto", "manuel"):
        error("Error: connection_mode can be 'auto' or 'manual' only !")
    
    xml_template = """<?xml version="1.0"?>
<WLANProfile xmlns="http://www.microsoft.com/networking/WLAN/profile/v1">
    <name>{tmpl_ssid}</name>
    <SSIDConfig>
        <SSID>
            <name>{tmpl_ssid}</name>
        </SSID>
        <nonBroadcast>{tmpl_hidden}</nonBroadcast>
    </SSIDConfig>
    <connectionType>ESS</connectionType>
    <connectionMode>{tmpl_connection_mode}</connectionMode>
    <MSM>
        <security>
            <authEncryption>
                <authentication>{tmpl_authentication_mode}</authentication>
                <encryption>{tmpl_encryption_mode}</encryption>
                <useOneX>false</useOneX>
            </authEncryption>
            <sharedKey>
                <keyType>passPhrase</keyType>
                <protected>false</protected>
                <keyMaterial>{tmpl_password}</keyMaterial>
            </sharedKey>
        </security>
    </MSM>
</WLANProfile>"""

    if hidden:
        hidden_value = r'true'
    else:
        hidden_value = r'false'

    # Replace variables in template
    xml_data = xml_template.format(
        tmpl_ssid = ssid,
        tmpl_hidden = hidden_value,
        tmpl_connection_mode = connection_mode,
        tmpl_authentication_mode = authentication_mode,
        tmpl_encryption_mode = encryption_mode,
        tmpl_password = password
    )

    # Remove existings profiles first
    remove_wifi_profile_windows(ssid, verbose=verbose)  
    time.sleep(2)

    # Use try/finally to ensure XML file is always deleted
    try:
        # Use temporary file
        with tempfile.NamedTemporaryFile(mode="w+", suffix=".xml", delete=False, encoding="utf-8") as tmpfile:
            tmpfile.write(xml_data)
            tmpfile.flush()  # force write on disk
            xml_file = tmpfile.name

            if verbose:
                tmpfile.seek(0)
                print("XML content:\n%s" % tmpfile.read())

        print(r'Creating or updating WLAN profile %s...' % ssid)
        cmd = r'netsh wlan add profile filename=%s user=all' % xml_file
        if verbose:
            print(r'Command used: %s' % cmd)
        run(cmd)
    finally:
        remove_file(xml_file)

def remove_wifi_profile_windows(ssid, verbose=False):
    r"""Remove WIFI profile if exists on Windows using netsh

    Args:
        ssid: profile SSID to remove
        verbose: if set to True, display command used
    """
    print(r'Removing WLAN profile %s if exists...' % ssid)
    cmd = r'netsh wlan delete profile name= %s' % ssid
    if verbose:
        print(r'Command used: %s' % cmd)
    run_notfatal(cmd)


#### LINUX FUNCTIONS

def set_wifi_profile_linux(ssid, password, hidden=False, verbose=False):
    r"""Configure and apply WIFI profile on Linux using nmcli

    Args:
        ssid: network SSID to connect
        password: wifi pre-shared key
        hidden: if set to True, configure a hidden network (default: False)
        verbose: if set to True, display generated XML and command used (default: False)

        Note: 
          - nmcli does not care about authentication and encryption modes, it always use the most secure option possible
          - the configured SSID will be set to connect automatically by default

        For more informations see https://networkmanager.dev/docs/api/latest/nmcli.html
    """
    if hidden:
        hidden_value = r'yes'
    else:
        hidden_value = r'no'

    print(r'Creating or updating WLAN profile %s...' % ssid)

    # Remove existings profiles first
    remove_wifi_profile_linux(ssid, verbose=verbose)
    time.sleep(2)

    # Get interface name
    cmd = "nmcli d | grep wifi | grep -v p2p | awk \'{print $1}\'"
    if verbose:
        print(r'Getting wifi interface name with command: %s' % cmd)
    ifname = run(cmd).strip()
    if ifname == "":
        error(r'Error getting wifi interface name !')

    # Add connection
    cmd = r'nmcli con add type wifi con-name %s ssid "%s" ifname %s' % (ssid, ssid, ifname)
    if verbose:
        print(r'Creating wifi connection with command: %s' % cmd)
    run(cmd)

    # Try to connect (sometimes nmcli needs time to be able to interact with previously created connection)
    cmd = r'nmcli dev wifi con "%s" name "%s" password "%s" hidden %s' % (ssid, ssid, password, hidden_value)
    if verbose:
        print(r'Connecting to wifi network with command: %s' % cmd)
    try_nb = 12
    sleep_duration = 5
    for i in range(1, try_nb):
        time.sleep(sleep_duration)
        try:
            print(r'Trying to connect... %s/%s ' % (i, try_nb), end="")
            run(cmd)
        except:
            print(r'FAILED')
            continue
        else:
            print(r'SUCCESS')
            return True
    error(r'Error: cannot connect to wifi network %s after %s attempts !' % (ssid, try_nb))

def remove_wifi_profile_linux(ssid, verbose=False):
    r"""Remove WIFI profile if exists on Linux using nmcli

    Args:
        ssid: profile SSID to remove
        verbose: if set to True, display command used
    """
    print(r'Removing WLAN profile %s if exists...' % ssid)
    cmds = [r'nmcli con delete "%s" ' % ssid,
            r'nmcli con delete "Auto %s" ' % ssid] # Linux Mint create a "Auto <SSID>" connection when configured through GUI, ensure it's deleted too
    if verbose:
        print(r'Commands used: %s' % cmds)
    for cmd in cmds:
        run_notfatal(cmd)
update_package.py:

Codice: Seleziona tutto

# -*- coding: UTF-8 -*-
from setuphelpers import *
import json
import base64
from waptcrypto import SSLCertificate
import waptguihelper

encrypted_txt_file = makepath(r'certs', r'encrypt-txt.json')

def update_package():
    # Get server URL
    urlserver = inifile_readstring(makepath(install_location('WAPT_is1'),'wapt-get.ini'),'global','wapt_server').replace('https://','')

    # Connect to API
    print(r'Asking user for WAPT server credentials...')
    credentials_url = waptguihelper.login_password_dialog('Credentials for wapt server',urlserver,'admin','')
    WAPT.waptserver.post('api/v3/login' ,data=json.dumps({'user': credentials_url["user"], 'password':credentials_url["password"]}))

    # Get WIFI password
    print(r'Asking user for WIFI password...')
    encrypted_txt = waptguihelper.input_dialog("Encrypting with WAPT", "Enter the WIFI password: ", "")
    if not encrypted_txt:
        error("Please provide WIFI password !")

    # Remove certs directory and recreate it empty
    if isdir("certs"):
        remove_tree("certs")
    mkdirs("certs")

    # Get certificates from all hosts
    data = WAPT.waptserver.get("api/v3/hosts?columns=host_certificate&limit=1000000")["result"]

    # Encrypt WIFI password with all certificates
    print(r'Encrypting WIFI password with all hosts certificates...')
    encrypt_dict = {}
    for value in data:
        if value["host_certificate"]:
            host_cert = SSLCertificate(crt_string=value["host_certificate"])
            encrypt_dict[value["uuid"]] = base64.b64encode(host_cert.encrypt(encrypted_txt.encode("utf-8"))).decode("utf-8")
            print(value["computer_fqdn"] + ":" + value["uuid"] + ":" + encrypt_dict[value["uuid"]])

    # Write encrypted password to json file
    json_write_file(encrypted_txt_file, encrypt_dict)

    print("\nPackage updated with success !")
E il piccolo file di testo che aggiungo a questo tipo di pacchetti, in modo che i miei colleghi sappiano più o meno come funziona e come aggiornarlo:

Codice: Seleziona tutto

## Principe :

- on chiffre le mot de passe wifi avec chacune des clés publique de chacun des agents WAPT et on l'exporte dans un fichier texte
- lors de l'installation, chacun des agents WAPT pourra déchiffrer le mot de passe grace à sa clé privée

-> ainsi, le mot de passe wifi n'est pas en clair dans le paquet (il l'est uniquement le temps de l'installation)


## Mettre à jour le paquet :

- dans VSCode, lancer l'update-package depuis le menu de gauche

- entrer les identifiants d'accès à WAPT (identique au mot de passe de la console)

- lorsque c'est demandé, entrer le mot de passe wifi

- si aucune erreur, la console en bas de VSCode doit afficher "Package updated with success !"

- on supprime le dossier __pycache__ à la racine du paquet

- on incrémente le numéro de version du paquet (la partie après le tiret)

- on peut build-upload le paquet ;)

Re: [RISOLTO] Pacchetto per configurare una rete Wi-Fi su Windows, Linux e macOS

Pubblicato: 12 gennaio 2026 - 10:07
di dcardon
Ciao Bastien,

è un pacchetto davvero valido. :-) Penso che potrebbe essere fonte di ispirazione per molti altri amministratori WAPT, mostrando loro la potenza di questo strumento.

Per quanto riguarda l'autenticazione Wi-Fi, se puoi configurare i tuoi AP in modalità di autenticazione RADIUS EAP-TLS, Simon ha sviluppato un equivalente di ADCS (Active Directory Certificate Services) [1] per la distribuzione di certificati alle workstation client aggiunte al dominio. L'obiettivo principale di questo nuovo strumento è facilitare la distribuzione di certificati alle workstation degli utenti per l'autenticazione 802.1x (ora che Microsoft ha rimosso l'autenticazione MS-CHAPv2 in Windows 11) o VPN.

Se hai un po' di tempo, ti incoraggio a testare lo strumento. Se ti permette di eliminare qualche password qua e là, è un'ottima cosa. :-)

A presto e buon anno 2026!

Denis

[1] https://github.com/tranquilit/adcs_python