** Distribuera en Amazon Web Services (AWS) webbapplikation till Azure

I den här artikeln distribuerar du Yelb-programmet till klustret Azure Kubernetes Service (AKS) som du skapade i previous-artikeln.

Kontrollera miljön

Innan du distribuerar applikationen bör du säkerställa att din AKS-kluster är korrekt konfigurerad genom att använda följande kommandon:

  1. Lista namnområdena i din kluster med kommandot kubectl get namespace.

    kubectl get namespace
    

    Om du installerade NGINX-ingresskontrollern med hjälp av tillägget för applikationsdirigering, bör du se app-routing-system-namnområdet i utdata:

    NAME                 STATUS   AGE
    app-routing-system   Active   4h28m
    cert-manager         Active   109s
    dapr-system          Active   4h18m
    default              Active   4h29m
    gatekeeper-system    Active   4h28m
    kube-node-lease      Active   4h29m
    kube-public          Active   4h29m
    kube-system          Active   4h29m
    

    Om du har installerat NGINX-ingresskontrollen via Helm bör du se ingress-basic-namnrymden i resultatet:

    NAME                STATUS   AGE
    cert-manager        Active   7m42s
    dapr-system         Active   11m
    default             Active   21m
    gatekeeper-system   Active   20m
    ingress-basic       Active   7m19s
    kube-node-lease     Active   21m
    kube-public         Active   21m
    kube-system         Active   21m
    prometheus          Active   8m9s
    
  2. Hämta tjänstinformationen för app-routing-system namnområdet eller ingress-basic med hjälp av kubectl get service command.

    kubectl get service --namespace <namespace-name> -o wide
    

    Om du använde tilläggsprogrammet för applikationsroutering bör du se att EXTERNAL-IP för tjänsten nginx är en privat IP-adress. Den här adressen är den privata IP-adressen för en IP-konfiguration för klientdelen i den kubernetes-internal privata lastbalanseraren för ditt AKS-kluster:

    NAME    TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)                                      AGE     SELECTOR
    nginx   LoadBalancer   172.16.55.104   10.240.0.7    80:31447/TCP,443:31772/TCP,10254:30459/TCP   4h28m   app=nginx
    

    Om du använde Helm bör du se att EXTERNAL-IP-tjänstens IP-adress är privat. Denna adress är den privata IP-adressen för en frontend-IP-konfiguration i den privata lastbalanseraren för ditt AKS-kluster.

    NAME                                               TYPE           CLUSTER-IP       EXTERNAL-IP   PORT(S)                      AGE
    nginx-ingress-ingress-nginx-controller             LoadBalancer   172.16.42.152    10.240.0.7    80:32117/TCP,443:32513/TCP   7m31s
    nginx-ingress-ingress-nginx-controller-admission   ClusterIP      172.16.78.85     <none>        443/TCP                      7m31s
    nginx-ingress-ingress-nginx-controller-metrics     ClusterIP      172.16.109.138   <none>        10254/TCP                    7m31s
    

Förbered att distribuera Yelb-applikationen

Om du vill distribuera exemplet med hjälp av TLS-terminering vid Application Gateway och Yelb-anrop via HTTP, kan du hitta Bash-skript och YAML-mallar för att distribuera Yelb-applikationen i http-mappen.

Om du vill distribuera exemplet med hjälp av Implementing end-to-end TLS med hjälp av arkitekturen Azure Application Gateway kan du hitta Bash-skript och YAML-mallar för att distribuera webbprogrammet i mappen https.

I de återstående avsnitten i den här artikeln vägleder vi dig genom distributionsprocessen för exempelprogrammet med hjälp av TLS-metoden från slutpunkt till slutpunkt.

Anpassa variabler

  1. Innan du kör några skript behöver du anpassa värdena för variablerna i 00-variables.sh-filen. Denna fil är inkluderad i alla skript och innehåller följande variabler:

    # Azure subscription and tenant
    RESOURCE_GROUP_NAME="<aks-resource-group>"
    SUBSCRIPTION_ID="$(az account show --query id --output tsv)"
    SUBSCRIPTION_NAME="$(az account show --query name --output tsv)"
    TENANT_ID="$(az account show --query tenantId --output tsv)"
    AKS_CLUSTER_NAME="<aks-name>"
    AGW_NAME="<application-gateway-name>"
    AGW_PUBLIC_IP_NAME="<application-gateway-public-ip-name>"
    DNS_ZONE_NAME="<your-azure-dns-zone-name-eg-contoso.com>"
    DNS_ZONE_RESOURCE_GROUP_NAME="<your-azure-dns-zone-resource-group-name>"
    DNS_ZONE_SUBSCRIPTION_ID="<your-azure-dns-zone-subscription-id>"
    
    # NGINX ingress controller installed via Helm
    NGINX_NAMESPACE="ingress-basic"
    NGINX_REPO_NAME="ingress-nginx"
    NGINX_REPO_URL="https://kubernetes.github.io/ingress-nginx"
    NGINX_CHART_NAME="ingress-nginx"
    NGINX_RELEASE_NAME="ingress-nginx"
    NGINX_REPLICA_COUNT=3
    
    # Specify the ingress class name for the ingress controller
    # - nginx: Unmanaged NGINX ingress controller installed via Helm
    # - webapprouting.kubernetes.azure.com: Managed NGINX ingress controller installed via AKS application routing add-on
    INGRESS_CLASS_NAME="webapprouting.kubernetes.azure.com"
    
    # Subdomain of the Yelb UI service
    SUBDOMAIN="<yelb-application-subdomain>"
    
    # URL of the Yelb UI service
    URL="https://$SUBDOMAIN.$DNS_ZONE_NAME"
    
    # Secret provider class
    KEY_VAULT_NAME="<key-vault-name>"
    KEY_VAULT_CERTIFICATE_NAME="<key-vault-resource-group-name>"
    KEY_VAULT_SECRET_PROVIDER_IDENTITY_CLIENT_ID="<key-vault-secret-provider-identity-client-id>"
    TLS_SECRET_NAME="yelb-tls-secret"
    NAMESPACE="yelb"
    
  2. Du kan köra följande kommando az aks show för att hämta clientId för den användartilldelade hanterade identiteten som används av Azure Key Vault Provider for Secrets Store CSI Driver. Modulrollen keyVault.bicepKey Vault Administrator tilldelas den användartilldelade hanterade identiteten för tillägget, så att den kan hämta certifikatet som används av Kubernetes Ingress för att exponera tjänsten yelb-ui via NGINX Ingress Controller.

    az aks show \
      --name <aks-name> \
      --resource-group <aks-resource-group-name> \
      --query addonProfiles.azureKeyvaultSecretsProvider.identity.clientId \
      --output tsv \
      --only-show-errors
    
  3. Om du har distribuerat den Azure infrastrukturen med hjälp av de Bicep moduler som medföljer det här exemplet kan du gå vidare till distribuera Yelb-programmet. Om du vill distribuera applikationen i din AKS-kluster kan du använda följande skript för att konfigurera din miljö. Du kan använda 02-create-nginx-ingress-controller.sh för att installera NGINX ingress controller med den öppna källkodsapplikationsbrandväggen ModSecurity (WAF) aktiverad.

    #!/bin/bash
    
    # Variables
    source ./00-variables.sh
    
    # Check if the NGINX ingress controller Helm chart is already installed
    result=$(helm list -n $NGINX_NAMESPACE | grep $NGINX_RELEASE_NAME | awk '{print $1}')
    
    if [[ -n $result ]]; then
      echo "[$NGINX_RELEASE_NAME] NGINX ingress controller release already exists in the [$NGINX_NAMESPACE] namespace"
    else
      # Check if the NGINX ingress controller repository is not already added
      result=$(helm repo list | grep $NGINX_REPO_NAME | awk '{print $1}')
    
      if [[ -n $result ]]; then
        echo "[$NGINX_REPO_NAME] Helm repo already exists"
      else
        # Add the NGINX ingress controller repository
        echo "Adding [$NGINX_REPO_NAME] Helm repo..."
        helm repo add $NGINX_REPO_NAME $NGINX_REPO_URL
      fi
    
      # Update your local Helm chart repository cache
      echo 'Updating Helm repos...'
      helm repo update
    
      # Deploy NGINX ingress controller
      echo "Deploying [$NGINX_RELEASE_NAME] NGINX ingress controller to the [$NGINX_NAMESPACE] namespace..."
      helm install $NGINX_RELEASE_NAME $NGINX_REPO_NAME/$nginxChartName \
        --create-namespace \
        --namespace $NGINX_NAMESPACE \
        --set controller.nodeSelector."kubernetes\.io/os"=linux \
        --set controller.replicaCount=$NGINX_REPLICA_COUNT \
        --set defaultBackend.nodeSelector."kubernetes\.io/os"=linux \
        --set controller.service.annotations."service\.beta\.kubernetes\.io/azure-load-balancer-health-probe-request-path"=/healthz
    fi
    
    # Get values
    helm get values $NGINX_RELEASE_NAME --namespace $NGINX_NAMESPACE
    

Distribuera programmet

  1. Kör följande 03-deploy-yelb.sh script för att distribuera Yelb-applikationen och ett Kubernetes Ingress objekt för att göra yelb-ui-tjänsten tillgänglig på det allmänna internet.

    #!/bin/bash
    
    # Variables
    source ./00-variables.sh
    
    # Check if namespace exists in the cluster
    result=$(kubectl get namespace -o jsonpath="{.items[?(@.metadata.name=='$NAMESPACE')].metadata.name}")
    
    if [[ -n $result ]]; then
      echo "$NAMESPACE namespace already exists in the cluster"
    else
      echo "$NAMESPACE namespace does not exist in the cluster"
      echo "creating $NAMESPACE namespace in the cluster..."
      kubectl create namespace $NAMESPACE
    fi
    
    # Create the Secret Provider Class object
    echo "Creating the secret provider class object..."
    cat <<EOF | kubectl apply -f -
    apiVersion: secrets-store.csi.x-k8s.io/v1
    kind: SecretProviderClass
    metadata:
      namespace: $NAMESPACE
      name: yelb
    spec:
      provider: azure
      secretObjects:
        - secretName: $TLS_SECRET_NAME
          type: kubernetes.io/tls
          data: 
            - objectName: $KEY_VAULT_CERTIFICATE_NAME
              key: tls.key
            - objectName: $KEY_VAULT_CERTIFICATE_NAME
              key: tls.crt
      parameters:
        usePodIdentity: "false"
        useVMManagedIdentity: "true"
        userAssignedIdentityID: $KEY_VAULT_SECRET_PROVIDER_IDENTITY_CLIENT_ID
        keyvaultName: $KEY_VAULT_NAME
        objects: |
          array:
            - |
              objectName: $KEY_VAULT_CERTIFICATE_NAME
              objectType: secret
        tenantId: $TENANT_ID
    EOF
    
    # Apply the YAML configuration
    kubectl apply -f yelb.yml
    
    echo "waiting for secret $TLS_SECRET_NAME in namespace $namespace..."
    
    while true; do
      if kubectl get secret -n $NAMESPACE $TLS_SECRET_NAME >/dev/null 2>&1; then
        echo "secret $TLS_SECRET_NAME found!"
        break
      else
        printf "."
        sleep 3
      fi
    done
    
    # Create chat-ingress
    cat ingress.yml |
      yq "(.spec.ingressClassName)|="\""$INGRESS_CLASS_NAME"\" |
      yq "(.spec.tls[0].hosts[0])|="\""$SUBDOMAIN.$DNS_ZONE_NAME"\" |
      yq "(.spec.tls[0].secretName)|="\""$TLS_SECRET_NAME"\" |
      yq "(.spec.rules[0].host)|="\""$SUBDOMAIN.$DNS_ZONE_NAME"\" |
      kubectl apply -f -
    
    # Check the deployed resources within the yelb namespace:
    kubectl get all -n yelb
    
  2. Uppdatera YAML-manifestet yelb-ui så att det innehåller definitionen csi volume och volume mount för att läsa certifikatet som en hemlighet från Azure Key Vault.

apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: yelb
  name: yelb-ui
spec:
  replicas: 1
  selector:
    matchLabels:
      app: yelb-ui
      tier: frontend
  template:
    metadata:
      labels:
        app: yelb-ui
        tier: frontend
    spec:
      containers:
        - name: yelb-ui
          image: mreferre/yelb-ui:0.7
          ports:
            - containerPort: 80
          volumeMounts:
            - name: secrets-store-inline
              mountPath: "/mnt/secrets-store"
              readOnly: true
      volumes:
        - name: secrets-store-inline
          csi:
            driver: secrets-store.csi.k8s.io
            readOnly: true
            volumeAttributes:
              secretProviderClass: yelb
  1. Du kan nu distribuera applikationen. Skriptet använder yelb.yml YAML-manifestet för att distribuera programmet och ingress.yml för att skapa ingressobjektet. Om du använder en Azure offentlig DNS-zon för domännamnsmatchning kan du använda skriptet 04-configure-dns.sh. Detta skript associerar den offentliga IP-adressen för NGINX-ingresskontrollern med domänen som används av ingressobjektet, vilket exponerar yelb-ui-tjänsten. Skriptet utför följande steg:

    1. Hämtar den offentliga adressen för den Azure offentliga IP-adressen som används av klientdels-IP-konfigurationen för Application Gateway.
    2. Kontrollerar om en A-post finns för underdomänen som används av yelb-ui-tjänsten.
    3. Om A-posten inte finns, skapar skriptet den.
source ./00-variables.sh

# Get the address of the Application Gateway Public IP
echo "Retrieving the address of the [$AGW_PUBLIC_IP_NAME] public IP address of the [$AGW_NAME] Application Gateway..."
PUBLIC_IP_ADDRESS=$(az network public-ip show \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $AGW_PUBLIC_IP_NAME \
    --query ipAddress \
    --output tsv \
    --only-show-errors)
if [[ -n $PUBLIC_IP_ADDRESS ]]; then
    echo "[$PUBLIC_IP_ADDRESS] public IP address successfully retrieved for the [$AGW_NAME] Application Gateway"
else
    echo "Failed to retrieve the public IP address of the [$AGW_NAME] Application Gateway"
    exit
fi
# Check if an A record for todolist subdomain exists in the DNS Zone
echo "Retrieving the A record for the [$SUBDOMAIN] subdomain from the [$DNS_ZONE_NAME] DNS zone..."
IPV4_ADDRESS=$(az network dns record-set a list \
    --zone-name $DNS_ZONE_NAME \
    --resource-group $DNS_ZONE_RESOURCE_GROUP_NAME \
    --subscription $DNS_ZONE_SUBSCRIPTION_ID \
    --query "[?name=='$SUBDOMAIN'].ARecords[].IPV4_ADDRESS" \
    --output tsv \
    --only-show-errors)
if [[ -n $IPV4_ADDRESS ]]; then
    echo "An A record already exists in [$DNS_ZONE_NAME] DNS zone for the [$SUBDOMAIN] subdomain with [$IPV4_ADDRESS] IP address"
    if [[ $IPV4_ADDRESS == $PUBLIC_IP_ADDRESS ]]; then
        echo "The [$IPV4_ADDRESS] ip address of the existing A record is equal to the ip address of the ingress"
        echo "No additional step is required"
        continue
    else
        echo "The [$IPV4_ADDRESS] ip address of the existing A record is different than the ip address of the ingress"
    fi
    # Retrieving name of the record set relative to the zone
    echo "Retrieving the name of the record set relative to the [$DNS_ZONE_NAME] zone..."
    RECORDSET_NAME=$(az network dns record-set a list \
        --zone-name $DNS_ZONE_NAME \
        --resource-group $DNS_ZONE_RESOURCE_GROUP_NAME \
        --subscription $DNS_ZONE_SUBSCRIPTION_ID \
        --query "[?name=='$SUBDOMAIN'].name" \
        --output tsv \
        --only-show-errors 2>/dev/null)
    if [[ -n $RECORDSET_NAME ]]; then
        echo "[$RECORDSET_NAME] record set name successfully retrieved"
    else
        echo "Failed to retrieve the name of the record set relative to the [$DNS_ZONE_NAME] zone"
        exit
    fi
    # Remove the A record
    echo "Removing the A record from the record set relative to the [$DNS_ZONE_NAME] zone..."
    az network dns record-set a remove-record \
        --ipv4-address $IPV4_ADDRESS \
        --record-set-name $RECORDSET_NAME \
        --zone-name $DNS_ZONE_NAME \
        --resource-group $DNS_ZONE_RESOURCE_GROUP_NAME \
        --subscription $DNS_ZONE_SUBSCRIPTION_ID \
        --only-show-errors 1>/dev/null
    if [[ $? == 0 ]]; then
        echo "[$IPV4_ADDRESS] ip address successfully removed from the [$RECORDSET_NAME] record set"
    else
        echo "Failed to remove the [$IPV4_ADDRESS] ip address from the [$RECORDSET_NAME] record set"
        exit
    fi
fi
# Create the A record
echo "Creating an A record in [$DNS_ZONE_NAME] DNS zone for the [$SUBDOMAIN] subdomain with [$PUBLIC_IP_ADDRESS] IP address..."
az network dns record-set a add-record \
    --zone-name $DNS_ZONE_NAME \
    --resource-group $DNS_ZONE_RESOURCE_GROUP_NAME \
    --subscription $DNS_ZONE_SUBSCRIPTION_ID \
    --record-set-name $SUBDOMAIN \
    --ipv4-address $PUBLIC_IP_ADDRESS \
    --only-show-errors 1>/dev/null
if [[ $? == 0 ]]; then
    echo "A record for the [$SUBDOMAIN] subdomain with [$PUBLIC_IP_ADDRESS] IP address successfully created in [$DNS_ZONE_NAME] DNS zone"
else
    echo "Failed to create an A record for the $SUBDOMAIN subdomain with [$PUBLIC_IP_ADDRESS] IP address in [$DNS_ZONE_NAME] DNS zone"
fi

Anmärkning

Innan du distribuerar Yelb-programmet och skapar objektet ingress genererar skriptet en SecretProviderClass för att hämta TLS-certifikatet från Azure Key Vault och generera Kubernetes-hemligheten för objektet ingress. Observera att CSI-drivrutinen för Secrets Store för Key Vault endast skapar Kubernetes-hemligheten som innehåller TLS-certifikatet när SecretProviderClass och volymdefinitionen ingår i deployment. För att säkerställa att TLS-certifikatet hämtas korrekt från Azure Key Vault och lagras i Kubernetes-hemligheten som används av objektet ingress måste vi göra följande ändringar i YAML-manifestet för yelb-ui distribution:

  • Lägg till csi volume definition med hjälp av drivrutinen secrets-store.csi.k8s.io, som refererar till objektet SecretProviderClass som ansvarar för att hämta TLS-certifikatet från Azure Key Vault.
  • Inkludera volume mount för att läsa certifikatet som en hemlighet från Azure Key Vault.

För mer information, se Installera Secrets Store CSI Driver för att aktivera NGINX-ingresskontroller med TLS.

Testa programmet

Använd 05-call-yelb-ui.sh-scriptet för att anropa yelb-ui-tjänsten, simulera SQL-injektioner, XSS-attacker, och observera hur den hanterade regelsamlingen av ModSecurity blockerar skadliga förfrågningar.

#!/bin/bash
# Variables
source ./00-variables.sh
# Call REST API
echo "Calling Yelb UI service at $URL..."
curl -w 'HTTP Status: %{http_code}\n' -s -o /dev/null $URL
# Simulate SQL injection
echo "Simulating SQL injection when calling $URL..."
curl -w 'HTTP Status: %{http_code}\n' -s -o /dev/null $URL/?users=ExampleSQLInjection%27%20--
# Simulate XSS
echo "Simulating XSS when calling $URL..."
curl -w 'HTTP Status: %{http_code}\n' -s -o /dev/null $URL/?users=ExampleXSS%3Cscript%3Ealert%28%27XSS%27%29%3C%2Fscript%3E
# A custom rule blocks any request with the word blockme in the querystring.
echo "Simulating query string manipulation with the 'blockme' word in the query string..."
curl -w 'HTTP Status: %{http_code}\n' -s -o /dev/null $URL/?users?task=blockme

Bash-skriptet bör producera följande utdata, där det första anropet lyckas, medan ModSecurity-regler blockerar de följande två anropen.

Calling Yelb UI service at https://yelb.contoso.com...
HTTP Status: 200
Simulating SQL injection when calling https://yelb.contoso.com...
HTTP Status: 403
Simulating XSS when calling https://yelb.contoso.com...
HTTP Status: 403
Simulating query string manipulation with the 'blockme' word in the query string...
HTTP Status: 403

Övervaka programmet

I den föreslagna lösningen konfigurerar distributionsprocessen automatiskt resursen Azure Application Gateway för att samla in diagnostikloggar och mått till en Azure Log Analytics arbetsyta arbetsyta. Genom att aktivera loggar kan du få värdefulla insikter om utvärderingar, matchningar och block som utförs av Azure Web Application Firewall (WAF) i Application Gateway. Mer information finns i Diagnostikloggar för Application Gateway. Du kan också använda Log Analytics för att undersöka data i brandväggsloggarna. När du har brandväggsloggarna på din Log Analytics arbetsyta kan du visa data, skriva frågor, skapa visualiseringar och lägga till dem på portalens instrumentpanel. Detaljerad information om loggfrågor finns i Översikt över loggfrågor i Azure Monitor.

Utforska data med Kusto-frågor

I den föreslagna lösningen konfigurerar distributionsprocessen automatiskt resursen Azure Application Gateway för att samla in diagnostikloggar och mått till en Azure Log Analytics arbetsyta. Genom att aktivera loggar kan du få insikter om utvärderingar, matchningar och block som utförs av Azure Web Application Firewall (WAF) i Application Gateway. Mer information finns i Diagnostikloggar för Application Gateway.

Du kan också använda Log Analytics för att undersöka data i brandväggsloggarna. När du har brandväggsloggarna på din Log Analytics arbetsyta kan du visa data, skriva frågor, skapa visualiseringar och lägga till dem på portalens instrumentpanel. Mer information om loggfrågor finns i Översikt över loggfrågor i Azure Monitor.

AzureDiagnostics 
| where ResourceProvider == "MICROSOFT.NETWORK" and Category == "ApplicationGatewayFirewallLog"
| limit 10

Alternativt, när du arbetar med resursspecifika tabellen, kan de råa brandväggsloggdata nås med hjälp av följande fråga. För att lära dig mer om resurs-specifika tabeller, se Dokumentationen för övervakningsdata.

AGWFirewallLogs
| limit 10

När du har uppgifterna kan du fördjupa dig och skapa grafer eller visualiseringar. Här är några ytterligare exempel på KQL-frågor som kan användas:

Matchade/blockerade begäranden efter IP

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.NETWORK" and Category == "ApplicationGatewayFirewallLog"
| summarize count() by clientIp_s, bin(TimeGenerated, 1m)
| render timechart

Matchade/blockerade förfrågningar efter URI

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.NETWORK" and Category == "ApplicationGatewayFirewallLog"
| summarize count() by requestUri_s, bin(TimeGenerated, 1m)
| render timechart

De mest matchande reglerna

| where ResourceProvider == "MICROSOFT.NETWORK" and Category == "ApplicationGatewayFirewallLog"
| summarize count() by ruleId_s, bin(TimeGenerated, 1m)
| where count_ > 10
| render timechart

De fem vanligaste matchningsregelgrupperna

AzureDiagnostics
| where ResourceProvider == "MICROSOFT.NETWORK" and Category == "ApplicationGatewayFirewallLog"
| summarize Count=count() by details_file_s, action_s
| top 5 by Count desc
| render piechart

Granska distribuerade resurser

Du kan använda Azure CLI eller Azure PowerShell för att lista de distribuerade resurserna i resursgruppen.

Lista de distribuerade resurserna i resursgruppen med kommandot [az resource list][az-resource-list].

az resource list --resource-group <resource-group-name>

Du kan använda Azure CLI eller Azure PowerShell för att ta bort resursgruppen när du inte längre behöver de resurser som du skapade i den här självstudien.

Ta bort resursgruppen och dess tillhörande resurser med hjälp av kommandot az group delete.

az group delete --name <resource-group-name>

Nästa steg

Du kan öka säkerheten och hotskyddet för lösningen med hjälp av Azure DDoS Protection och Azure Firewall. Mer information finns i följande artiklar:

Om du använder NGINX-ingresskontrollanten eller någon annan AKS-värdbaserad ingresskontrollant i stället för Azure Application Gateway kan du använda Azure Firewall för att inspektera trafik till och från AKS-klustret och skydda klustret från dataexfiltrering och annan oönstrad nätverkstrafik. Mer information finns i följande artiklar:

Bidragsgivare

Microsoft hanterar den här artikeln. Följande deltagare skrev den ursprungligen:

Huvudförfattare:

Övriga medarbetare: