Gestire Sophos Switch con CLI, API REST locale e API Fusion
La CLI e l’API REST locali accedono direttamente a un Sophos Switch. La Sophos Fusion Switch Management API è separata: usa credenziali di un service principal a livello di tenant e distribuisce policy centrali agli switch. Questo runbook descrive, per entrambi i percorsi, identità, URL di base e procedure di verifica.
Ambito e decisione di base
Questo articolo tratta gli argomenti seguenti:
- accesso CLI locale tramite un percorso di gestione rilasciato per il dispositivo, in particolare SSH;
- orientamento e diagnosi nella CLI;
- accesso all’API REST locale del dispositivo;
- ciclo di vita del token Bearer della sessione;
- un test API documentato in sola lettura con
GET /api/ports; - verifica sicura, risoluzione dei problemi, rollback e chiusura della sessione.
Non presenta volutamente un catalogo completo degli endpoint API o dei comandi CLI. La documentazione centrale fornisce indicazioni generali; prima di usare l’API, tuttavia, occorre verificare che le funzioni siano disponibili sul dispositivo di destinazione. I percorsi indicati come /ports sono relativi alla base /api, quindi il percorso completo della chiamata è /api/ports. Anche i comandi, le modalità e i parametri CLI possono variare in base al modello e al firmware. La verifica obbligatoria è descritta nella sezione Variabilità del firmware e del modello.
Prima dell’accesso, stabilire quanto segue:
- Il sistema autorevole è Sophos Fusion o la gestione locale?
- Quali switch, modello, firmware e indirizzo IP di gestione sono interessati?
- È sufficiente l’accesso in lettura o serve una modifica approvata?
- Quali sono lo stato iniziale, il criterio di successo e il piano di rollback?
- Esiste un percorso di gestione indipendente nel caso in cui la modifica interrompa l’accesso normale?
Non mescolare identità e ruoli
Gli account disponibili in People sono account locali dello switch:
| Tipo di privilegio locale | Diritti sullo switch | Uso tipico |
|---|---|---|
| Admin | Visualizza e modifica tutte le funzioni dello switch | Amministrazione locale approvata e chiamate API di scrittura |
| User | Visualizza le impostazioni, ma non può modificarle | Diagnosi e verifica secondo il principio del privilegio minimo |
Questi ruoli locali non corrispondono ai ruoli amministrativi di Sophos Fusion. Un ruolo Fusion non conferisce automaticamente l’accesso alla CLI o all’API locale, e le credenziali locali non sono credenziali Fusion. Le credenziali di un account locale dello switch vengono inviate a /api/system/login.
Gli account locali sono gestiti nell’interfaccia locale sotto People:
- Selezionare Add per creare un account o scegliere Edit accanto a un account.
- Impostare Username, Password e Privilege type.
- Selezionare Privilege type come Admin o User.
- Salvare con Apply.
Una password locale deve soddisfare tutti i seguenti requisiti:
- almeno 10 e non più di 32 caratteri;
- almeno una lettera e un numero;
- almeno uno di questi caratteri speciali:
@ ~ % * # + - =.
Un account Admin locale può cambiare le password degli altri account, ma non quella dell’account predefinito admin. La password di quest’ultimo può essere cambiata soltanto dall’account admin stesso o da Sophos Fusion. Per le normali attività, utilizzare account locali personali anziché condividere l’account admin.
Preparare l’accesso
Prima di una sessione:
- Confrontare con il ticket di modifica l’IP di gestione, il modello, lo stato del firmware, la posizione e il numero di serie.
- Consentire l’accesso soltanto da una rete di gestione amministrativa e da un host amministrativo autorizzato.
- Verificare che i servizi di gestione non siano accessibili dalle VLAN utente o da Internet.
- Controllare l’origine dell’ora dello switch e dell’host amministrativo; un’ora errata rende più difficile analizzare log e risposte API.
- Per una modifica, predisporre un backup della configurazione corrente e un percorso di ripristino indipendente.
- Se la gestione è centralizzata, acquisire lo stato di destinazione centrale e documentare esplicitamente l’eccezione locale.
- Non memorizzare mai credenziali o token Bearer nel testo del ticket, in chat, screenshot, cronologia della shell o codice sorgente.
Sostituire i segnaposto seguenti:
| Segnaposto | Significato |
|---|---|
<Switch-IP address> | IP di gestione o nome di gestione attendibile del dispositivo di destinazione |
<LOCAL-USERNAME> | account locale dello switch, non utente Fusion |
your-password | password locale; solo un segnaposto, mai usare come password reale |
xxxxxxxx.yyyyyyyy.zzzzzz | esempio mascherato di token Bearer, non un token reale |
Usa CLI in modo sicuro
Stabilire la connessione
SSH fa parte dei servizi di gestione locale. Deve essere configurato sul firmware in uso e accessibile dalla rete di gestione. Un comando client generico è:
ssh <LOCAL-USERNAME>@<Switch-IP-address>
La sintassi è soltanto un esempio: sostituire interamente <LOCAL-USERNAME> e <Switch-IP-address>, incluse le parentesi angolari. Prima di accettare la chiave host, confrontarne l’impronta tramite un canale indipendente e attendibile. Non ignorare un avviso relativo a una chiave host cambiata: verificare prima un’eventuale sostituzione del dispositivo, un ripristino delle impostazioni di fabbrica, un conflitto IP o un possibile attacco man-in-the-middle.
Se il modello dispone di un accesso fisico alla console, questo può fungere da percorso di manutenzione indipendente. Adattare al modello i parametri seriali e di connessione; non riutilizzare quelli di un altro modello Sophos Switch.
Orientarsi nella CLI
Osservare innanzitutto il prompt e la modalità di comando correnti. Non presumere che un comando sia disponibile in tutte le modalità. I tasti e gli strumenti di aiuto documentati sono:
| Input | Effetto |
|---|---|
? | Elenca i comandi disponibili |
TAB | Completa il comando |
| Freccia su/giù | Mostra i comandi eseguiti in precedenza |
| Freccia sinistra/destra | Sposta il cursore nella riga corrente |
Backspace o Ctrl + H | Eliminare un carattere |
History | Visualizza la cronologia dei comandi |
Q | Chiude l’output e torna al prompt dello switch |
Verificare sul dispositivo di destinazione, con ? o TAB, le maiuscole e minuscole e l’effettiva disponibilità. Q chiude un output impaginato o attivo, ma non termina automaticamente la sessione SSH.
Sequenza CLI sicura
- Iniziare con un account User locale se è sufficiente l’accesso in sola lettura.
- Controllare il dispositivo di destinazione, il prompt e la modalità di comando.
- Con
?, visualizzare i comandi disponibili in questa modalità. - Eseguire inizialmente soltanto operazioni di stato o visualizzazione.
- Prima di una modifica, acquisire lo stato effettivo completo e definire il rollback esatto.
- Modificare un solo elemento tecnico alla volta e verificarlo immediatamente.
- Se l’output è impaginato, tornare al prompt con
Q. - Terminare la sessione con il comando di uscita/logout indicato da
?sul dispositivo di destinazione, quindi verificare che la connessione SSH sia chiusa.
Trattare la cronologia dei comandi come informazione sensibile: può contenere indirizzi di gestione, nomi utente o parametri immessi. Non inserire mai password o token come argomenti della CLI; fornirli soltanto quando lo switch li richiede in modo interattivo.
API REST: login documentato
L’API è raggiungibile tramite HTTPS all’indirizzo di gestione dello switch. Lo switch crea un nuovo token Bearer per ogni sessione. Quando la sessione corrente viene chiusa, è necessario ottenere un nuovo token.
Il login documentato utilizza PATCH /api/system/login. L’esempio seguente riporta la sintassi pubblicata senza modifiche, con segnaposto neutri:
curl -k https://<Switch-IP address>/api/system/login -X PATCH -H 'Content-Type:application/json' -d '{"user":"admin","password":"your-password"}'
Una risposta di successo di esempio ha questa struttura:
{
"restful_res": {
"token": "xxxxxxxx.yyyyyyyy.zzzzzz",
"utctimestamp": "##########",
"timeout": 900,
"errCode": 0,
"message": "OK"
}
}
Si applicano le indicazioni seguenti:
tokenè un segreto con gli stessi requisiti di protezione di una password.utctimestampetimeoutsi riferiscono alla sessione specifica.- La risposta di esempio documentata mostra
timeout: 900; la descrizione statica non ne specifica l’unità. Per l’automazione, verificarne il significato nella guida API pubblicata e sul firmware effettivamente in uso, senza codificare il valore in modo rigido. errCode: 0emessage: "OK"indicano la risposta di successo mostrata. Inoltre, controllare lo stato HTTP.- Richiedere un nuovo token dopo la chiusura della sessione o se una sessione viene rifiutata o è scaduta; non riutilizzare un token precedente.
Modello cURL rafforzato
Il modello seguente evita password e token negli argomenti del processo, verifica il certificato TLS e non scrive segreti su file. Richiede curl, jq e una shell che supporti le here string:
read -r -p 'Utente locale dello switch: ' SW_USER
read -r -s -p 'Password locale dello switch: ' SW_PASSWORD
printf '\n'
LOGIN_RESPONSE="$({
printf '%s\n%s\n' "$SW_USER" "$SW_PASSWORD" |
jq -Rn '[inputs] | {user: .[0], password: .[1]}' |
curl --disable --silent --show-error --fail-with-body \
--cacert /path/to/switch-ca.pem \
--request PATCH \
--header 'Content-Type:application/json' \
--data-binary @- \
'https://<Switch-IP-address>/api/system/login'
})"
unset SW_PASSWORD
if [ "$(jq -r '.restful_res.errCode' <<<"$LOGIN_RESPONSE")" != "0" ]; then
printf '%s\n' 'Login API non riuscito.' >&2
unset LOGIN_RESPONSE SW_USER
exit 1
fi
SW_TOKEN="$(jq -er '.restful_res.token' <<<"$LOGIN_RESPONSE")" || exit 1
unset LOGIN_RESPONSE
case "$SW_TOKEN" in
''|*[!A-Za-z0-9._~+/=-]*)
printf '%s\n' 'Il login API non ha restituito un token Bearer valido.' >&2
unset SW_TOKEN SW_USER
exit 1
;;
esac
Sostituire /path/to/switch-ca.pem e <Switch-IP-address>. Utilizzare il nome host per cui è stato emesso il certificato. In una shell interattiva, assicurarsi che non sia attiva alcuna traccia di debug, come set -x. Non esporre le variabili tramite env, export, output di debug o core dump.
API REST: chiamata e verifica
La chiamata di esempio documentata legge le porte e passa il token nell’header di autorizzazione:
curl -k https://<Switch IP Address>/api/ports -H 'authorization:Bearer <Token>'
<Token> è il segnaposto canonico per il token Bearer. Questo esempio pubblicato non è quindi eseguibile: non sostituire <Token> con il token reale e non inserire un comando simile nella cronologia della shell. La chiamata eseguibile seguente usa la variabile SW_TOKEN impostata durante il login.
Per una sessione reale, utilizzare il token già protetto e verificare il certificato. --config - legge la configurazione di curl dallo standard input; in questo modo l’header di autorizzazione non viene passato come argomento del processo e non viene creato un file temporaneo:
printf 'header = "Authorization: Bearer %s"\n' "$SW_TOKEN" |
curl --disable --silent --show-error --fail-with-body \
--config - \
--cacert /path/to/switch-ca.pem \
'https://<Switch-IP-address>/api/ports'
GET /api/ports è il test funzionale iniziale appropriato perché la chiamata documentata richiede lo stato anziché apportare una modifica alla configurazione. Il test ha esito positivo soltanto se:
- la verifica del certificato e la connessione TLS hanno esito positivo;
- non viene restituito alcun errore HTTP;
- la risposta è sintatticamente e tecnicamente plausibile;
- numeri, nomi e stati attesi delle porte corrispondono al dispositivo di destinazione corretto;
- nell’output o nei log non compaiono credenziali o token.
curl --fail-with-body restituisce uno stato di errore in caso di errore HTTP, ma conserva il corpo della risposta per la diagnosi locale. Prima di condividerlo, verificare che non contenga token, indirizzi, numeri di serie o altri dati interni.
Controllare le chiamate API di scrittura
Eseguire chiamate di scrittura soltanto come modifiche approvate, limitate e reversibili. Non riutilizzare il payload di un altro modello, di un altro firmware o di un vecchio script non verificato nuovamente.
Per ogni chiamata di scrittura:
- Controllare metodo, percorso, parametri, tipi di dati e schema della risposta nello schema Swagger/OpenAPI del dispositivo di destinazione, confermandone la disponibilità per il firmware in esecuzione.
- Acquisire immediatamente prima lo stato interessato con un’operazione di lettura appropriata e conservarlo in modo sicuro.
- Inviare soltanto i campi minimi necessari; non presumere valori predefiniti sconosciuti.
- Operare su un solo switch e con un ambito ridotto e reversibile.
- Valutare lo stato HTTP e i campi specifici dell’applicazione, come
errCodeemessage. - Confermare lo stato con un
GETindipendente e, se opportuno, con un test funzionale. - Interrompersi in caso di scostamento; non inviare altre modifiche in un ciclo di tentativi.
Una connessione HTTP riuscita non dimostra da sola che la modifica abbia avuto esito positivo. Allo stesso modo, un corpo JSON plausibile non dimostra che il percorso dati previsto continui a funzionare. Le modifiche a porte, VLAN o gestione, ad esempio, devono essere verificate anche dal segmento di rete interessato.
Gestire in sicurezza il ciclo di vita dei token Bearer
- Generare: Ottenere un nuovo token per ogni sessione API tramite
PATCH /api/system/login. - Verificare: Controllare il risultato HTTP,
errCode,message, il campo del token e i valori della sessione senza visualizzare il token. - Utilizzare: Inviare il token soltanto nell’header
Authorization: Bearer <token>e soltanto allo switch previsto.<Token>indica il segnaposto canonico non eseguibile nell’esempio documentato; gli esempi eseguibili generano l’header daSW_TOKEN. - Limitare: Non esportare, conservare o condividere i token e non scriverli in file, Git, log CI o ticket. Usare una sessione controllata separata per ogni processo parallelo.
- Reagire: Se la sessione termina o viene rifiutata, non continuare con lo stesso token: creare una nuova sessione. Evitare cicli infiniti di login automatico.
- Chiudere: Eseguire l’operazione di logout documentata e autenticata
PATCH /api/system/logout. Anche qui l’intestazione arriva a curl tramite l’ingresso standard anziché tramite gli argomenti del processo:
printf 'header = "Authorization: Bearer %s"\n' "$SW_TOKEN" |
curl --disable --silent --show-error --fail-with-body \
--config - \
--cacert /path/to/switch-ca.pem \
--request PATCH \
'https://<Switch-IP-address>/api/system/logout'
- Eliminazione locale: Dopo il logout, rimuovere le variabili locali. Se non è più possibile eseguire il logout perché la connessione è interrotta o la sessione è già scaduta, eliminare comunque i segreti in locale e verificare la conclusione della sessione secondo le specifiche del firmware in uso:
unset SW_TOKEN SW_USER LOGIN_RESPONSE SW_PASSWORD
- Controllare: Verificare che la cronologia della shell, i file temporanei e i log dei processi non contengano segreti esposti accidentalmente. Considerare compromesso un token rivelato, terminare la sessione e non inviare altre chiamate con tale token.
Sophos Fusion Switch Management API a livello di tenant
Questa sezione non usa https://<Switch-IP-address>/api/.... L’API Fusion autentica un service principal sull’host regionale di Sophos Fusion (in precedenza Sophos Central) e opera sul tenant indicato. Account locali People, ruoli Admin/User, token di sessione locali e schema Swagger del dispositivo non si applicano.
Prerequisiti, ruoli e credenziali
Lo switch deve essere registrato nel tenant corretto e gestito da Sophos Fusion. La procedura eseguibile seguente vale esclusivamente per le credenziali API dirette del tenant (client_id e client_secret). Solo un Super Admin del tenant diretto può crearle in Global Settings > Access Control > API Credentials; il ruolo assegnato al service principal deve consentire gli accessi in lettura e scrittura necessari. Non usare credenziali Partner o Enterprise con questi esempi di shell. In tali casi, eseguire prima la procedura separata di selezione del tenant descritta in Gestire in sicurezza le credenziali API di Sophos Fusion, quindi tornare a questa procedura con credenziali emesse e verificate appositamente per il tenant diretto selezionato.
Conservare il client secret e il JWT in un archivio di segreti, mai in script, ticket, cronologia della shell o output CI. Sono necessari curl, jq, Bash, una finestra di modifica approvata, nonché l’ID e la regione del tenant, la lista corrente, la lista completa desiderata e un rollback documentato. Le credenziali non sostituiscono la licenza o la Support Subscription.
Autenticare il service principal e individuare l’host regionale
L’IDP emette il JWT con POST https://id.sophos.com/api/v2/oauth2/token. Con le credenziali dirette del tenant richieste in questa procedura, GET https://api.central.sophos.com/whoami/v1 restituisce i campi id e apiHosts.dataRegion. La procedura si interrompe se whoami non restituisce un’identità di tipo tenant. Non inviare mai un ID Partner o Organization come X-Tenant-ID; non dedurre l’host regionale e non copiarlo da un altro tenant.
read -r -p 'Service principal client ID: ' SP_CLIENT_ID
read -r -s -p 'Service principal client secret: ' SP_CLIENT_SECRET
printf '\n'
TOKEN_RESPONSE="$({
jq -rn --arg id "$SP_CLIENT_ID" --arg secret "$SP_CLIENT_SECRET" \
'"grant_type=client_credentials&client_id=\($id|@uri)&client_secret=\($secret|@uri)&scope=token"' |
curl --disable --silent --show-error --fail-with-body \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-binary @- \
'https://id.sophos.com/api/v2/oauth2/token'
})"
unset SP_CLIENT_SECRET
FUSION_TOKEN="$(jq -er '
select(.token_type == "bearer") |
.access_token | select(type == "string" and length > 0)
' <<<"$TOKEN_RESPONSE")" || exit 1
unset TOKEN_RESPONSE
WHOAMI_RESPONSE="$(
printf 'header = "Authorization: Bearer %s"\n' "$FUSION_TOKEN" |
curl --disable --silent --show-error --fail-with-body \
--config - \
'https://api.central.sophos.com/whoami/v1'
)"
UUID_RE='^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}$'
FUSION_TENANT_ID="$(jq -er --arg re "$UUID_RE" \
'select(.idType == "tenant") | .id | select(type == "string" and test($re))' \
<<<"$WHOAMI_RESPONSE")" || exit 1
FUSION_DATA_REGION="$(jq -er \
'.apiHosts.dataRegion | select(type == "string" and test("^https://api-[a-z0-9-]+\\.central\\.sophos\\.com$"))' \
<<<"$WHOAMI_RESPONSE")" || exit 1
unset WHOAMI_RESPONSE UUID_RE
Ogni richiesta relativa agli switch richiede entrambi gli header Authorization: Bearer <token> e X-Tenant-ID: <tenant-id>, oltre all’host <data-region> completo ricavato in precedenza. Gli stati di successo documentati sono 200 o 201; indicano soltanto che la richiesta è stata accettata.
Leggere e sostituire completamente i filtri MAC in sicurezza
GET /switch/v1/settings/mac-filtering legge la lista degli indirizzi bloccati del tenant. PUT /switch/v1/settings/mac-filtering non aggiunge elementi: macAddresses deve contenere l’intera lista desiderata e sostituisce quella esistente. {"macAddresses":[]} cancella tutte le voci.
L’esempio aggiunge un indirizzo sintetico 02:, amministrato localmente. Non pubblicare indirizzi MAC reali. Proteggere i file di lavoro, che contengono dati operativi.
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/settings/mac-filtering" \
> mac-filter-before.json
jq -e '.macAddresses | type == "array"' mac-filter-before.json >/dev/null || exit 1
jq '{macAddresses: (.macAddresses + ["02:00:00:00:00:51"] | unique)}' \
mac-filter-before.json > mac-filter-desired.json
jq -S '.macAddresses' mac-filter-before.json mac-filter-desired.json
Eseguire la scrittura soltanto dopo l’approvazione del diff completo. Dall’acquisizione dello stato iniziale dei task fino alla memorizzazione dell’ID del task correlato in modo univoco, nel tenant non deve essere in corso alcun’altra modifica macFilters:
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/tasks?type=macFilters&pageSize=50&pageTotal=true" \
> mac-filter-tasks-before.json
jq -e '
(.items | type == "array") and
(.pages.current == 1) and
(.pages.total >= 0) and (.pages.total <= 1) and
((.items | length) <= 50)
' mac-filter-tasks-before.json >/dev/null || exit 1
CHANGE_STARTED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
--request PUT \
--header 'Content-Type: application/json' \
--data-binary @mac-filter-desired.json \
"$FUSION_DATA_REGION/switch/v1/settings/mac-filtering" \
> mac-filter-put-response.json
Rilettura, polling dei task e verifica
Il successo di un PUT non dimostra che ogni switch abbia applicato la policy. Rileggere l’impostazione esatta e quindi interrogare GET /switch/v1/tasks. I task vengono eliminati dopo 30 giorni e non costituiscono un archivio di audit permanente. I filtri documentati sono type, pageSize e pageTotal. Poiché questa procedura non presuppone l’esistenza di un altro parametro di paginazione documentato, elabora al massimo la prima pagina completa di 50 task e si interrompe se pages.total > 1, anziché ignorare silenziosamente altre pagine.
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/settings/mac-filtering" \
> mac-filter-after.json
jq -e '.macAddresses | type == "array"' mac-filter-after.json >/dev/null || exit 1
diff -u \
<(jq -S '.macAddresses' mac-filter-desired.json) \
<(jq -S '.macAddresses' mac-filter-after.json) || exit 1
CHANGE_TASK_ID=''
CHANGE_TASK_DONE=false
for CHANGE_POLL_ATTEMPT in $(seq 1 30); do
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/tasks?type=macFilters&pageSize=50&pageTotal=true" \
> mac-filter-tasks.json
jq -e '
(.items | type == "array") and
(.pages.current == 1) and
(.pages.total >= 0) and (.pages.total <= 1) and
((.items | length) <= 50)
' mac-filter-tasks.json >/dev/null || exit 1
if [ -z "$CHANGE_TASK_ID" ]; then
jq -e -s --arg started "$CHANGE_STARTED_AT" '
def epoch: sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601;
[.[0].items[].id] as $before |
[.[1].items[] |
select(
.type == "macFilters" and
(.id as $id | ($before | index($id) | not)) and
((.createdAt | epoch) >= ($started | epoch)) and
((.updatedAt | epoch) >= ($started | epoch))
)
] | if length > 1 then error("più task corrispondenti") else . end
' mac-filter-tasks-before.json mac-filter-tasks.json \
> mac-filter-task-matches.json || exit 1
if jq -e 'length == 1' mac-filter-task-matches.json >/dev/null; then
CHANGE_TASK_ID="$(jq -er '.[0].id | strings | select(length > 0)' \
mac-filter-task-matches.json)" || exit 1
fi
fi
if [ -n "$CHANGE_TASK_ID" ]; then
jq -e --arg id "$CHANGE_TASK_ID" '
[.items[] | select(.id == $id)] |
select(length == 1) | .[0]
' mac-filter-tasks.json > mac-filter-task-current.json || exit 1
if jq -e '.status.pending > 0' mac-filter-task-current.json >/dev/null; then
:
else
jq -e '
(.status.total | type == "number") and (.status.total > 0) and
(.status.pending == 0) and (.status.failed == 0) and
((.status.noSupportSubscription // 0) == 0) and
(.status.succeeded == .status.total) and
(.switches | type == "array") and
((.switches | length) == .status.total) and
all(.switches[];
(.id | type == "string" and length > 0) and
(.status == "succeeded") and
(.error == null)
)
' mac-filter-task-current.json >/dev/null || exit 1
CHANGE_TASK_DONE=true
break
fi
fi
[ "$CHANGE_POLL_ATTEMPT" -lt 30 ] && sleep 10
done
[ "$CHANGE_TASK_DONE" = true ] || exit 1
jq '{id, type, createdAt, updatedAt, status, switches}' mac-filter-task-current.json
La procedura correla esattamente un nuovo task usando CHANGE_STARTED_AT, type: "macFilters" e gli ID acquisiti prima del PUT. Interroga quindi la raccolta al massimo 30 volte, a intervalli di dieci secondi, selezionando esclusivamente l’ID del task memorizzato. Il successo richiede sia lo stato aggregato positivo sia lo stato terminale succeeded per ogni elemento di switches[]; ambiguità, paginazione, timeout, errori e noSupportSubscription causano l’interruzione. Il PUT non viene ripetuto.
Gestire con precisione gli errori dell’API Fusion
- 401/403: Controllare scadenza JWT, service principal, contesto tenant e header; i ruoli locali non aiutano.
- Tenant o host regionale errato: Ricavare nuovamente ID e host da
whoamio dalla lista dei tenant gestiti. - HTTP 200/201 senza effetto: Controllare la rilettura e i task; continuare il polling limitato senza ripetere il PUT alla cieca.
noSupportSubscription: Correggere Support Subscription e stato del dispositivo nel tenant giusto; nessuna soluzione locale.- Codice
10905–Duplicate MAC filter policy: Esaminareswitches[].error, rileggere lo stato e risolvere la richiesta duplicata o obsoleta; non effettuare tentativi alla cieca. - Codice
10906–MAC filter list is exhausted: Fermarsi e approvare una lista completa più piccola; non inviare mai un sottoinsieme come se fosse un’aggiunta. - Codice
10908–MAC address already allowed in the static MAC table: Risolvere il conflitto e valutarne l’impatto; non rimuovere voci consentite senza verifica.
In caso di errore registrare insieme stato HTTP, ID task, ID switch, status, error, message e code, oscurando i dati sensibili.
Limiti di rollback e ripristino
Il rollback è un altro PUT completo della lista salvata. Rileggere prima ed escludere modifiche concorrenti, quindi eseguire la stessa rilettura e verifica dei task fino allo stato finale di ogni switch.
# Prima della nuova lettura, garantire una finestra di modifica esclusiva.
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/settings/mac-filtering" \
> mac-filter-pre-restore.json
jq -e '.macAddresses | type == "array"' mac-filter-pre-restore.json >/dev/null || exit 1
diff -u \
<(jq -S '.macAddresses' mac-filter-desired.json) \
<(jq -S '.macAddresses' mac-filter-pre-restore.json) || exit 1
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/tasks?type=macFilters&pageSize=50&pageTotal=true" \
> mac-filter-restore-tasks-before.json
jq -e '
(.items | type == "array") and
(.pages.current == 1) and
(.pages.total >= 0) and (.pages.total <= 1) and
((.items | length) <= 50)
' mac-filter-restore-tasks-before.json >/dev/null || exit 1
jq '{macAddresses}' mac-filter-before.json > mac-filter-restore.json
RESTORE_STARTED_AT="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
--request PUT \
--header 'Content-Type: application/json' \
--data-binary @mac-filter-restore.json \
"$FUSION_DATA_REGION/switch/v1/settings/mac-filtering" \
> mac-filter-restore-response.json
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/settings/mac-filtering" \
> mac-filter-restored.json
jq -e '.macAddresses | type == "array"' mac-filter-restored.json >/dev/null || exit 1
diff -u \
<(jq -S '.macAddresses' mac-filter-restore.json) \
<(jq -S '.macAddresses' mac-filter-restored.json) || exit 1
RESTORE_TASK_ID=''
RESTORE_TASK_DONE=false
for RESTORE_POLL_ATTEMPT in $(seq 1 30); do
printf 'header = "Authorization: Bearer %s"\nheader = "X-Tenant-ID: %s"\n' \
"$FUSION_TOKEN" "$FUSION_TENANT_ID" |
curl --disable --silent --show-error --fail-with-body \
--config - \
"$FUSION_DATA_REGION/switch/v1/tasks?type=macFilters&pageSize=50&pageTotal=true" \
> mac-filter-restore-tasks.json
jq -e '
(.items | type == "array") and
(.pages.current == 1) and
(.pages.total >= 0) and (.pages.total <= 1) and
((.items | length) <= 50)
' mac-filter-restore-tasks.json >/dev/null || exit 1
if [ -z "$RESTORE_TASK_ID" ]; then
jq -e -s --arg started "$RESTORE_STARTED_AT" '
def epoch: sub("\\.[0-9]+Z$"; "Z") | fromdateiso8601;
[.[0].items[].id] as $before |
[.[1].items[] |
select(
.type == "macFilters" and
(.id as $id | ($before | index($id) | not)) and
((.createdAt | epoch) >= ($started | epoch)) and
((.updatedAt | epoch) >= ($started | epoch))
)
] | if length > 1 then error("più task di rollback corrispondenti") else . end
' mac-filter-restore-tasks-before.json mac-filter-restore-tasks.json \
> mac-filter-restore-task-matches.json || exit 1
if jq -e 'length == 1' mac-filter-restore-task-matches.json >/dev/null; then
RESTORE_TASK_ID="$(jq -er '.[0].id | strings | select(length > 0)' \
mac-filter-restore-task-matches.json)" || exit 1
fi
fi
if [ -n "$RESTORE_TASK_ID" ]; then
jq -e --arg id "$RESTORE_TASK_ID" '
[.items[] | select(.id == $id)] |
select(length == 1) | .[0]
' mac-filter-restore-tasks.json > mac-filter-restore-task-current.json || exit 1
if jq -e '.status.pending > 0' mac-filter-restore-task-current.json >/dev/null; then
:
else
jq -e '
(.status.total | type == "number") and (.status.total > 0) and
(.status.pending == 0) and (.status.failed == 0) and
((.status.noSupportSubscription // 0) == 0) and
(.status.succeeded == .status.total) and
(.switches | type == "array") and
((.switches | length) == .status.total) and
all(.switches[];
(.id | type == "string" and length > 0) and
(.status == "succeeded") and
(.error == null)
)
' mac-filter-restore-task-current.json >/dev/null || exit 1
RESTORE_TASK_DONE=true
break
fi
fi
[ "$RESTORE_POLL_ATTEMPT" -lt 30 ] && sleep 10
done
[ "$RESTORE_TASK_DONE" = true ] || exit 1
jq '{id, type, createdAt, updatedAt, status, switches}' \
mac-filter-restore-task-current.json
unset FUSION_TOKEN SP_CLIENT_ID FUSION_TENANT_ID FUSION_DATA_REGION \
CHANGE_STARTED_AT CHANGE_TASK_ID RESTORE_STARTED_AT RESTORE_TASK_ID
mac-filter-before.json è soltanto un’istantanea dell’impostazione, non un backup completo. Se manca lo stato iniziale, non usare mai una lista vuota come ripristino: cancellerebbe tutti i blocchi. Questa API non ripristina la configurazione CLI/REST locale e non revoca l’isolamento Active Threat Response. Infine, eliminare le variabili del token, proteggere o cancellare i file e documentare il risultato.
Risolvere gli errori in base al sintomo
Errore TLS o certificato
- Verificare il nome di gestione, IP, validità, catena di certificazione e tempo di sistema.
- Fornire la CA corretta con
--cacertoppure sostituire il certificato del dispositivo tramite il processo di gestione previsto. - Non usare
-kcome soluzione permanente. Cifra il trasporto, ma non autentica lo switch. - Se il certificato o la chiave host SSH cambia, verificare innanzitutto che ci si stia collegando allo switch previsto.
Connessione respinta, timeout o nessun percorso
- Controllare IP di gestione, VLAN di gestione, routing, ACL e abilitazione del servizio.
- Test da un host autorizzato nella rete di gestione prevista.
- Non bypassare aprendo il servizio per tutte le reti o Internet.
- Se una modifica appena eseguita ha interrotto l’accesso, utilizzare il percorso di gestione indipendente e il rollback predisposto.
Il login non riesce
- Assicurarsi che venga utilizzato un account locale dello switch, non un account Sophos Fusion.
- Controllare nome utente, requisiti di password, stato account e ruolo locale.
- Non effettuare tentativi automatici ripetuti: potrebbero bloccare l’account e nascondere la causa.
- Per l’account predefinito
admin, ricordare che la password può essere cambiata soltanto dall’accountadminstesso o da Sophos Fusion.
HTTP 401 o 403
- Per
401, controllare il token e la sessione, quindi eseguire nuovamente il login una sola volta. - Per
403, controllare il ruolo locale e l’autorizzazione per l’operazione; non aumentare indiscriminatamente i privilegi. - Provare al massimo un nuovo token. Se l’errore persiste, conservare in modo sicuro la risposta e lo stato del firmware, consultare la guida API pubblicata e confrontare modello e firmware del dispositivo di destinazione.
HTTP 400, 404 o 405
- Confrontare percorso, metodo, header e JSON con la guida API pubblicata e convalidarli per il modello e il firmware del dispositivo di destinazione.
- Controllare maiuscole, minuscole e prefisso
/api. - Un
404o405può indicare un endpoint non disponibile o definito diversamente nel firmware in uso. Non tentare altre operazioni di scrittura simili.
Errore HTTP con corpo di risposta o errCode diverso da 0
- Acquisire lo stato HTTP e il corpo della risposta.
- Documentare
message, oscurare le informazioni interne prima della condivisione e non ripetere alla cieca una modifica identica. - Se non è chiaro se una modifica sia stata applicata parzialmente, determinare prima lo stato reale tramite
GET, visualizzazione CLI e test funzionale.
Comando CLI mancante o respinto
- Con
?controllare se il comando esiste nella modalità corrente. - Con
TABcompletare la sintassi offerta sul dispositivo. - Controllare il ruolo locale, la modalità di comando, il modello e il firmware.
- Non usare un comando dal nome simile preso da un altro firmware.
Rollback, disconnessione e chiusura della sessione
Un semplice test GET /api/ports non modifica la configurazione e non richiede un rollback tecnico. Il login API crea comunque una sessione: terminarla secondo il ciclo di vita del token ed eliminare le variabili locali.
Per una modifica della configurazione, definire il rollback in anticipo:
- Esportare lo stato effettivo e gli oggetti interessati, oppure registrarli tramite operazioni di lettura.
- Preparare l’esatta operazione inversa in base alla guida API pubblicata e alla relativa convalida per il firmware in uso, oppure in base alla guida CLI del dispositivo di destinazione.
- Definire i criteri per un rollback immediato, come la perdita dell’accesso di gestione, dell’uplink, della raggiungibilità VLAN o dell’alimentazione PoE.
- In caso di errore, non tentare ulteriori correzioni: ripristinare il valore precedente tramite il percorso ancora funzionante.
- Successivamente, verificare di nuovo l’accesso di gestione, lo stato delle porte, l’uplink e i servizi interessati.
- Se il percorso normale non è più disponibile, eseguire il rollback tramite il percorso di gestione indipendente verificato in precedenza, eventualmente usando l’accesso alla console previsto dal modello. Un ripristino delle impostazioni di fabbrica non è un normale rollback, perché cancella la configurazione.
Per uno switch gestito da Sophos Fusion, il solo rollback locale non è sufficiente. Controllare lo stato di destinazione autorevole in Sophos Fusion e riportare lì in modo coerente una modifica di emergenza approvata, oppure rimuoverla completamente in locale. Non alternare modifiche tra il canale locale e quello centrale.
Per concludere,
- chiudere un output CLI con
Qe usare al prompt il comando di uscita/logout indicato dal dispositivo; - chiudere la sessione API con il comando documentato
PATCH /api/system/logout; - scartare le variabili di token e password locali con
unset; - controllare che nessun file temporaneo o log di debug contenga segreti;
- documentare nel ticket risultato, firmware, percorso amministrativo usato, verifica ed eventuale rollback.
Rafforzamento della sicurezza
- VLAN di gestione dedicata, con ACL limitata a pochi host amministrativi e ai protocolli necessari.
- Attivare HTTPS e SSH solo quando necessario; disabilitare i servizi di gestione insicuri o non utilizzati.
- Utilizzare certificati affidabili e le chiavi di host SSH verificate.
- Utilizzare account locali personali: User per il solo accesso in lettura e Admin soltanto per modifiche approvate.
- Sostituire immediatamente la password predefinita; evitare password comuni e ruotare dopo modifiche del personale o del fornitore di servizi.
- Tenere i segreti API fuori da codice sorgente, file
.env, cronologia della shell, argomenti dei processi e output CI. - Non eseguire client API con il debug esteso degli header mentre è impostato un header di autorizzazione.
- Mantenere brevi le sessioni, utilizzare un nuovo token per ogni sessione e infine eliminare le variabili.
- Conservare backup della configurazione protetti e recuperabili al di fuori dello switch.
- Correlare temporalmente log locali, eventi centrali e ticket di modifica; verificare l’ora dello switch.
- Riesaminare periodicamente account locali, ACL di gestione, certificati, chiavi SSH e accessi di automazione.
Variabilità del firmware e del modello
La pagina statica dell’API REST documenta il percorso di login /api/system/login, il test di lettura /api/ports e l’header di autorizzazione; la guida API pubblicata documenta anche il percorso di logout /api/system/logout. Queste risorse centrali forniscono esempi e indicazioni, ma non uno schema universalmente applicabile a tutti i dispositivi. Lo schema Swagger/OpenAPI specifico viene generato dallo switch di destinazione in esecuzione e riflette modello e firmware. La documentazione CLI pubblicata descrive inoltre gli strumenti di aiuto della CLI citati sopra, ma non garantisce che ogni altro comando sia identico su tutti i firmware.
Prima della produzione, quindi, per modello e firmware:
- Documentare la versione firmware e il modello hardware esatto;
- Controllare la modalità CLI e la sintassi con
?eTAB; - controllare nello schema Swagger/OpenAPI metodi, percorsi e schemi forniti dal dispositivo di destinazione per il firmware in esecuzione;
- provare prima il login e
GET /api/portsin una sessione controllata; - convalidare qualsiasi automazione di scrittura rispetto a questo specifico schema di destinazione e anche in un ambiente non produttivo o chiaramente circoscritto;
- dopo gli aggiornamenti del firmware, verificare nuovamente login, controllo dei certificati, schema della risposta, gestione dei token e tutti gli endpoint utilizzati;
- in caso di scostamenti, non «adattare lo script» prima di avere compreso la nuova semantica e il rollback.
Verifica finale
- Modello, firmware e IP di gestione corretti confermati.
- Sistema autorevole, Sophos Fusion o gestione locale, documentato.
- Account locale User o Admin utilizzato in base all’attività.
- Certificato TLS o chiave host SSH verificati; nessuna opzione permanente di esclusione della verifica.
- Token usato soltanto per la sessione e mai esposto.
- Stato HTTP,
errCode,messagee stato tecnico controllati. - Per le modifiche, stato effettivo e rollback acquisiti e accesso indipendente disponibile.
- Risultato verificato tramite
GET, visualizzazione CLI e test funzionale richiesto. - Sessione chiusa, variabili eliminate e log controllati per individuare eventuali segreti.
- Eccezione locale rispetto a Sophos Fusion rimossa e documentazione nel ticket completata.