Saltar para o conteudo
Avanet

Exportar e importar Sophos Firewall através da API do Central

Desde o Sophos Central Firewall Management 2026.29, é possível exportar e importar através da REST API as configurações de firewalls com SFOS 22.0 MR2 ou posterior. O processo inclui apenas alguns passos: autenticar o acesso à API, determinar o ID da firewall, iniciar a exportação ou o upload e verificar a transação devolvida até à conclusão.

Uma importação altera a firewall de destino e não equivale ao restauro de um backup. Antes, deve ser criado um backup atual da firewall, o processo deve ser executado primeiro numa firewall de teste e o resultado deve ser validado localmente.

Preparar os requisitos e as variáveis da API

São necessários:

  • uma firewall com SFOS 22.0 MR2 ou posterior, ligada e gerida através do Sophos Central;
  • uma subscrição paga da firewall ativa para além da Base License ou um contrato de suporte ativo;
  • credenciais de API próprias com as permissões necessárias;
  • curl, jq e, para uma importação, md5 ou md5sum;
  • ID do tenant, host regional da API e ID da firewall;
  • uma janela de manutenção e um caminho de retorno testado para importações.

As credenciais da API são criadas no Sophos Central em Global Settings > Access Control > API Credentials. Para um tenant, a Sophos recomenda a função Service Principal Firewall. No Partner Dashboard, a função encontra-se em Global Settings > APIs & Integrations > API Credentials Management; estão disponíveis outras funções e deve ser usada a de menor privilégio com acesso ao tenant de destino. O Client Secret, o JWT, o Secure Storage Master Key e os URL de download ou upload emitidos posteriormente não devem ser incluídos em pedidos de suporte, chats ou capturas de ecrã.

Os exemplos são executados em Bash e pressupõem credenciais de um tenant. As credenciais de partner e enterprise têm primeiro de determinar o tenant de destino e o respetivo host regional da API. A Sophos explica esta diferença em How Our APIs Work.

Introduzir o Client ID e o Client Secret sem escrever o secret no histórico da shell:

read -r -p "Client ID: " CLIENT_ID
read -r -s -p "Client Secret: " CLIENT_SECRET
printf '\n'

Em seguida, solicitar um JWT de duração 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

Os exemplos simples de curl passam o JWT de curta duração como argumento do header. Por isso, devem ser executados numa estação de trabalho administrativa fidedigna, onde outros utilizadores não possam ler os argumentos dos processos locais.

Com credenciais de um tenant, whoami devolve o ID do tenant e o host regional da 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"

Se jq terminar aqui, é provável que as credenciais estejam associadas a um partner ou a uma organização enterprise. Nesse caso, não se deve continuar com o respetivo ID como X-Tenant-ID; deve ser determinado o tenant gerido e o seu apiHost.

Determinar o ID da firewall

A lista de firewalls mostra o nome, hostname, número de série, firmware e 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'

Usar o UUID da firewall correta e não selecionar apenas com base num nome de apresentação semelhante:

FIREWALL_ID="<firewall-uuid>"

Se a firewall não aparecer, verificar primeiro o tenant, a região, a ligação ao Central e a autorização de gestão. O registo é explicado em Ligar o Sophos Firewall ao Sophos Central.

Exportar a configuração

Iniciar uma exportação completa

A exportação é executada de forma assíncrona. Por isso, o primeiro pedido devolve apenas um ID de transação:

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"

Voltar a obter o estado após alguns 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 o pedido aproximadamente a cada dez segundos até status atingir o estado final finished. pending e started ainda não são erros.

Só quando status: "finished" e result: "success" é que response.url contém o URL de download de duração 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

O ficheiro TAR pode conter dados sensíveis de rede, utilizadores, VPN e políticas. Deve ser guardado de forma protegida ou eliminado depois da análise. Para obter um relatório legível ou uma comparação antes/depois, o ficheiro Entities.xml incluído pode ser utilizado no Sophos Firewall Config Studio.

Exportar apenas configurações selecionadas

Numa exportação seletiva, os nomes das entidades têm de ser indicados exatamente e com a utilização correta de maiúsculas e minúsculas. Este exemplo exporta regras de firewall e NAT com objetos dependentes:

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"

Os nomes de entidades válidos encontram-se no atual endpoint de exportação da Sophos Firewall API. Numa exportação seletiva, includeDependency tem o valor predefinido false; a opção deve ser definida de forma consciente e o pacote resultante deve ser verificado mesmo assim.

Importar a configuração

A importação consiste em solicitar um URL de upload, carregar o ficheiro TAR, confirmar os dados do ficheiro e verificar a transação. O exemplo utiliza apenas uma firewall de teste e define deliberadamente performPartialImport como false. O valor predefinido da API é true e permite uma importação parcial por firewall.

Verificar primeiro a compatibilidade: a firewall de destino necessita, no mínimo, da mesma versão de firmware e de patterns. A Sophos só suporta importações seletivas entre modelos diferentes de um modelo inferior para um superior; o hardware de destino tem de ter, no mínimo, o mesmo número de portas Ethernet. Se os nomes das portas forem diferentes, Entities.xml tem de ser adaptado antes do upload.

Preparar o ficheiro de importação e o checksum

Preparar o ficheiro de destino, o checksum e o tamanho do ficheiro:

FILE="sophos-firewall-config-2026-08-03.tar"
FILE_SIZE=$(wc -c <"$FILE" | tr -d ' ')

No macOS:

CHECKSUM_MD5=$(md5 -q "$FILE")

No Linux:

CHECKSUM_MD5=$(md5sum "$FILE" | awk '{print $1}')

Solicitar uma sessão de upload e carregar o ficheiro

Solicitar uma sessão de upload e mostrar apenas os campos não 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"

Carregar o ficheiro antes da expiração do URL pre-signed com o método PUT devolvido. Não enviar headers JWT ou de tenant para este URL:

curl --fail-with-body --silent --show-error \
  --request PUT \
  --upload-file "$FILE" \
  "$UPLOAD_URL"
unset UPLOAD_URL

Concluir a importação e verificar o estado

Se o pacote contiver informações sensíveis e for importado para uma firewall diferente ou reinstalada, é necessário o Secure Storage Master Key correspondente. Sem esta chave, o SFOS não importa as informações sensíveis nem as configurações que delas dependem. O pedido seguinte não mostra o secret; ao premir Enter, este é omitido:

read -r -s -p "Secure Storage Master Key, ou Enter para omitir: " SSMK
printf '\n'

Concluir o upload e atribuir o pacote à 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"

Um pedido aceita entre 1 e 25 IDs de firewall exclusivos. Para firewalls adicionais, o pacote tem de ser carregado novamente; não reutilizar o mesmo URL pre-signed ou ID de transação.

Verificar o estado da importação através do mesmo endpoint de transações:

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 também este pedido aproximadamente a cada dez segundos até aparecer status: "finished". Só depois se deve avaliar result e, no caso de vários destinos, cada elemento em response.items.

success confirma o processamento pela API, não o efeito funcional.

Validar localmente a importação

Após finished, verificar em cada firewall de destino:

  1. Estão presentes exatamente as regras, os objetos e as definições esperados?
  2. Faltam utilizadores, palavras-passe, certificados ou objetos dependentes devido a um SSMK em falta ou incorreto?
  3. As interfaces, zonas, gateways, firmware, versão dos patterns e modelo de hardware correspondem ao pacote?
  4. O routing, NAT, VPN, autenticação e acesso de gestão funcionam com casos de teste definidos?
  5. O Audit Trail e os logs de configuração mostram alterações inesperadas?
  6. Uma nova exportação ou uma comparação no Config Studio mostra apenas as diferenças planeadas?

A importação/exportação atualiza a configuração existente e não remove automaticamente tudo o que está em falta no pacote. Por isso, o ficheiro TAR não representa um estado de destino completo nem substitui um backup, um rollback e um teste funcional.

Resolução de problemas

A exportação não apresenta um URL de download

O URL só aparece depois de status: "finished" e result: "success". Em caso de error ou partialSuccess, verificar os campos em response e não continuar com um URL vazio ou expirado.

HTTP 401 ou 403

Com 401, o JWT está geralmente expirado ou é inválido. Efetuar novamente a autenticação. Com 403, verificar a função do Central, o contexto do tenant, X-Tenant-ID e o host regional da API. A autorização da API XML local em /webconsole/APIController não é responsável por esta função; é abordada separadamente em Proteger o acesso à API XML do Sophos Firewall.

Upload-complete devolve 400 ou 409

Verificar o ID da transação, a data de expiração do URL de upload, o tamanho real do ficheiro, o checksum MD5 hexadecimal e os IDs de firewall exclusivos. Não alterar o ficheiro depois do upload. Para uma nova tentativa, utilizar uma nova sessão de upload.

A importação termina com error ou partialSuccess

Guardar response e, no caso de várias firewalls de destino, response.items. Se uma chamada à API devolver uma resposta de erro 4xx ou 5xx separada, documentar também error, message, code, correlationId e requestId. Em seguida, verificar localmente a versão de destino, o estado dos patterns, o modelo, as portas, as dependências e o SSMK. Se a resposta do Central não for suficiente, verificar fwcm-api-executor.log na firewall de acordo com Serviços e ficheiros de log do Sophos Firewall.

O endpoint de transações é determinante para o estado da API; não assumir que a tarefa aparece na Central Firewall Task Queue.

Uma importação muito grande é interrompida

A Sophos indica o Known Issue NR-19066: uma importação com um número muito elevado de objetos pode exceder o limite de processamento de duas horas. Em vez de repetir a importação completa sem alterações, criar pacotes seletivos mais pequenos, importá-los individualmente e validar cada passo.

No fim da sessão, remover as variáveis sensíveis:

unset JWT CLIENT_ID TENANT_ID API_HOST FIREWALL_ID TARGET_FIREWALL_ID
unset EXPORT_TX IMPORT_TX CHECKSUM_MD5 FILE_SIZE