Exportar e importar Sophos Firewall mediante la API de Central
Desde Sophos Central Firewall Management 2026.29, las configuraciones de firewalls con SFOS 22.0 MR2 o posterior se pueden exportar e importar mediante la API REST. El proceso consta de unos pocos pasos: autenticar el acceso a la API, determinar el ID del firewall, iniciar la exportación o la carga y comprobar la transacción devuelta hasta que finalice.
Una importación modifica el firewall de destino y no es una restauración de una copia de seguridad. Primero debe crearse una copia de seguridad del firewall actual, ejecutar el proceso inicialmente con un firewall de prueba y validar después el resultado de forma local.
Preparar los requisitos y las variables de la API
Se necesita lo siguiente:
- un firewall con SFOS 22.0 MR2 o posterior conectado y administrado mediante Sophos Central;
- una suscripción de pago activa para el firewall distinta de la Base License o un contrato de soporte activo;
- una credencial de API propia con los permisos necesarios;
curl,jqy, para una importación,md5omd5sum;- el ID del tenant, el host regional de la API y el ID del firewall;
- una ventana de mantenimiento y un procedimiento de recuperación probado para las importaciones.
Las credenciales de API se crean en Sophos Central en Global Settings > Access Control > API Credentials. Para un tenant, Sophos recomienda el rol Service Principal Firewall. En el Partner Dashboard, la función se encuentra en Global Settings > APIs & Integrations > API Credentials Management; allí hay otros roles disponibles y debe utilizarse el rol con menos privilegios que permita acceder al tenant de destino. El Client Secret, el JWT, el Secure Storage Master Key y las URL de descarga o carga emitidas posteriormente no deben incluirse en tickets, chats ni capturas de pantalla.
Los ejemplos se ejecutan en Bash y presuponen una credencial de tenant. Las credenciales de Partner y Enterprise primero deben resolver el tenant de destino y su host regional de la API. Sophos explica esta diferencia en How Our APIs Work.
Introducir el Client ID y el Client Secret sin guardar el secreto en el historial de la shell:
read -r -p "Client ID: " CLIENT_ID
read -r -s -p "Client Secret: " CLIENT_SECRET
printf '\n'
A continuación, solicitar un JWT de duración limitada:
JWT=$(
printf '%s' "$CLIENT_SECRET" |
curl --fail-with-body --silent --show-error \
--request POST \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=client_credentials' \
--data-urlencode "client_id=$CLIENT_ID" \
--data-urlencode 'client_secret@-' \
--data-urlencode 'scope=token' \
https://id.sophos.com/api/v2/oauth2/token |
jq -er '.access_token'
)
unset CLIENT_SECRET
Los ejemplos sencillos de curl pasan el JWT de corta duración como argumento de cabecera. Por tanto, deben ejecutarse en una estación de administración de confianza en la que ningún usuario no autorizado pueda leer los argumentos de los procesos locales.
Con una credencial de tenant, whoami devuelve el ID del tenant y el host regional de la API:
WHOAMI=$(
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $JWT" \
https://api.central.sophos.com/whoami/v1
)
TENANT_ID=$(jq -er 'select(.idType == "tenant") | .id' <<<"$WHOAMI")
API_HOST=$(jq -er '.apiHosts.dataRegion' <<<"$WHOAMI")
printf 'Tenant: %s\nAPI host: %s\n' "$TENANT_ID" "$API_HOST"
Si jq se interrumpe en este punto, probablemente la credencial está asignada a un partner o a una organización Enterprise. En ese caso, no debe continuarse utilizando su ID como X-Tenant-ID; es necesario determinar el tenant administrado y su apiHost.
Determinar el ID del firewall
La lista de firewalls muestra el nombre, el hostname, el número de serie, el firmware y el UUID:
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
"$API_HOST/firewall/v1/firewalls?pageSize=1000" |
jq -r '.items[] |
[.name, .hostname, .serialNumber, .firmwareVersion, .id] |
@tsv'
Debe utilizarse el UUID del firewall correcto y no seleccionarlo únicamente por un nombre para mostrar similar:
FIREWALL_ID="<firewall-uuid>"
Si el firewall no aparece, primero deben comprobarse el tenant, la región, la conexión con Central y la aprobación de la administración. El registro se explica en Conectar Sophos Firewall con Sophos Central.
Exportar la configuración
Iniciar una exportación completa
La exportación se ejecuta de forma asíncrona. Por ello, la primera solicitud solo devuelve un ID de transacción:
EXPORT_RESPONSE=$(
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
--header 'Content-Type: application/json' \
--data '{"fullExport":true}' \
"$API_HOST/firewall/v1/firewall-config/firewalls/$FIREWALL_ID/export"
)
EXPORT_TX=$(jq -er '.transactionId' <<<"$EXPORT_RESPONSE")
printf 'Export transaction: %s\n' "$EXPORT_TX"
Volver a consultar el estado después de unos segundos:
EXPORT_STATUS=$(
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
"$API_HOST/firewall/v1/firewall-config/firewalls/transactions/$EXPORT_TX"
)
jq '{status, result, createdAt, finishedAt, expiryAt, response}' \
<<<"$EXPORT_STATUS"
Repetir la solicitud aproximadamente cada diez segundos hasta que status alcance el estado final finished. pending y started todavía no son errores.
Solo cuando aparecen status: "finished" y result: "success", response.url contiene la URL de descarga de duración limitada:
DOWNLOAD_URL=$(
jq -er '
select(.status == "finished" and .result == "success") |
.response.url
' <<<"$EXPORT_STATUS"
)
OUTPUT="sophos-firewall-config-$(date +%F).tar"
curl --fail-with-body --location --output "$OUTPUT" "$DOWNLOAD_URL"
tar -tf "$OUTPUT"
unset DOWNLOAD_URL
El archivo TAR puede contener datos confidenciales de red, usuarios, VPN y políticas. Debe almacenarse de forma segura o eliminarse después del análisis. Para obtener un informe legible o comparar el antes y el después, el archivo Entities.xml incluido puede utilizarse en Sophos Firewall Config Studio.
Exportar solo configuraciones seleccionadas
Para una exportación selectiva, los nombres de las entidades deben indicarse de forma exacta y respetando las mayúsculas y minúsculas. Este ejemplo exporta reglas de firewall y NAT con los objetos dependientes:
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
--header 'Content-Type: application/json' \
--data '{
"fullExport": false,
"includeDependency": true,
"exportEntities": ["FirewallRule", "NATRule"]
}' \
"$API_HOST/firewall/v1/firewall-config/firewalls/$FIREWALL_ID/export"
Los nombres de entidad válidos se encuentran en el endpoint de exportación actual de la API de Sophos Firewall. En una exportación selectiva, el valor predeterminado de includeDependency es false; esta opción debe configurarse de forma consciente y el paquete resultante debe comprobarse de todos modos.
Importar la configuración
La importación consiste en solicitar una URL de carga, cargar el archivo TAR, confirmar los metadatos del archivo y comprobar la transacción. El ejemplo utiliza un único firewall de prueba y establece deliberadamente performPartialImport en false. El valor predeterminado de la API es true y permite una importación parcial por firewall.
Antes debe comprobarse la compatibilidad: el firewall de destino necesita como mínimo la misma versión de firmware y patrones. Sophos solo admite importaciones selectivas entre modelos distintos desde un modelo inferior a otro superior; el hardware de destino debe tener al menos el mismo número de puertos Ethernet. Si los nombres de los puertos son distintos, debe adaptarse Entities.xml antes de la carga.
Preparar el archivo de importación y la suma de comprobación
Preparar el archivo de destino, la suma de comprobación y el tamaño del archivo:
FILE="sophos-firewall-config-2026-08-03.tar"
FILE_SIZE=$(wc -c <"$FILE" | tr -d ' ')
En macOS:
CHECKSUM_MD5=$(md5 -q "$FILE")
En Linux:
CHECKSUM_MD5=$(md5sum "$FILE" | awk '{print $1}')
Solicitar una sesión de carga y cargar el archivo
Solicitar una sesión de carga y mostrar únicamente los campos que no son secretos:
IMPORT_SESSION=$(
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
"$API_HOST/firewall/v1/firewall-config/firewalls/import"
)
IMPORT_TX=$(jq -er '.transactionId' <<<"$IMPORT_SESSION")
UPLOAD_URL=$(jq -er '.url' <<<"$IMPORT_SESSION")
jq '{transactionId, method, expiresAt}' <<<"$IMPORT_SESSION"
Cargar el archivo con el método PUT devuelto antes de que caduque la URL prefirmada. No debe enviarse a esta URL ninguna cabecera JWT ni de tenant:
curl --fail-with-body --silent --show-error \
--request PUT \
--upload-file "$FILE" \
"$UPLOAD_URL"
unset UPLOAD_URL
Completar la importación y comprobar el estado
Si el paquete contiene información confidencial y se importa en un firewall diferente o recién desplegado, se necesita el Secure Storage Master Key correspondiente. Sin él, SFOS no importa la información confidencial ni las configuraciones que dependen de ella. La siguiente solicitud no muestra el secreto; puede pulsarse Enter para omitirlo:
read -r -s -p "Secure Storage Master Key, o pulse Enter: " SSMK
printf '\n'
Completar la carga y asignar el paquete al firewall de destino:
TARGET_FIREWALL_ID="$FIREWALL_ID"
COMPLETE_RESPONSE=$(
printf '%s' "$SSMK" |
jq -Rsc \
--arg firewall "$TARGET_FIREWALL_ID" \
--arg checksum "$CHECKSUM_MD5" \
--argjson size "$FILE_SIZE" '
. as $ssmk |
{
firewallIds: [$firewall],
checksumMd5: $checksum,
fileSizeBytes: $size,
performPartialImport: false
}
+ if ($ssmk | length) > 0
then {secureMasterKey: $ssmk}
else {}
end
' |
curl --fail-with-body --silent --show-error \
--request POST \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
--header 'Content-Type: application/json' \
--data-binary @- \
"$API_HOST/firewall/v1/firewall-config/firewalls/import/$IMPORT_TX/upload-complete"
)
unset SSMK
jq '{id, status, result}' <<<"$COMPLETE_RESPONSE"
Una solicitud acepta entre 1 y 25 ID de firewall únicos. Para otros firewalls, el paquete debe cargarse de nuevo; no debe reutilizarse la misma URL prefirmada ni el mismo ID de transacción.
Comprobar el estado de la importación mediante el mismo endpoint de transacción:
IMPORT_STATUS=$(
curl --fail-with-body --silent --show-error \
--header "Authorization: Bearer $JWT" \
--header "X-Tenant-ID: $TENANT_ID" \
"$API_HOST/firewall/v1/firewall-config/firewalls/transactions/$IMPORT_TX"
)
jq '{status, result, createdAt, finishedAt, response}' <<<"$IMPORT_STATUS"
Repetir también esta consulta aproximadamente cada diez segundos hasta que aparezca status: "finished". Solo entonces deben evaluarse result y, si hay varios destinos, cada elemento de response.items.
success confirma el procesamiento por la API, no el resultado funcional.
Validar la importación de forma local
Después de finished, comprobar en cada firewall de destino:
- ¿Están presentes exactamente las reglas, los objetos y los ajustes esperados?
- ¿Faltan usuarios, contraseñas, certificados u objetos dependientes debido a un SSMK ausente o incorrecto?
- ¿Coinciden las interfaces, las zonas, los gateways, el firmware, la versión de patrones y el modelo de hardware con el paquete?
- ¿Funcionan el enrutamiento, NAT, VPN, la autenticación y el acceso de administración con casos de prueba definidos?
- ¿Muestran el Audit Trail y los registros de configuración cambios inesperados?
- ¿Muestra una nueva exportación o comparación con Config Studio únicamente las diferencias previstas?
La importación y exportación actualizan la configuración existente y no eliminan automáticamente todo lo que falta en el paquete. Por tanto, el archivo TAR no representa un estado objetivo completo ni sustituye a una copia de seguridad, un rollback y una prueba funcional.
Solución de problemas
La exportación no muestra una URL de descarga
La URL solo aparece después de status: "finished" y result: "success". Si se obtiene error o partialSuccess, deben comprobarse los campos de response y no continuar con una URL vacía o caducada.
HTTP 401 o 403
Con 401, el JWT suele haber caducado o no ser válido. Debe repetirse la autenticación. Con 403, deben comprobarse el rol de Central, el contexto del tenant, X-Tenant-ID y el host regional de la API. El permiso de la API XML local en /webconsole/APIController no se aplica aquí; se trata por separado en Proteger el acceso a la API XML de Sophos Firewall.
Upload-complete devuelve 400 o 409
Comprobar el ID de transacción, la caducidad de la URL de carga, el tamaño real del archivo, la suma de comprobación MD5 hexadecimal y los ID de firewall únicos. El archivo cargado no debe modificarse posteriormente. Para realizar otro intento debe utilizarse una nueva sesión de carga.
La importación termina con error o partialSuccess
Guardar response y, si hay varios firewalls de destino, response.items. Si una llamada a la API devuelve una respuesta de error 4xx o 5xx independiente, documentar también error, message, code, correlationId y requestId. A continuación, comprobar localmente la versión de destino, la versión de patrones, el modelo, los puertos, las dependencias y el SSMK. Si la respuesta de Central no es suficiente, comprobar fwcm-api-executor.log en el firewall tal como se describe en Servicios y archivos de registro de Sophos Firewall.
El endpoint de transacción es determinante para el estado de la API; no debe presuponerse que la tarea aparece en la Central Firewall Task Queue.
Una importación muy grande se interrumpe
Sophos documenta el Known Issue NR-19066: una importación con un número muy elevado de objetos puede superar el límite de procesamiento de dos horas. En lugar de repetir la importación completa sin cambios, deben crearse paquetes selectivos más pequeños, importarlos individualmente y validar cada paso.
Al final de la sesión deben eliminarse las variables confidenciales:
unset JWT CLIENT_ID TENANT_ID API_HOST FIREWALL_ID TARGET_FIREWALL_ID
unset EXPORT_TX IMPORT_TX CHECKSUM_MD5 FILE_SIZE