Administrar Sophos Switch mediante CLI, API REST local y API de Fusion
La CLI local y la API REST local acceden directamente a un Sophos Switch. La API de gestión de switches de Sophos Fusion es independiente: utiliza credenciales de service principal a nivel de tenant y distribuye políticas centrales a los switches. Este runbook identifica la identidad, la URL base y la validación aplicables a ambas vías.
Alcance y decisión básica segura
Este runbook cubre lo siguiente:
- acceso local mediante CLI a través de una vía de gestión habilitada para el dispositivo, en particular SSH;
- orientación y diagnóstico en la CLI;
- inicio de sesión en la API REST local del dispositivo;
- ciclo de vida del token Bearer asociado a la sesión;
- una prueba API documentada y sin modificaciones con
GET /api/ports; - verificación segura, resolución de problemas, reversión y fin de sesión.
No pretende ser un catálogo completo de endpoints de API ni de comandos de CLI. La documentación central sirve de orientación, pero antes de trabajar con la API debe comprobarse su disponibilidad en el dispositivo de destino. Las rutas indicadas allí, como /ports, son relativas a la base del servidor /api; por tanto, la ruta completa es /api/ports. Los comandos, modos y parámetros de CLI también pueden variar según el modelo y el firmware. Consulte la clasificación vinculante en la sección Variabilidad del firmware y del modelo.
Antes de acceder, determine lo siguiente:
- ¿El sistema de referencia es Sophos Fusion o la administración local?
- ¿Qué switch específico, qué modelo, qué firmware y qué IP de administración se ven afectados?
- ¿Es suficiente el acceso de solo lectura o se requiere un cambio aprobado?
- ¿Cuál es el estado actual, cuál es el criterio de éxito y cómo se realizará la reversión?
- ¿Existe una vía de gestión independiente si el cambio interrumpe el acceso normal?
No mezclar identidades y roles
Las cuentas de People son cuentas locales del switch:
| Tipo de privilegio local | Permisos en el switch | Uso típico |
|---|---|---|
| Admin | Ver y modificar todas las funciones del switch | Administración local aprobada y llamadas de escritura a la API |
| User | Ver la configuración, pero no modificarla | Diagnóstico y control conforme al principio de mínimo privilegio |
Estos roles locales no son los mismos que los roles de administrador en Sophos Fusion. Una función de Fusion no otorga automáticamente privilegios CLI o API locales, y las credenciales locales no son credenciales de Fusion. Las credenciales de una cuenta de switch local se envían a /api/system/login.
Las cuentas locales se administran en la interfaz local, en People:
- Seleccione Add para crear una cuenta o Edit junto a una cuenta existente.
- Defina Username, Password y Privilege type.
- En Privilege type, seleccione Admin o User.
- Guarde con Apply.
Una contraseña local debe cumplir todos los requisitos siguientes:
- un mínimo de 10 y un máximo de 32 caracteres;
- al menos una letra y un número;
- al menos uno de estos caracteres especiales:
@ ~ % * # + - =.
Una cuenta local Admin puede cambiar las contraseñas de otras cuentas, pero no la contraseña de la cuenta predeterminada admin. Solo la propia cuenta admin o Sophos Fusion pueden cambiarla. Para el funcionamiento habitual, utilice cuentas locales personales en lugar de compartir la cuenta admin.
Preparar acceso
Antes de una sesión:
- Verifique la IP de administración, el modelo, la versión de firmware, la ubicación y el número de serie con el ticket de cambio.
- Permita el acceso solo desde una red de gestión administrativa y desde un host de administración autorizado.
- No haga que los servicios de administración sean accesibles desde las VLAN de los usuarios o desde Internet.
- Verifique la fuente horaria y el host de administración; una hora incorrecta dificulta el análisis de registros y de la API.
- En caso de un cambio, tenga disponible una copia de seguridad de la configuración actual y una ruta de regreso independiente.
- Si utiliza la gestión mediante Fusion, guarde el estado objetivo central y documente expresamente la excepción local.
- Nunca almacene credenciales o tokens Bearer en el texto del ticket, chat, captura de pantalla, historial de shell o código fuente.
Se deben reemplazar los siguientes marcadores de posición:
| Marcador de posición | Significado |
|---|---|
<Switch-IP address> | IP de administración o un nombre de administración resuelto de confianza del dispositivo de destino |
<LOCAL-USERNAME> | Cuenta local del switch, no usuario de Fusion |
your-password | contraseña local; solo un marcador de posición, nunca lo use como contraseña real |
xxxxxxxx.yyyyyyyy.zzzzzz | Ejemplo enmascarado de un token Bearer, no un token real |
Utilice CLI de forma segura
Establecer conexión
SSH forma parte de los servicios de gestión local. Debe configurarse en el firmware utilizado y ser accesible desde la red de gestión. Una llamada de cliente general es:
ssh <LOCAL-USERNAME>@<Switch-IP-address>
La notación es un ejemplo de cliente: sustituya por completo <LOCAL-USERNAME> y <Switch-IP-address>, incluidos los corchetes angulares. Antes de confirmar la clave de host, compare su huella digital por una vía independiente y de confianza. No ignore una advertencia sobre una clave de host modificada; descarte primero la sustitución del dispositivo, un restablecimiento de fábrica, un conflicto de IP o un posible ataque de intermediario.
Si el modelo concreto dispone de acceso físico a la consola, puede utilizarlo como vía de mantenimiento independiente. La conexión y los parámetros serie deben corresponder al modelo; no los copie de otro modelo de Sophos Switch.
Orientarse en la CLI
Observe primero el prompt actual y, por tanto, el modo de comandos activo. No dé por hecho que un comando está disponible en todos los modos. Las ayudas y teclas documentadas son:
| Entrada | Efecto |
|---|---|
? | Listar los comandos disponibles |
TAB | Completar el comando |
| Flecha arriba/abajo | Ver los comandos ejecutados previamente |
| Flecha izquierda/derecha | Navegar por la línea actual |
Backspace o Ctrl + H | Eliminar un carácter |
History | Ver la lista del historial de comandos |
Q | Salir de una visualización y volver al prompt del switch |
Verifique la sensibilidad a mayúsculas/minúsculas y la disponibilidad exacta con ? o TAB en el dispositivo de destino. Q finaliza una salida paginada o activa y no es automáticamente el final de la sesión SSH.
Flujo CLI seguro
- Comience con una cuenta local User si basta con acceso de solo lectura.
- Verifique el dispositivo de destino, el prompt y el modo de comandos.
- Utilice
?para mostrar los comandos disponibles en este modo. - En primer lugar, realice únicamente operaciones de visualización o estado.
- Antes de realizar un cambio, registre el estado actual completo y el procedimiento exacto de reversión.
- Cambie solo un aspecto técnico a la vez y compruébelo inmediatamente.
- Si la salida está paginada, vuelva al prompt con
Q. - Finalice la sesión limpiamente con el comando salir/cerrar sesión que se muestra en el dispositivo de destino en
?y luego verifique la conexión SSH.
Trate el historial de comandos como información confidencial: puede contener direcciones de administración, nombres de usuarios o parámetros ingresados. Nunca ingrese contraseñas y tokens como parámetros CLI a menos que el switch los solicite de forma interactiva.
API REST: inicio de sesión documentado
Se puede acceder a la API a través de HTTPS en la dirección de administración del switch. El switch crea un nuevo token Bearer para cada sesión. Si se cierra la sesión actual, se debe obtener un nuevo token.
El inicio de sesión documentado utiliza PATCH /api/system/login. El siguiente ejemplo muestra la sintaxis publicada sin cambios con marcadores de posición neutrales:
curl -k https://<Switch-IP address>/api/system/login -X PATCH -H 'Content-Type:application/json' -d '{"user":"admin","password":"your-password"}'
Un ejemplo de respuesta exitoso tiene esta estructura:
{
"restful_res": {
"token": "xxxxxxxx.yyyyyyyy.zzzzzz",
"utctimestamp": "##########",
"timeout": 900,
"errCode": 0,
"message": "OK"
}
}
Se aplica lo siguiente:
tokenes un secreto con los mismos requisitos de protección que una contraseña.utctimestampytimeoutpertenecen a la sesión específica.- La respuesta de ejemplo documentada muestra
timeout: 900; la descripción estática no especifica la unidad. Para la automatización, compruebe su significado en la ayuda publicada de la API, valídelo para el firmware que se utiliza realmente y no lo codifique de forma fija. errCode: 0ymessage: "OK"indican la respuesta exitosa mostrada. Verifique además el estado de HTTP.- Solicite un token nuevo cuando termine la sesión o si esta se rechaza o caduca; no reutilice un token antiguo.
Patrón de cURL reforzado
El siguiente patrón evita incluir la contraseña y el token en los argumentos del proceso, verifica el certificado TLS y no crea ningún archivo con secretos. Requiere curl, jq y un shell compatible con here-strings:
read -r -p 'Usuario local del switch: ' SW_USER
read -r -s -p 'Contraseña local del 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' 'Error al iniciar sesión en la API.' >&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' 'El inicio de sesión en la API no devolvió un token Bearer válido.' >&2
unset SW_TOKEN SW_USER
exit 1
;;
esac
Reemplace /path/to/switch-ca.pem y <Switch-IP-address>. Utilice el nombre de host para el que se emite el certificado. En un shell interactivo, asegúrese de antemano de que no esté activo ningún seguimiento de depuración como set -x. No exponga variables con env, export, resultados de depuración o volcados de núcleo.
API REST: llamadas y pruebas
La llamada de ejemplo documentada lee los puertos y pasa el token en el encabezado de autorización:
curl -k https://<Switch IP Address>/api/ports -H 'authorization:Bearer <Token>'
Aquí <Token> es deliberadamente solo un marcador de posición para el token Bearer. Por tanto, este ejemplo publicado no es ejecutable: no sustituya <Token> por el token real ni introduzca ese comando en el historial del shell. La siguiente llamada ejecutable utiliza en su lugar la variable SW_TOKEN definida durante el inicio de sesión.
Para una sesión real, utilice el token ya protegido y verifique el certificado. --config - lee la configuración de curl desde la entrada estándar; de este modo, el encabezado de autorización no se pasa como argumento del proceso y no se crea ningún archivo temporal:
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 es la primera prueba funcional adecuada porque la llamada documentada consulta el estado en lugar de demostrar un cambio de configuración. La prueba solo se considera correcta si:
- La verificación TLS y la conexión se completan correctamente;
- no se devuelve ningún error HTTP;
- la respuesta es sintáctica y técnicamente plausible;
- el número de puerto, los nombres de los puertos y los estados esperados coinciden con los del dispositivo de destino correcto;
- no aparecen credenciales ni tokens en la salida ni en los registros.
curl --fail-with-body proporciona un estado de error para errores HTTP, pero conserva el cuerpo de la respuesta para el diagnóstico local. Antes de compartirlo, revíselo para detectar tokens, direcciones, números de serie y otros datos internos.
Controlar las llamadas de escritura a la API
Realice llamadas de escritura solo como cambios aprobados, limitados y reversibles. No reutilice una carga útil de un modelo, firmware o script anterior diferente sin verificarlo.
Lo siguiente se aplica a cada llamada de escritura:
- En el esquema Swagger/OpenAPI del dispositivo de destino, verifique el método, la ruta, los parámetros, los tipos de datos y el esquema de respuesta, y confirme que estén disponibles en la versión de firmware en ejecución.
- Capture el estado afectado inmediatamente antes con una operación de lectura adecuada y guárdelo de forma segura.
- Envíe solo los campos imprescindibles; no presuponga valores predeterminados desconocidos.
- Limite el alcance a un único switch y a un cambio pequeño y reversible.
- Evalúe el estado HTTP, así como los campos específicos de la aplicación, como
errCodeymessage. - Confirme la condición con un
GETindependiente y, cuando sea relevante, con una prueba funcional. - Deténgase ante cualquier desviación; no envíe más cambios mediante un bucle de reintentos.
Una conexión HTTP exitosa por sí sola no demuestra un cambio exitoso. Del mismo modo, un cuerpo JSON plausible no prueba que la ruta de datos prevista siga funcionando. Por ejemplo, los cambios de puerto, VLAN o administración también deben probarse desde el segmento de red afectado.
Gestionar de forma segura los tokens Bearer durante todo su ciclo de vida
- Crear: Obtenga un nuevo token para cada sesión de API a través de
PATCH /api/system/login. - Verificar: Verifique el resultado HTTP,
errCode,message, el campo del token y los valores de la sesión sin mostrar el token. - Usar: Envíe el token únicamente en el encabezado
Authorization: Bearer <token>y solo al switch previsto.<Token>es exclusivamente un marcador de posición no ejecutable; los ejemplos ejecutables generan el encabezado a partir deSW_TOKEN. - Límite: No exporte, persista, comparta ni escriba tokens en archivos, Git, registros de CI ni tickets. Utilice una sesión controlada independiente para cada trabajo paralelo.
- Renovar: Cuando finalice o se rechace la sesión, no continúe trabajando con el mismo token; cree una sesión nueva. Evite los bucles automáticos interminables de inicio de sesión.
- Cerrar: Llame a la operación de cierre de sesión documentada y autenticada mediante
PATCH /api/system/logout. También aquí, el encabezado se pasa a curl por la entrada estándar, no mediante los argumentos del proceso:
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'
- Descartar localmente: Elimine las variables locales después de cerrar sesión. Si ya no es posible cerrar sesión debido a una conexión interrumpida o a una sesión no válida, descarte los secretos localmente y verifique el fin de la sesión según las especificaciones del firmware utilizado:
unset SW_TOKEN SW_USER LOGIN_RESPONSE SW_PASSWORD
- Verificar: Verifique el historial del shell, los archivos temporales y los registros de trabajos en busca de secretos accidentales. Trate un token revelado como comprometido, finalice la sesión y deje de realizar más llamadas con él.
API de gestión de switches de Sophos Fusion a nivel de tenant
Esta sección no utiliza https://<Switch-IP-address>/api/.... La API de Fusion autentica un service principal en el host regional de Sophos Fusion (antes Sophos Central) y actúa sobre el tenant indicado. Aquí no se aplican las cuentas locales de People, los roles Admin/User, los tokens de sesión locales ni el esquema Swagger específico del dispositivo.
Requisitos, roles y datos de acceso
El switch debe estar registrado en el tenant correcto y gestionado por Sophos Fusion. El siguiente flujo ejecutable se aplica exclusivamente a las credenciales de API directas de este tenant (client_id y client_secret). Solo un Super Admin del tenant directo puede crearlas en Global Settings > Access Control > API Credentials; el rol asignado al service principal debe permitir el acceso de lectura y escritura necesario. No utilice credenciales de Partner o Enterprise con estos ejemplos de shell. En esos casos, siga primero el proceso independiente de selección del tenant descrito en Gestionar de forma segura las credenciales de la API de Sophos Fusion y vuelva a este procedimiento únicamente con credenciales emitidas y verificadas específicamente para el tenant directo seleccionado.
Guarde el secreto y el JWT en un almacén de secretos, nunca en scripts, tickets, el historial del shell ni salidas de CI. Se requieren curl, jq, un shell Bash, una ventana de cambio aprobada y documentación del ID del tenant, la región, la lista actual, la lista objetivo completa y la reversión. Las credenciales de API no sustituyen una licencia ni una Support Subscription.
Autenticar el service principal y obtener el host regional
El IDP emite el JWT mediante POST https://id.sophos.com/api/v2/oauth2/token. Con las credenciales directas de tenant requeridas aquí, GET https://api.central.sophos.com/whoami/v1 devuelve los campos id y apiHosts.dataRegion. El procedimiento se interrumpe si whoami no devuelve una identidad de tenant. Nunca envíe un ID de Partner u Organization como X-Tenant-ID; no adivine el host regional ni lo copie de otro tenant.
read -r -p 'ID de cliente del service principal: ' SP_CLIENT_ID
read -r -s -p 'Secreto de cliente del service principal: ' 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
Cada solicitud a la API de switches requiere los encabezados Authorization: Bearer <token> y X-Tenant-ID: <tenant-id>, además del host <data-region> completo obtenido anteriormente. Los estados de éxito documentados son 200 o 201; solo demuestran que la solicitud se ha aceptado.
Leer y sustituir de forma segura todos los filtros MAC
GET /switch/v1/settings/mac-filtering lee la lista de bloqueo de todo el tenant. PUT /switch/v1/settings/mac-filtering no añade elementos: macAddresses debe contener la lista objetivo completa y sustituye la lista existente. {"macAddresses":[]} elimina todas las entradas de bloqueo.
El ejemplo añade una dirección MAC sintética con el prefijo 02:, administrada localmente. No publique una dirección MAC real de un cliente. Los archivos de trabajo contienen datos operativos y deben protegerse.
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
Realice la escritura solo después de aprobar el diff completo. Desde que se captura el estado de referencia de las tareas hasta que se guarda el ID de la tarea correlacionada de forma unívoca, no debe ejecutarse ningún otro cambio de macFilters en el tenant:
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
Relectura, sondeo de tareas y validación
Un PUT correcto no demuestra que todos los switches hayan aplicado la política. Vuelva a leer la configuración para comprobar que coincida exactamente y consulte después GET /switch/v1/tasks. Las tareas se eliminan a los 30 días y no constituyen un archivo de auditoría permanente. Los filtros documentados son type, pageSize y pageTotal. Como no se presupone ningún otro parámetro de paginación documentado, el procedimiento procesa como máximo la primera página completa de 50 tareas y se interrumpe si pages.total > 1, en lugar de ignorar silenciosamente otras páginas.
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("varias tareas coincidentes") 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
El procedimiento correlaciona exactamente una tarea nueva mediante CHANGE_STARTED_AT, type: "macFilters" y los ID de tarea guardados antes del PUT. A continuación, consulta la colección un máximo de 30 veces a intervalos de diez segundos y selecciona exclusivamente el ID de tarea guardado. Para considerar correcta la operación, se requiere un estado agregado satisfactorio y el estado terminal succeeded en cada entrada de switches[]. La ambigüedad, la paginación, el tiempo de espera agotado, los errores y noSupportSubscription provocan la interrupción. El PUT no se repite.
Tratar de forma específica los errores de la API de Fusion
- 401/403: Verifique la caducidad del JWT, el service principal, el contexto del tenant y los encabezados; los roles locales no sirven en este caso.
- Tenant o host de región incorrecto: Vuelva a obtener el ID del tenant y el host a partir de
whoamio de la lista de tenants administrados. - HTTP 200/201 sin efecto: Verifique la relectura y la tarea correspondiente; continúe el sondeo de forma limitada, pero no repita el PUT a ciegas.
noSupportSubscription: Corrija la Support Subscription y el estado del dispositivo en el tenant correcto; no existe una solución local.- Código
10905–Duplicate MAC filter policy: Verifiqueswitches[].error, vuelva a leer el estado y resuelva la solicitud duplicada u obsoleta; no reintente a ciegas. - Código
10906–MAC filter list is exhausted: Deténgase y apruebe una lista completa más pequeña; nunca envíe un subconjunto como si fuera una adición. - Código
10908–MAC address already allowed in the static MAC table: Aclare el conflicto y su impacto en la seguridad; no elimine entradas permitidas estáticas sin revisarlas.
En caso de error, recopile conjuntamente el estado HTTP, el ID de la tarea, el ID del switch, status, error, message y code, y oculte los datos confidenciales.
Límites de la reversión y la restauración
La reversión consiste en otro PUT completo de la lista guardada. Vuelva a leerla primero y descarte cambios simultáneos; a continuación, realice la misma relectura y el mismo flujo de tareas hasta alcanzar el estado terminal de cada switch.
# Mantenga una ventana de cambio exclusiva antes de volver a leer.
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("varias tareas de reversión coincidentes") 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 es solo una instantánea de esta configuración, no una copia de seguridad completa del switch. Si pierde el estado de referencia, nunca envíe una lista vacía para restablecerla: se eliminarían todas las entradas de bloqueo. Esta API no restaura la configuración local de CLI/REST ni anula el aislamiento de Active Threat Response. Para finalizar, elimine las variables de token, proteja o borre de forma segura los archivos y documente el resultado de la tarea.
Tratar los errores por síntoma
Error de certificado o TLS
- Verifique el nombre de administración, la IP, la validez, la cadena de certificados y la hora del sistema.
- Especifique la CA correcta mediante
--cacerto sustituya el certificado del dispositivo siguiendo el proceso de administración previsto. - No utilice
-kcomo solución permanente. Aunque cifra el transporte, no autentica el switch. - Si ha cambiado el certificado o la clave de host SSH, confirme primero que se está accediendo realmente al switch previsto.
Conexión rechazada, tiempo de espera agotado o sin ruta
- Verifique la IP y la VLAN de administración, el enrutamiento, la ACL y que el servicio esté habilitado.
- Pruebe desde un host autorizado en la red de administración designada.
- No eluda estas restricciones abriendo el servicio a todas las redes o a Internet.
- Si un cambio en curso interrumpió el acceso, utilice la ruta de administración independiente y la reversión preparada.
El inicio de sesión falla
- Asegúrese de utilizar una cuenta local del switch, no una cuenta de Sophos Fusion.
- Verifique el nombre de usuario, los requisitos de contraseña, el estado de la cuenta y el rol local.
- No realice intentos automatizados repetidos; pueden provocar bloqueos y ocultar la causa.
- Para la cuenta estándar
admin, tenga en cuenta que su contraseña solo puede ser cambiada por el propioadmino por Sophos Fusion.
HTTP 401 o 403
- Ante un
401, verifique el token y el fin de la sesión e inicie una sesión nueva. - Ante un
403, verifique el rol local y el permiso para la operación; no amplíe los privilegios de forma indiscriminada. - Obtenga un token nuevo como máximo una vez y de forma controlada. Si el error persiste, guarde la respuesta y la versión del firmware, consulte la ayuda publicada de la API y compruebe el modelo y el firmware del dispositivo de destino.
HTTP 400, 404 o 405
- Compare la ruta, el método, el encabezado y el JSON con la ayuda API publicada y valide el modelo y el firmware del dispositivo de destino.
- Verifique la distinción entre mayúsculas y minúsculas y el prefijo
/api. - Un
404o405puede indicar un endpoint no disponible o definido de otro modo en ese firmware. No intente solucionarlo probando operaciones de escritura parecidas.
Error HTTP pese a la respuesta o valor de errCode distinto de 0
- Evalúe el estado HTTP y el cuerpo de la respuesta juntos.
- Documente
message, oculte la información interna antes de compartirla y no repita a ciegas un cambio idéntico. - Si no está claro si un cambio se ha aplicado parcialmente, determine primero el estado real mediante
GET, la salida de la CLI y una prueba funcional.
Falta el comando CLI o se rechaza
- Utilice
?para comprobar si el comando existe en el modo actual. - Utilice
TABpara completar la sintaxis ofrecida en el dispositivo. - Compruebe el rol local, el modo de comandos, el modelo y el firmware.
- No copie un comando que suene similar desde otro firmware.
Reversión, interrupción y fin de sesión
Una prueba aislada con GET /api/ports no modifica la configuración y no requiere una reversión funcional. Sin embargo, el inicio de sesión en la API crea una sesión; ciérrela conforme al ciclo de vida del token y elimine las variables locales.
Si se realiza un cambio de configuración, la reversión debe determinarse por adelantado:
- Exporte el estado actual y los objetos afectados o captúrelos con operaciones de lectura.
- Prepare la operación inversa exacta utilizando la ayuda API publicada y su validación para el firmware implementado o utilizando la ayuda CLI del dispositivo de destino.
- Defina criterios que causen la terminación inmediata, como pérdida de administración, enlace ascendente, accesibilidad de VLAN o suministro de PoE.
- Si se produce un error, no intente realizar más optimizaciones, sino restaurar el valor anterior utilizando el método que aún funciona.
- Luego verifique nuevamente el acceso de administración, el estado del puerto, los enlaces ascendentes y los servicios afectados.
- Si se pierde la vía normal, revierta el cambio a través de la vía de gestión independiente verificada previamente y, si procede, mediante el acceso a consola específico del modelo. Un restablecimiento de fábrica no es una reversión normal, ya que elimina la configuración.
Para un switch gestionado en Sophos Fusion, una reversión local no basta. Compruebe el estado objetivo de referencia en Sophos Fusion y regularice allí el cambio de emergencia aprobado o elimínelo por completo a nivel local. No alterne cambios entre los canales local y central.
En conclusión:
- salga de una visualización de la CLI con
Qy, en el prompt, utilice el comando de salida o cierre de sesión indicado por el dispositivo; - cierre la sesión API con el
PATCH /api/system/logoutdocumentado; - descarte las variables de token y contraseña locales con
unset; - compruebe que ningún archivo temporal ni registro de depuración contenga secretos;
- documente en el ticket el resultado, el firmware, la vía de administración utilizada, la verificación y cualquier reversión.
Refuerzo de seguridad
- VLAN de administración dedicada con ACL limitadas a unos pocos hosts de administración y protocolos requeridos.
- Habilite HTTPS y SSH solo cuando sean necesarios; deshabilite los servicios de administración inseguros o no utilizados.
- Utilice certificados confiables y claves de host SSH verificadas.
- Utilice cuentas locales personales: User para el acceso de solo lectura y Admin únicamente para cambios aprobados.
- Sustituya inmediatamente la contraseña predeterminada; evite las contraseñas compartidas y rótelas tras cambios de personal o de proveedor de servicios.
- Mantenga los secretos de API fuera del código fuente, los archivos
.env, el historial del shell, los argumentos del proceso y la salida de CI. - Nunca ejecute clientes API con una depuración exhaustiva de encabezados mientras esté configurado un encabezado de autorización.
- Mantenga las sesiones breves, utilice un nuevo token por sesión y elimine las variables después.
- Mantenga las copias de seguridad de la configuración protegidas y recuperables fuera del switch.
- Correlacione temporalmente los registros locales, los eventos centrales y los tickets de cambio; compruebe la hora del switch.
- Recertifique periódicamente las cuentas locales, las ACL de administración, los certificados, las claves SSH y el acceso a la automatización.
Variabilidad del firmware y del modelo
La página estática de la API REST documenta la ruta de inicio de sesión /api/system/login, la prueba de lectura /api/ports y el encabezado de autorización; la ayuda publicada de la API también documenta la ruta de cierre de sesión /api/system/logout. Estas ayudas centrales ofrecen ejemplos y orientación, pero no un esquema válido de forma universal para todos los dispositivos. El switch de destino genera el esquema Swagger/OpenAPI específico para su modelo y versión de firmware. La documentación publicada de la CLI confirma las ayudas indicadas, pero tampoco garantiza que todos los demás comandos sean idénticos en cada firmware.
Por lo tanto, antes del uso en producción, según modelo y firmware:
- documentar la versión exacta del firmware y el modelo de hardware;
- Verifique el modo CLI y la sintaxis con
?yTAB; - verifique el esquema Swagger/OpenAPI para conocer los métodos, rutas y esquemas proporcionados por el dispositivo de destino para el firmware en ejecución;
- Inicie sesión y pruebe
GET /api/portsprimero en una sesión controlada; - validar cada automatización de escritura exactamente con respecto a este esquema objetivo y, además, en un entorno no productivo o claramente limitado;
- después de las actualizaciones de firmware, vuelva a probar el inicio de sesión, la verificación de certificados, el esquema de respuesta, el manejo de tokens y todos los endpoints utilizados;
- si detecta diferencias, no «adapte el script» hasta comprender la nueva semántica y el procedimiento de reversión.
Inspección final
- Modelo correcto, firmware correcto e IP de gestión correcta confirmados.
- Sistema de referencia, Sophos Fusion o administración local, documentado.
- Cuenta local User o Admin adecuada para la tarea utilizada.
- Certificado TLS o clave de host SSH verificados; no se utilizó ninguna opción insegura de forma permanente.
- Token utilizado únicamente para su sesión y nunca divulgado.
- Estado HTTP,
errCode,messagey estado funcional verificados. - En caso de cambios, estado actual, reversión y acceso independiente disponible.
- Resultado verificado mediante
GET, la salida de la CLI y las pruebas funcionales necesarias. - Sesión cerrada, variables eliminadas y registros revisados en busca de secretos.
- Excepción local conciliada con Sophos Fusion y cerrada en el ticket.