|
| 1 | +--- |
| 2 | +title: SignalR Application Firewall (Preview) |
| 3 | +description: An introduction about why and how to set up Application Firewall for Azure SignalR service |
| 4 | +author: biqian |
| 5 | +ms.service: signalr |
| 6 | +ms.custom: devx-track-azurecli |
| 7 | +ms.topic: how-to |
| 8 | +ms.date: 07/10/2024 |
| 9 | +ms.author: biqian |
| 10 | +--- |
| 11 | +# Application Firewall for Azure SignalR Service |
| 12 | + |
| 13 | +The Application Firewall provides sophisticated control over client connections in a distributed system. Before diving into its functionality and setup, let's clarify what the Application Firewall does not do: |
| 14 | + |
| 15 | +1. It does not replace authentication. The firewall operates behind the client connection authentication layer. |
| 16 | +2. It is not related to network layer access control. |
| 17 | + |
| 18 | +## What Does the Application Firewall Do? |
| 19 | + |
| 20 | +The Application Firewall consists of various rule lists. Currently, there is a rule list called *Client Connection Count Rules*. Future updates will support more rule lists to control aspects like connection lifetime and message throughput. |
| 21 | + |
| 22 | +This guideline is divided into three parts: |
| 23 | +1. Introduction to different application firewall rules. |
| 24 | +2. Instructions on configuring the rules using the Portal or Bicep on the SignalR service side. |
| 25 | +3. Steps to configure the token on the server side. |
| 26 | + |
| 27 | +## Prerequisites |
| 28 | + |
| 29 | +* An Azure SignalR Service in [Premium tier](https://azure.microsoft.com/pricing/details/signalr-service/). |
| 30 | + |
| 31 | +## Client Connection Count Rules |
| 32 | +Client Connection Count Rules restrict concurrent client connections. When a client attempts to establish a new connection, the rules are checked **sequentially**. If any rule is violated, the connection is rejected with a status code 429. |
| 33 | + |
| 34 | + #### ThrottleByUserIdRule |
| 35 | + This rule limits the concurrent connections of a user. For example, if a user opens multiple browser tabs or logs in using different devices, you can use this rule to restrict the number of concurrent connections for that user. |
| 36 | + |
| 37 | + > [!NOTE] |
| 38 | + > * The **UserId** must exist in the access token for this rule to work. Refer to [Configure access token](#configure-access-token). |
| 39 | +
|
| 40 | + |
| 41 | + #### ThrottleByJwtSignatureRule |
| 42 | + This rule limits the concurrent connections of the same token to prevent malicious users from reusing tokens to establish infinite connections, which can exhaust connection quota. |
| 43 | + |
| 44 | + > [!NOTE] |
| 45 | + > * It's not guaranteed by default that tokens generated by the SDK are different each time. Though each token contains a timestamp, this timestamp might be the same if vast tokens are generated within seconds. To avoid identical tokens, insert a random claim into the token claims. Refer to [Configure access token](#configure-access-token). |
| 46 | +
|
| 47 | + |
| 48 | + #### ThrottleByJwtCustomClaimRule |
| 49 | + |
| 50 | + More advanced, connections could be grouped into different groups according to custom claim. Connections with the same claim are aggregated to do the check. For example, you could add a **ThrottleByJwtCustomClaimRule** to allow 5 concurrent connections with custom claim name *freeUser*. |
| 51 | + |
| 52 | + > [!NOTE] |
| 53 | + > * The rule applies to all claims with a certain claim name. The connection count aggregation is on the same claim (including claim name and claim value). The *ThrottleByUserIdRule* is a special case of this rule, applying to all connections with the userIdentity claim. |
| 54 | + |
| 55 | + |
| 56 | +> [!WARNING] |
| 57 | +> * **Avoid using too aggressive maxCount**. Client connections may close without completing the TCP handshake. SignalR service can't detect those "half-closed" connections immediately. The connection is taken as active until the heartbeat failure. Therefore, aggressive throttling strategies might unexpectedly throttle clients. A smoother approach is to **leave some buffer** for the connection count, for example: double the *maxCount*. |
| 58 | +
|
| 59 | + |
| 60 | + |
| 61 | +## Set up Application Firewall |
| 62 | + |
| 63 | +# [Portal](#tab/Portal) |
| 64 | +To use Application Firewall, navigate to the SignalR **Application Firewall** blade on the Azure portal and click **Add** to add a rule. |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +# [Bicep](#tab/Bicep) |
| 69 | + |
| 70 | +Use Visual Studio Code or your favorite editor to create a file with the following content and name it main.bicep: |
| 71 | + |
| 72 | +```bicep |
| 73 | +@description('The name for your SignalR service') |
| 74 | +param resourceName string = 'contoso' |
| 75 | +
|
| 76 | +resource signalr 'Microsoft.SignalRService/signalr@2024-04-01-preview' = { |
| 77 | + name: resourceName |
| 78 | + properties: { |
| 79 | + applicationFirewall:{ |
| 80 | + clientConnectionCountRules:[ |
| 81 | + // Add or remove rules as needed |
| 82 | + { |
| 83 | + // This rule will be skipped if no userId is set |
| 84 | + type: 'ThrottleByUserIdRule' |
| 85 | + maxCount: 5 |
| 86 | + } |
| 87 | + { |
| 88 | + type: 'ThrottleByJwtSignatureRule' |
| 89 | + maxCount: 10 |
| 90 | + } |
| 91 | + { |
| 92 | + // This rule will be skipped if no freeUser claim is set |
| 93 | + type: 'ThrottleByJwtCustomClaimRule' |
| 94 | + maxCount: 10 |
| 95 | + claimName: 'freeUser' |
| 96 | + } |
| 97 | + { |
| 98 | + // This rule will be skipped if no paidUser claim is set |
| 99 | + type: 'ThrottleByJwtCustomClaimRule' |
| 100 | + maxCount: 100 |
| 101 | + claimName: 'paidUser' |
| 102 | + } |
| 103 | + ] |
| 104 | + } |
| 105 | + } |
| 106 | +} |
| 107 | +
|
| 108 | +``` |
| 109 | + |
| 110 | +Deploy the Bicep file using Azure CLI |
| 111 | + ```azurecli |
| 112 | + az deployment group create --resource-group MyResourceGroup --template-file main.bicep |
| 113 | + ``` |
| 114 | + |
| 115 | +---- |
| 116 | + |
| 117 | + |
| 118 | + |
| 119 | +## Configure access token |
| 120 | +The application firewall rules only take effect when the access token contains the corresponding claim. A rule is **skipped** if the connection does not have the corresponding claim. |
| 121 | + |
| 122 | +Below is an example to add userId or custom claim in the access token in **Default Mode**: |
| 123 | + |
| 124 | +```cs |
| 125 | +services.AddSignalR().AddAzureSignalR(options => |
| 126 | + { |
| 127 | + // Add necessary claims according to your rules. |
| 128 | + options.ClaimsProvider = context => new[] |
| 129 | + { |
| 130 | + // Add UserId: Used in ThrottleByUserIdRule |
| 131 | + new Claim(ClaimTypes.NameIdentifier, context.Request.Query["username"]), |
| 132 | + |
| 133 | + // Add unique claim: Ensure uniqueness when using ThrottleByJwtSignatureRule. |
| 134 | + // The token name is not important. You could change it as you like. |
| 135 | + new Claim("uniqueToken", Guid.NewGuid().ToString()), |
| 136 | + |
| 137 | + // Cutom claim: Used in ThrottleByJwtCustomClaimRule |
| 138 | + new Claim("<Custom Claim Name>", "<Custom Claim Value>"), |
| 139 | + // Custom claim example |
| 140 | + new Claim("freeUser", context.Request.Query["username"]), |
| 141 | + }; |
| 142 | + }); |
| 143 | +``` |
| 144 | +The logic for **Serverless Mode** is similar. |
| 145 | + |
| 146 | +For more details, refer to [Client negotiation](signalr-concept-client-negotiation.md#what-can-you-do-during-negotiation) . |
| 147 | + |
| 148 | + |
| 149 | + |
| 150 | + |
| 151 | + |
0 commit comments