Help secure your Microsoft Teams channel bot and web app behind a firewall

Azure App Service
Azure Web Application Firewall

This example scenario helps secure the connection to a Microsoft Teams channel bot's web app by using Azure Private Link and Azure Private Endpoint. At the same time, it enables channels in the Teams client to communicate with the bot through an IP that's exposed through an Azure Firewall instance.

Architecture

Diagram showing the Teams-to-Azure Firewall flowchart.

Download a Visio file of this architecture.

Dataflow

  • Azure Virtual Network enables communications between Azure resources. The virtual network in this example uses the address space of 10.0.0.0/16, and contains three subnets for use by the scenario's required components:

    • Azure Firewall Subnet (10.0.1.0/26).

    • Virtual Network Integration Subnet (10.0.2.0/24), which is used to route traffic from the bot's private endpoint to the firewall.

    • Private Endpoint Subnet (10.0.3.0/24), which is used to route traffic from the firewall to the bot's private endpoint.

  • Azure Firewall exposes a single public IP address that clients can use to communicate with the underlying bot services. Ordinarily, a firewall is placed in its own virtual network, which is a common pattern for hub and spoke architectures, but this simplified example deploys all services and resources into a single virtual network. The Azure Firewall instance is placed in its own subnet.

  • Route table defines the routes that traffic takes within the virtual network. It ensures that traffic coming to and from the bot passes through the firewall.

    • The default route with the 0.0.0.0/0 address prefix instructs Azure to route traffic that isn't within the address prefix of any other route to the subnet where the Azure Firewall instance is deployed. In this example, it's the only route.

    • The Virtual Network Integration Subnet and the Private Endpoint Subnet are associated with the route table, ensuring that any traffic passing through them is routed through the firewall.

  • Bot Service consists of the bot app service plan, app service, and bot channels registration.

    • The app service has a registered custom domain that points to the IP address of the firewall. This way, the app service can be accessed only through the firewall.
  • Azure Private Link service for inbound access to the bot app service over an Azure private endpoint.

  • Virtual network integration connects the app service to the virtual network, ensuring that outbound traffic from the bot app service passes through the firewall.

Components

Alternatives

  • An App Service Environment can provide a fully isolated and dedicated environment for securely running App Service apps at high scale. This example doesn't make use of an App Service Environment to reduce costs, but the sample architecture could support it, with modifications.

Scenario details

Bots allow Teams users to interact with web services through text, interactive cards, and task modules. The Microsoft Bot Framework and Azure Bot Services give you an easy-to-use set of tools for creating and managing these bots.

You can develop bots by using a variety of languages, such as C#, JavaScript, and Python. After they're developed, you can deploy them to Azure. A key component of a bot is the web app, which contains the core logic and interface that users communicate with. One of the key requirements for the bot to work is that it must expose a publicly accessible HTTPS endpoint.

InfoSec policy commonly requires that all incoming traffic to web apps go through a corporate firewall. This means that all traffic that goes to a bot, and responses from the bot, must route through a corporate firewall, as with any other web app.

Potential use cases

Organizations can utilize bots for mobile and desktop users. Some examples include:

  • Simple queries. Bots can deliver an exact match to a query or a group of related matches to help with disambiguation.
  • Multi-turn interactions. By helping anticipate possible next steps, bots make it much easier for people to a complete task flow.
  • Reaching out to users. Bots can send a message when something has changed in a document or a work item is closed.

Considerations

Monitoring

Although monitoring isn't implemented in this example scenario, a bot's app service can utilize Azure Monitor services to monitor its availability and performance.

Scalability

The bots used in this scenario are hosted on Azure App Service. As a result, you can use the standard App Service autoscaling features to automatically scale the number of instances running your bot, which allows your bot to keep up with demand. For more information about autoscaling, see Autoscaling best practices.

For other scalability topics, see the Azure Architecture Center Performance efficiency checklist.

DevOps

It's a common practice to deploy web apps, API apps, and mobile apps to an Azure App Service plan by using continuous deployment pipelines. Because a secured bot's app service is protected with a private endpoint, externally hosted build agents don't have the access that's required to deploy updates. To work around this, you might need to use a solution such as Azure Pipeline self-hosted DevOps agents.

Security

Azure DDoS Protection, combined with application-design best practices, provides enhanced DDoS mitigation features to provide more defense against DDoS attacks. You should enable Azure DDOS Protection on any perimeter virtual network.

Deploy this scenario

Prerequisites

You must have an existing Azure account. If you don't have an Azure subscription, create a free account before you begin.

Walkthrough

  1. Run the following Azure CLI commands in Azure Cloud Shell or your preferred deployment shell.

    This set of commands creates the necessary resource group, virtual network, and subnets that are required for this walkthrough. The IP range used by Teams is 52.112.0.0/14,52.122.0.0/15.

    # Declare variables (bash syntax)
    export PREFIX='SecureBot'
    export RG_NAME='rg-'${PREFIX}
    export VNET_NAME='vnet-'${PREFIX}
    export SUBNET_INT_NAME='VnetIntegrationSubnet'
    export SUBNET_PVT_NAME='PrivateEndpointSubnet'
    export LOCATION='eastus'
    export TEAMS_IP_RANGE='52.112.0.0/14 52.122.0.0/15'
    export FIREWALL_NAME='afw-'${LOCATION}'-'${PREFIX}
    
    # Create a resource group
    az group create --name ${RG_NAME} --location ${LOCATION}
    
    # Create a virtual network with a subnet for the firewall
    az network vnet create \
    --name ${VNET_NAME} \
    --resource-group ${RG_NAME} \
    --location ${LOCATION} \
    --address-prefix 10.0.0.0/16 \
    --subnet-name AzureFirewallSubnet \
    --subnet-prefix 10.0.1.0/26
    
    # Add a subnet for the Virtual network integration
    az network vnet subnet create \
    --name ${SUBNET_INT_NAME} \
    --resource-group ${RG_NAME} \
    --vnet-name ${VNET_NAME} \
    --address-prefix 10.0.2.0/24
    
    # Add a subnet where the private endpoint will be deployed for the app service
    az network vnet subnet create \
    --name ${SUBNET_PVT_NAME} \
    --resource-group ${RG_NAME} \
    --vnet-name ${VNET_NAME} \
    --address-prefix 10.0.3.0/24
    

    When you create a private endpoint subnet, the private endpoint policies are disabled by default.

    When the deployment is complete, you should see the following subnets within your virtual network:

    Screenshot of the vnet-SecureBot "Subnets" pane.

  2. Deploy an Azure Firewall instance into the firewall subnet that you created in step 1 by running the following CLI commands:

    # Create a firewall
    az network firewall create \
        --name ${FIREWALL_NAME} \
        --resource-group ${RG_NAME} \
        --location ${LOCATION}
    
    # Create a public IP for the firewall
    az network public-ip create \
        --name ${FIREWALL_NAME}-pip \
        --resource-group ${RG_NAME} \
        --location ${LOCATION} \
        --allocation-method static \
        --sku standard
    
    # Associate the IP with the firewall
    az network firewall ip-config create \
        --firewall-name ${FIREWALL_NAME} \
        --name ${FIREWALL_NAME}-Config \
        --public-ip-address ${FIREWALL_NAME}-pip \
        --resource-group ${RG_NAME} \
        --vnet-name ${VNET_NAME}
    
    # Update the firewall
    az network firewall update \
        --name ${FIREWALL_NAME} \
        --resource-group ${RG_NAME}
    
    # Get the public IP address for the firewall and take note of it for later use
    az network public-ip show \
        --name ${FIREWALL_NAME}-pip \
        --resource-group ${RG_NAME}
    

    Your firewall configuration should look something like this:

    Screenshot of fw-SecureBot firewall configuration.

  3. Create a basic bot.

  4. Deploy the basic bot into the resource group that you created in step 1.

    As part of this process, you'll create an app registration, which you need to interact with the bot via channels. During this process, you'll also deploy the necessary App Service plan, app service, and web app bot.

    Note

    Select an App Service plan that supports Azure Private Link.

  5. Map a custom domain to the app service that you deployed to the resource group in step 3.

    This step requires access to your domain registrar, and it requires you to add an A-record to the custom domain that points to the public IP of the firewall you created in step 2.

  6. Secure the mapped custom domain by either uploading an existing certificate for the domain or purchasing an App Service Certificate in Azure and importing it. You can do this by following the steps in Secure a custom DNS name with a TLS/SSL binding in Azure App Service.

    You should now have a fully functional bot that you can add to a channel in Teams or test through Web Chat by using the directions found in the Bot Framework SDK documentation.

    Note

    At this point the bot's app service is still publicly accessible over both the azurewebsites.net URL and over the custom URL you configured. In the next steps, you'll use private endpoints to disable public access. You'll also configure the firewall to allow the bot service to communicate only with Teams clients.

  7. Run the following Azure CLI script to deploy and configure the private endpoint. This step also implements virtual network integration for the bot's app service, which connects it to your virtual network's integration subnet.

    # Disable private endpoint network policies (this step is not required if you're using the Azure portal)
    az network vnet subnet update \
      --name ${SUBNET_PVT_NAME} \
      --resource-group ${RG_NAME} \
      --vnet-name ${VNET_NAME} \
      --disable-private-endpoint-network-policies true
    
    # Create the private endpoint, being sure to copy the correct resource ID from your deployment of the bot app service
    # The ID can be viewed by using the following CLI command:
    # az resource show --name wapp-securebot --resource-group rg-securebot --resource-type Microsoft.web/sites --query "id" 
    az network private-endpoint create \
      --name pvt-${PREFIX}Endpoint \
      --resource-group ${RG_NAME} \
      --location ${LOCATION} \
      --vnet-name ${VNET_NAME} \
      --subnet ${SUBNET_PVT_NAME} \
      --connection-name conn-${PREFIX} \
      --private-connection-resource-id /subscriptions/cad87d9e-c941-4519-a638-c9804a0577b9/resourceGroups/rg-securebot/providers/Microsoft.Web/sites/wapp-securebot \
      --group-id sites
    
    # Create a private DNS zone to resolve the name of the app service
    az network private-dns zone create \
      --name ${PREFIX}privatelink.azurewebsites.net \
      --resource-group ${RG_NAME}
    
    az network private-dns link vnet create \
      --name ${PREFIX}-DNSLink \
      --resource-group ${RG_NAME} \
      --registration-enabled false \
      --virtual-network ${VNET_NAME} \
      --zone-name ${PREFIX}privatelink.azurewebsites.net
    
    az network private-endpoint dns-zone-group create \
      --name chatBotZoneGroup \
      --resource-group ${RG_NAME} \
      --endpoint-name pvt-${PREFIX}Endpoint \
      --private-dns-zone ${PREFIX}privatelink.azurewebsites.net \
      --zone-name ${PREFIX}privatelink.azurewebsites.net
    
    # Establish virtual network integration for outbound traffic
    az webapp vnet-integration add \
      -g ${RG_NAME} \
      -n wapp-${PREFIX} \
      --vnet ${VNET_NAME} \
      --subnet ${SUBNET_INT_NAME}
    

    After you've run these commands, you should see the following resources in your resource group:

    Screenshot showing a list of resources in a resource group.

    The VNet Integration option under the Networking section of your app service should look like this:

    Screenshot of the wapp-securebot "Networking" options.

    Screenshot of the "VNet Integration" option.

    Screenshot of the "Private Endpoint connections" pane.

  8. Next, you create a route table to ensure that traffic to and from each subnet goes through the firewall. You'll need the private IP address of the firewall that you created in the previous step.

    # Create a route table
    az network route-table create \
      -g ${RG_NAME} \
      -n rt-${PREFIX}RouteTable
    
    # Create a default route with 0.0.0.0/0 prefix and the next hop as the Azure firewall virtual appliance to inspect all traffic. Make sure you use your firewall's internal IP address instead of 10.0.1.4
    az network route-table route create -g ${RG_NAME} \
      --route-table-name rt-${PREFIX}RouteTable -n default \
      --next-hop-type VirtualAppliance \
      --address-prefix 0.0.0.0/0 \
      --next-hop-ip-address 10.0.1.4
    
    # Associate the two subnets with the route table
    az network vnet subnet update -g ${RG_NAME} \
      -n ${SUBNET_INT_NAME} --vnet-name ${VNET_NAME} \
      --route-table rt-${PREFIX}RouteTable
    
    az network vnet subnet update -g ${RG_NAME} \
      -n ${SUBNET_PVT_NAME} \
      --vnet-name ${VNET_NAME} \
      --route-table rt-${PREFIX}RouteTable
    

    After you've run the commands, your route table resource should look like this:

    Screenshot of the rt-SecureBotRouteTable pane.

    After you've created the route table, you add rules to your firewall to deliver traffic from the public IP to the bot app service, and to restrict traffic from any endpoint other than Microsoft Teams. In addition, you'll allow traffic between the virtual network and Azure Bot Services or Microsoft Entra ID by using service tags.

  9. Run the following commands:

    # Create a NAT rule collection and a single rule. The source address is the public IP range of Microsoft Teams
    # Destination address is that of the firewall. 
    # The translated address is that of the app service's private link.
    az network firewall nat-rule create \
      --resource-group ${RG_NAME} \
      --collection-name coll-${PREFIX}-nat-rules \
      --priority 200 \
      --action DNAT \
      --source-addresses ${TEAMS_IP_RANGE} \
      --dest-addr 23.100.26.84 \
      --destination-ports 443 \
      --firewall-name ${FIREWALL_NAME} \
      --name rl-ip2appservice \
      --protocols TCP \
      --translated-address 10.0.3.4 \
      --translated-port 443
    
    # Create a network rule collection and add three rules to it. 
    # The first one is an outbound network rule to only allow traffic to the Teams IP range.
    # The source address is that of the virtual network address space, destination is the Teams IP range.
    az network firewall network-rule create \
      --resource-group ${RG_NAME} \
      --collection-name coll-${PREFIX}-network-rules \
      --priority 200 \
      --action Allow \
      --source-addresses 10.0.0.0/16 \
      --dest-addr ${TEAMS_IP_RANGE} \
      --destination-ports 443 \
      --firewall-name ${FIREWALL_NAME} \
      --name rl-OutboundTeamsTraffic \
      --protocols TCP
    
    # This rule will enable traffic to all IP addresses associated with Azure AD service tag
    az network firewall network-rule create \
      --resource-group ${RG_NAME} \
      --collection-name coll-${PREFIX}-network-rules \
      --source-addresses 10.0.0.0/16 \
      --dest-addr AzureActiveDirectory \
      --destination-ports '*' \
      --firewall-name ${FIREWALL_NAME} \
      --name rl-AzureAD \
      --protocols TCP
    
    # This rule will enable traffic to all IP addresses associated with Azure Bot Services service tag
    az network firewall network-rule create \
      --resource-group ${RG_NAME} \
      --collection-name coll-${PREFIX}-network-rules \
      --source-addresses 10.0.0.0/16 \
      --dest-addr AzureBotService \
      --destination-ports '*' \
      --firewall-name ${FIREWALL_NAME} \
      --name rl-AzureBotService \
      --protocols TCP
    

    After you've run the commands, your firewall rules will look something like this:

    Screenshot of the "Edit NAT rule collection" pane.

    Screenshot of the "Edit network rule collection" pane.

  10. Confirm that your bot is accessible only from a channel in Teams, and that all traffic to and from the bot app service goes through your firewall.

Contributors

This article is maintained by Microsoft. It was originally written by the following contributors.

Principal author:

Next steps