Bei der Entwicklung von Cloud-Lösungen ist man heutzutage nicht mehr darauf beschränkt, einzig und allein Cloud-Ressourcen zu verwenden. Das Ansprechen und Einbinden von On-Premises-Ressourcen gehört mittlerweile genauso zur Entwicklung dazu, wie die automatisierte Bereitstellung von Ressourcen via Pipelines. Die On-Premises-Server haben meistens und richtigerweise eine Firewall vorgeschaltet, welche nur bestimmte IP-Adressen und Port-Protokolle zulässt. Nun klingt es erstmal simpel, dass der Cloud-Entwickler eine Liste von IP-Adressen zur Verfügung stellt, welche das Team der Netzwerkverantwortlichen in der Firewall in die Whitelist aufnimmt. Wenn man sich allerdings die möglichen IP-Adressen der Azure-Datencenter anschaut, ist die Liste der möglichen IP-Adressen sehr lang. So wird schnell klar: Man sollte nicht eine Liste von über 30 IP-Adressen in der Firewall freischalten.
In diesem Beitrag möchte ich zeigen, wie wir mithilfe von Azure Functions eine festgelegte Outbound-IP-Adresse definieren, mit welcher die Azure Functions “nach draußen” kommuniziert.
Zielarchitektur
Unsere Zielarchitektur beinhaltet eine Azure Function, welche über ein VNET und NAT mithilfe einer definierte öffentliche IP-Adresse nach außen (outbound) kommuniziert. In diesem Beispiel wurde exemplarisch ein On-Premises-Server mit vorgeschalteter Firewall dargestellt.
Azure Function Outbound-IP-Adressen
Zunächst einmal möchten wir einsehen, welche IP-Adressen einer Azure Function überhaupt verwendet werden. Hierbei wird zwischen outboundIpAddresses
und possibleOutboundIpAddresses
unterschieden. Dabei stehen die outboundIpAddresses
für die gerade in Verwendung befindlichen IP-Adressen. Die possibleOutboundIpAddresses
beinhalten somit alle möglichen Outbound-IP-Adressen für diese Azure Function. Die outboundIpAddresses
bilden demnach eine Untermenge der possibleOutboundIpAddresses
. Diese kann man sich über die Azure CLI anzeigen lassen.
Das heißt, wir melden uns zunächst via Azure CLI an und wählen die entsprechende Subscription für uns aus.
az login
az account set -s "Subscription-GUID"
Mit dem folgenden Befehl erhalten wir eine Liste der Untermenge outboundIpAddresses
:
az functionapp show --resource-group "Ressource-Group" --name "Function-Name" --query outboundIpAddresses --output tsv
Durch Anpassen des query
-Parameter zu possibleOutboundIpAddresses
erhalten wir die komplette Liste aller möglichen IP-Adressen:
az functionapp show --resource-group "Ressource-Group" --name "Function-Name" --query possibleOutboundIpAddresses --output tsv
Umsetzen der Zielarchitektur mit Azure CLI
In diesem Szenario gehen wir davon aus, dass noch keine Azure-Ressourcen bereitgestellt wurden und somit alle Ressourcen from scratch erstellt werden sollen.
Erstellen der Netzwerkressourcen
Zunächst müssen alle notwendigen Netzwerkressourcen, wie das VNET, einer Public-IP und einem NAT erzeugt werden.
Beginnen wir mit dem VNET, welches wir in West-Europa und mit dem Standard-Adress-Prefix 10.0.0.0/16 bereitstellen möchten:
az network vnet create --resource-group "MyRessourceGroup" --name "vnet-okblog-euwest" --location westeurope --address-prefix 10.0.0.0/16
Anschließend erstellen wir ein Subnet, welches dem bestehenden VNET zugeordnet wird:
az network vnet subnet create --name "snet-okblog-euwest-prod" --resource-group "MyRessourceGroup" --vnet-name "vnet-okblog-euwest" --address-prefixes 10.0.0.0/29 --delegations 'Microsoft.Web/serverFarms'
Nun erstellen wir eine Public-IP-Adresse, welche später unsere Outbound-IP-Adresse für die Requests der Azure Function verwendet:
az network public-ip create --name "pip-func-okblog-euwest-prod" --resource-group "MyRessourceGroup" --version "IPv4" --tier "Regional" --sku "Standard" --location westeurope
Als letzte Netzwerkressource wird das NAT erstellt, welche unsere zuvor erstellte öffentliche IP-Adresse als public-ip-addresses
zugewiesen hat:
az network nat gateway create --name "nat-func-okblog-euwest-prod" --resource-group "MyRessourceGroup" --location westeurope --public-ip-addresses "pip-func-okblog-euwest-prod"
Durch ein Update des Subnets mit dem entsprechenden NAT-Gateway ist die Konfiguration der Netzwerkressourcen abgeschlossen:
az network vnet subnet update --resource-group "MyRessourceGroup"--vnet-name "vnet-okblog-euwest" --name "snet-okblog-euwest-prod" --nat-gateway "nat-func-okblog-euwest-prod"
Azure Function
Azure Functions können VNETs nur in einem Serviceplan verwenden (also kein Consumption-Plan). Hierzu wird zunächst ein App-Serviceplan benötigt, welchen wir in westeurope
im Standardplan S1 erstellen:
$appServicePlanId = az appservice plan create --resource-group "MyRessourceGroup" --name "MyAppServicPlanName" --location westeurope --sku S1
Die Service-Plan-ID können wir nun verwenden, um die zugehörige Azure Function zu erstellen:
az functionapp create --resource-group "MyRessourceGroup" --plan ($appServicePlanId | convertfrom-json).id --name "MyFunctionName" --storage-account "MyStorageAccountName" --functions-version 4 --runtime dotnet --os Windows
Nun muss nur noch die entsprechende vnet-integration
zur Function-App hinzugefügt werden:
$myExistingVnet = az network vnet list --resource-group "MyRessourceGroup" --query "[?name=='vnet-okblog-euwest']" | convertfrom-json
az functionapp vnet-integration add --resource-group "MyRessourceGroup" --name "MyFunctionName" --vnet $myExistingVnet.id --subnet "snet-okblog-euwest-prod"
Testen der Netzwerkfunktionalität
Um zu prüfen, ob das Erzeugen der Ressourcen und die Konfiguration derselben funktioniert hat und wir nun nur noch eine Outbound-IP-Adresse der Azure Function zur Folge haben, können wir eine Hilfsfunktion in dem Functions-Projekt hinzufügen. Diese Hilfsfunktion ruft intern die Adresse https://ifconfig.me
auf. Die Antwort beinhaltet die IP-Adresse, über welche ein Request ausgeführt wurde - also der Outbound-IP-Adresse. Natürlich muss die Function zunächst in Azure deployed sein, damit man die zuvor erstellte öffentliche IP-Adresse als Antwort der Function erhält.
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.Extensions.Logging;
using System.Net.Http;
using System.Threading.Tasks;
namespace Application.Functions.Todo
{
public static class IfConfigFunction
{
[FunctionName("IfConfig")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", Route = "ifconfig")] HttpRequest req, ILogger log)
{
var client = new HttpClient();
var response = await client.GetAsync(@"https://ifconfig.me");
var responseMessage = await response.Content.ReadAsStringAsync();
return new OkObjectResult(responseMessage);
}
}
}
Zusammenfassung
In diesem Blog-Beitrag konnte ich einen Überblick geben, wie man zum einen die verfügbaren Outbound-IP-Adressen verwaltet (outboundIpAddresses
und possibleOutboundIpAddresses
) und zu anderen wie man die Outbound-IP-Adressen auf eine einzige reduzieren kann. Somit muss der Netzwerkadministrator nicht mehr eine sehr große Liste von IP-Adressen als Ausnahme in der Firewall freischalten, sondern kann ganz gezielt auf die zuvor definierte IP-Adresse festlegen. Mit der Hilfs-Function kann man als Entwickler zum Ende der Erzeugung der Netzwerkressourcen prüfen, ob alles ordnungsgemäß funktioniert.