Azure 本地网络网关动态 IP 更新





5.00/5 (2投票s)
无需静态 IP 和昂贵的 VPN 设备即可实现无缝 S2S VPN。
引言
站点到站点连接,通常称为 S2S,可用于跨 premises 和混合配置。S2S VPN 网关连接是一种 IPsec/IKE VPN 隧道连接,我们可以在 Azure 资源与家庭/企业网络之间建立。然而,这种类型的连接需要一些在家用实验室中并不总是可用的东西。
- 一个 Azure 订阅 - 去拿一个,这是容易的部分,还有一些不错的注册优惠。
- 本地网络中属于兼容 VPN 设备列表的 VPN 设备
- 最后,此 VPN 设备已分配了一个公共静态 IP,并且不在 NAT 之后。
嗯,VPN 设备和静态 IP 的要求提高了门槛。没有人有家用的 VPN 设备,甚至企业级别的 VPN 设备用于实验或开发目的,从各个角度来看都是一种过度。幸运的是,有一个便宜又简单的替代方案:设置 RRAS(路由和远程访问服务)服务器。本文的目的不是介绍如何建立 S2S 连接或 RRAS 服务器。对于前者和后者,有很棒的文章(在这里,谷歌是你的朋友 ;))。
实际上,本文的重点是“静态 IP 要求”。在家拥有静态 IP 的人非常少见。我们大多数人都有一个宽带连接,其动态 IP 地址会经常更改。S2S VPN 连接的核心元素之一是所谓的 **本地网络网关**,它是位于云虚拟网络中并指向本地位置的组件。我们需要为本地网络网关指定两个参数:
- 网关 **IP 地址**,它应该是你的 VPN 设备的 IP - 不能位于 NAT 之后(我永远不会厌倦强调的重要细节)。
- 地址前缀,即 Azure 将流量路由到的本地地址空间。
因此,我们真正需要的是一种方法,可以自动将我们的本地网络网关的 IP 地址与 ISP 分配给我们的宽带路由器的 IP 地址同步。
背景
当我们通过原生 .NET 代码管理虚拟机、虚拟网络、Web 或移动应用或存储帐户时,**.NET 管理库** 将派上用场。Azure 有两种不同的部署、创建和管理资源的模型。所谓的 Classic 和 Resource Manager。在本文中,我们将完全专注于 Resource Manager 模块,因为 Classic 正在缓慢弃用,以及管理库的 **Microsoft.Azure.Management.Network** 命名空间。
我们将开发一个简单的控制台应用程序,它将作为 Windows 计划任务在后台定期运行,该应用程序将识别我们当前的 IP 地址,然后静默更新本地网络网关的 IP 地址,从而在跨 premises 之间保持几乎无缝的 VPN 连接。
完成工作
首先,我们必须在 Azure AD 中注册一个新应用程序。它必须是 **本机应用**,因为我们在这里讨论的是一个控制台应用程序。重定向 URI 并不真正重要,但至少要填写一个有效且有意义的值。
授予 **Windows Azure 服务管理 API** 权限给你的应用。
并创建一个密钥,我们稍后在请求应用程序的身份验证令牌时会用到它。
为了能够使用 Resource Manager 模型 (ARM),我们可以通过消耗公开的 REST API 端点来实现,或者直接利用 Microsoft 提供的 .NET 包装器。因此,在一个新的控制台应用程序中添加以下 nuget 包:
- Microsoft.Azure.Management.ResourceManager
- Microsoft.Azure.Management.Network
- Microsoft.IdentityModel.Clients.ActiveDirectory
这些足以让我们开始获取身份验证令牌并使用 ARM 及其网络资源对应项。
然后,我们必须开始配置我们的应用程序。打开 app.config,并在 appsettings 块下添加以下条目:
key="login" value="https://login.windows.net/{0}">
key="aadInstance" value="https://login.microsoftonline.com/{0}">
key="tenantId" value="YOUR AZURE AD TENANT ID">
key="apiEndpoint" value="https://management.azure.com/">
key="clientId" value="THE APP-ID YOUR REGISTERED IN AZURE AD">
key="clientSecretKeyPreviewPortal" value="THE APP SECRET KEY">
key="redirectUri" value="http://iprefresh">
key="subscriptionId" value="YOUR SUBSCRIPTION ID">
key="resourceGroupName" value="rgHybridDevelopmentLab">
key="localNetworkGatewayName" value="localgw-hyde-01">
我们的下一个问题将是提供一种身份验证方法,该方法最终可以为我们提供我们将用于未来对 ARM API 的请求的访问令牌。我们可以通过以下方法做到这一点:
private static async Task<AuthenticationResult> GetAccessTokenAsync() { var authContextUrl = String.Format(login, tenantId); var cc = new ClientCredential(clientId, clientSecretKeyPreviewPortal); var context = new AuthenticationContext(authContextUrl); var token = await context.AcquireTokenAsync(apiEndPoint, cc); if (token == null) { throw new InvalidOperationException("Could not get the Access Token, please check the clientId and clientSecretKey values."); } return token; }
并在每一次后续请求中,我们可以像这样从 AuthenticationResult 对象中提取访问令牌:
var token = await GetAccessTokenAsync(); var credentials = new TokenCredentials(token.AccessToken);
接下来,我们需要构建一个方法来识别我们当前的公共 IP。注意,我们这里讨论的是 ISP 分配给我们的公共 IP,而不是本地网络 IP,也许是路由器 DHCP 服务器自动分配给我们的主机。通过调用 Web 上各种动态 DNS 服务的特殊端点,这很容易做到,它们很乐意免费帮助我们。
private static string GetCurrentIPAddress() { string ipAddress = String.Empty; try { string url = "http://checkip.dyndns.org"; System.Net.WebRequest req = System.Net.WebRequest.Create(url); System.Net.WebResponse resp = req.GetResponse(); System.IO.StreamReader sr = new System.IO.StreamReader(resp.GetResponseStream()); string response = sr.ReadToEnd().Trim(); string[] a = response.Split(':'); string a2 = a[1].Substring(1); string[] a3 = a2.Split('<'); ipAddress = a3[0]; } catch (Exception checkIpException) { ipAddress = checkIpException.Message; } return ipAddress; }
还没有完全达到目标。最后但最重要的一步。我们获得了 IP 地址,现在我们必须使用新值更新我们的本地网络网关。
private static async Task SetCurrentIPAddress(IPAddress ipaddr) { if (ipaddr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork) { try { Console.WriteLine("Getting Local Network Gateway..."); var token = await GetAccessTokenAsync(); var credentials = new TokenCredentials(token.AccessToken); NetworkManagementClient vnetMgmtClient = new NetworkManagementClient(credentials); vnetMgmtClient.SubscriptionId = subscriptionId; var localGwOperations = vnetMgmtClient.LocalNetworkGateways; var localGws = LocalNetworkGatewaysOperationsExtensions.List(localGwOperations, resourceGroupName); if (localGws != null) { var localGw = localGws.Where(lgw => lgw.Name == localNetworkGatewayName).SingleOrDefault(); string ipAddress = ipaddr.ToString(); if (localGw.GatewayIpAddress != ipAddress) { localGw.GatewayIpAddress = ipAddress; var updatedLocalGw = LocalNetworkGatewaysOperationsExtensions.CreateOrUpdate(localGwOperations, resourceGroupName, localGw.Name, localGw); Console.WriteLine("Updated Local Network Gateway. New IP Address : " + ipAddress); } Console.WriteLine("No changes applied to Local Network Gateway IP, all in sync."); } } catch (Exception setIpAddressException) { Console.WriteLine(setIpAddressException.Message); Console.WriteLine("Failed to update Local Network Gateway..."); } } }
这段代码的简要作用是查询预定义的资源组,并获取其中所有可用的本地网络网关的列表。然后找到与我们在 appsettings 中提供的名称匹配的那个。如果新 IP 地址与现有 IP 地址不同,则使用新值更新资源。
最后但同样重要的是,如果我们的应用程序没有足够的权限,更新将会失败。所以回到 Azure Preview Portal,将我们的应用程序分配为订阅级别的所有者(这仅用于演示目的,在实际生活中,您应该始终遵循最小特权原则,并在资源层次结构的适当级别定义它)。
使用代码
如果您读到这里,我假设您已经安装了 RRAS 服务器,并且设法使用 Azure 中的虚拟网络建立了 S2S 连接。创建一个计划任务,最好是在 RRAS 服务器上,它将定期 - 我选择了 5 分钟的间隔 - 获取新 IP 并更新本地网络网关,从而始终保持您的 VPN 正常运行。
然后访问 Azure Preview Portal 中的连接资源,并注意到您的连接状态始终是“已连接”,并且本地网络网关 IP 地址始终会自动刷新其值。
所提供的代码仅用于演示目的,不适用于生产环境。尤其是在性能方面,还有很大的改进空间。
历史
-