Skip to content

[AzureFirewall] NAT and FQDN Tags #7230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 47 commits into from
Sep 14, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
bb247b2
Azure Firewall NAT Rule Collection
fanymanea Sep 9, 2018
8b004b0
AzFw FQDN Tag
fanymanea Sep 9, 2018
b5687b5
Add validation for DNAT in particular. This validation will be remove…
Sep 10, 2018
2c2a8ae
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallNatRule…
Sep 10, 2018
c9fd462
Add NAT Rule Collections to ChangeLog
Sep 10, 2018
02dde51
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallFqdnTags
Sep 10, 2018
18faf9f
Update unsupported format
Sep 11, 2018
65c4a42
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallNatRule…
Sep 11, 2018
66da20c
Add NatRuleCollection to NetworkRMProfile and format.ps1xml
Sep 11, 2018
ffb268d
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallFqdnTags
Sep 11, 2018
3f5351e
Add test owner to FqdnTag test
Sep 11, 2018
d45a2c1
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallNatRule…
Sep 12, 2018
9f31c97
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallFqdnTags
Sep 12, 2018
4c83d74
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallNatRule…
Sep 12, 2018
8973ee3
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallFqdnTags
Sep 12, 2018
fa97a2d
Add aliases for NAT Rule Collection
Sep 12, 2018
041a427
Move AzFw tests to westcentralus again
Sep 12, 2018
744ef4a
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallNatRule…
Sep 12, 2018
8b7ca7d
Merge branch 'AzureFirewallOnAugustVersion' into AzureFirewallFqdnTags
Sep 12, 2018
99e976c
Rename NatRC cmdlets to use the AzureRMPrefix
Sep 12, 2018
806a72d
Merge branch 'AzureFirewallFqdnTags' into AzureFirewallNatAndFqdnTags
Sep 12, 2018
3c87281
Rename FQDN Tag cmdlet to include AzureRMPrefix
Sep 12, 2018
603bf24
Remove all FQDN Tag cmdlet traces
Sep 12, 2018
3b7359a
Provide good data for NAT tests
Sep 13, 2018
db8a895
Merge branch 'network-september-release' into AzureFirewallNatAndFqdn…
Sep 13, 2018
2c5a0bb
Use the right name for cmdlet
Sep 13, 2018
ad6ea28
Update New-AzureFirewall cmdlet help to include NatRuleCollection
Sep 13, 2018
8c46249
Add a DNAT example in New-AzureFirewall cmdlet help
Sep 13, 2018
3092d06
Help adjustment
Sep 13, 2018
d68b014
Update help to mention the hardcoded list of FQDN Tags we support in …
Sep 13, 2018
a853cbf
Make the list a list
Sep 13, 2018
828e0e8
Add an empty line before the list to make it a nice markdown list
Sep 13, 2018
bd39ed5
Make TargetFqdns and FqdnTags part of separate param sets in Applicat…
Sep 13, 2018
aeedc84
Remove action type from Nat test - only DNAT is supported for now
Sep 13, 2018
7b77ada
Re-register AzFwCRUD test
Sep 13, 2018
cd33d78
Use parameter sets for ApplicationRule - docs to be regenerated
Sep 13, 2018
68559bb
Save csproj
Sep 13, 2018
65ded2c
Regenerate ApplicationRule help file with platy
fanymanea Sep 13, 2018
e6a51eb
Mark the default parameter set for New-FirewallApplicationRule
fanymanea Sep 13, 2018
4f511a1
Mark the default parameter set for New-FirewallApplicationRule
fanymanea Sep 13, 2018
5c98e02
Regenerate NatRule* help with platy
fanymanea Sep 13, 2018
40d9956
Merge branch 'network-september-release' into AzureFirewallNatAndFqdn…
fanymanea Sep 13, 2018
88f37e0
Remove extra files
fanymanea Sep 13, 2018
c6c1dc5
Correct the autogenerated md file
Sep 13, 2018
81c7f26
Merge branch 'network-september-release' into AzureFirewallNatAndFqdn…
Sep 13, 2018
f60f6c0
Merge branch 'network-september-release' into AzureFirewallNatAndFqdn…
Sep 13, 2018
5355c97
Do not allow FQDN Tags with spaces
Sep 13, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/ResourceManager/Network/Commands.Network/Az.Network.psd1
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,8 @@ CmdletsToExport = 'Add-AzApplicationGatewayAuthenticationCertificate',
'Remove-AzFirewall',
'New-AzFirewallApplicationRuleCollection',
'New-AzFirewallApplicationRule',
'New-AzFirewallNatRuleCollection',
'New-AzFirewallNatRule',
'New-AzFirewallNetworkRuleCollection',
'New-AzFirewallNetworkRule',
'Get-AzInterfaceEndpoint'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

namespace Microsoft.Azure.Commands.Network
{
public static class AzureFirewallApplicationRuleParameterSets
{
public const string TargetFqdn = @"TargetFqdn";

public const string FqdnTag = @"FqdnTag";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,20 @@
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text.RegularExpressions;
using Microsoft.Azure.Commands.Network.Models;
using MNM = Microsoft.Azure.Management.Network.Models;

namespace Microsoft.Azure.Commands.Network
{
[Cmdlet(VerbsCommon.New, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "FirewallApplicationRule", SupportsShouldProcess = true), OutputType(typeof(PSAzureFirewallApplicationRule))]
[Cmdlet(VerbsCommon.New, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "FirewallApplicationRule", SupportsShouldProcess = true, DefaultParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn), OutputType(typeof(PSAzureFirewallApplicationRule))]
public class NewAzureFirewallApplicationRuleCommand : NetworkBaseCmdlet
{
[Parameter(
Mandatory = true,
ParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn,
HelpMessage = "The name of the Application Rule")]
[Parameter(
Mandatory = true,
ParameterSetName = AzureFirewallApplicationRuleParameterSets.FqdnTag,
HelpMessage = "The name of the Application Rule")]
[ValidateNotNullOrEmpty]
public virtual string Name { get; set; }
Expand All @@ -45,20 +48,35 @@ public class NewAzureFirewallApplicationRuleCommand : NetworkBaseCmdlet

[Parameter(
Mandatory = true,
ParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn,
HelpMessage = "The target FQDNs of the rule")]
[ValidateNotNullOrEmpty]
public List<string> TargetFqdn { get; set; }

[Parameter(
Mandatory = true,
ParameterSetName = AzureFirewallApplicationRuleParameterSets.FqdnTag,
HelpMessage = "The FQDN Tags of the rule")]
[ValidateNotNullOrEmpty]
public List<string> FqdnTag { get; set; }

[Parameter(
Mandatory = true,
ParameterSetName = AzureFirewallApplicationRuleParameterSets.TargetFqdn,
HelpMessage = "The protocols of the rule")]
[ValidateNotNullOrEmpty]
public List<string> Protocol { get; set; }

public override void Execute()
{
base.Execute();


if (FqdnTag != null)
{
this.Protocol = new List<string> { "http", "https" };
FqdnTag = AzureFirewallFqdnTagHelper.MapUserInputToAllowedFqdnTags(FqdnTag);
}

var protocolsAsWeExpectThem = MapUserProtocolsToFirewallProtocols(Protocol);

var applicationRule = new PSAzureFirewallApplicationRule
Expand All @@ -67,63 +85,15 @@ public override void Execute()
Description = this.Description,
SourceAddresses = this.SourceAddress,
Protocols = protocolsAsWeExpectThem,
TargetFqdns = this.TargetFqdn
TargetFqdns = this.TargetFqdn,
FqdnTags = this.FqdnTag
};
WriteObject(applicationRule);
}

private List<PSAzureFirewallApplicationRuleProtocol> MapUserProtocolsToFirewallProtocols(List<string> userProtocols)
{
var protocolRegEx = new Regex("^[hH][tT][tT][pP][sS]?(:[1-9][0-9]*)?$");

var supportedProtocolsAndTheirDefaultPorts = new List<PSAzureFirewallApplicationRuleProtocol>
{
new PSAzureFirewallApplicationRuleProtocol { ProtocolType = MNM.AzureFirewallApplicationRuleProtocolType.Http, Port = 80 },
new PSAzureFirewallApplicationRuleProtocol { ProtocolType = MNM.AzureFirewallApplicationRuleProtocolType.Https, Port = 443 }
};

// User can pass "http", "HTtP" or "hTTp:8080"
var protocolsAsWeExpectThem = this.Protocol.Select(userText =>
{
//The actual validation is performed in NRP. Here we are just trying to map user info to our model
if (!protocolRegEx.IsMatch(userText))
{
throw new ArgumentException($"Invalid protocol {userText}");
}

var userParts = userText.Split(':');
var userProtocolText = userParts[0];
var userPortText = userParts.Length == 2 ? userParts[1] : null;

PSAzureFirewallApplicationRuleProtocol supportedProtocol;
try
{
supportedProtocol = supportedProtocolsAndTheirDefaultPorts.Single(protocol => protocol.ProtocolType.Equals(userProtocolText, StringComparison.InvariantCultureIgnoreCase));
}
catch
{
throw new ArgumentException($"Unsupported protocol {userProtocolText}. Supported protocols are {string.Join(", ", supportedProtocolsAndTheirDefaultPorts.Select(proto => proto.ProtocolType))}", nameof(Protocol));
}

uint port;
if (userPortText == null)
{
// Use default port for this protocol
port = supportedProtocol.Port;
}
else if (!uint.TryParse(userPortText, out port))
{
throw new ArgumentException($"Invalid port {userText}", nameof(Protocol));
}

return new PSAzureFirewallApplicationRuleProtocol
{
ProtocolType = supportedProtocol.ProtocolType,
Port = port
};
}).ToList();

return protocolsAsWeExpectThem;
return userProtocols.Select(PSAzureFirewallApplicationRuleProtocol.MapUserInputToApplicationRuleProtocol).ToList();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using MNM = Microsoft.Azure.Management.Network.Models;

namespace Microsoft.Azure.Commands.Network
{
public static class AzureFirewallNetworkRuleProtocolHelper
{
private static readonly IDictionary<string, string> SupportedNetworkProtocols = new Dictionary<string, string>
{
{ MNM.AzureFirewallNetworkRuleProtocol.Any.ToUpper(), MNM.AzureFirewallNetworkRuleProtocol.Any },
{ MNM.AzureFirewallNetworkRuleProtocol.TCP.ToUpper(), MNM.AzureFirewallNetworkRuleProtocol.TCP },
{ MNM.AzureFirewallNetworkRuleProtocol.UDP.ToUpper(), MNM.AzureFirewallNetworkRuleProtocol.UDP },
};

internal static string MapUserInputToNetworkProtocol(string protocolType)
{
if (protocolType == null)
{
throw new ArgumentException("A protocol must be provided", nameof(protocolType));
}

var userInputKey = protocolType.ToUpper();
if (!SupportedNetworkProtocols.ContainsKey(userInputKey))
{
throw new ArgumentException(
$"Invalid protocol {protocolType}. Accepted values are {string.Join(", ", SupportedNetworkProtocols.Values)}.",
nameof(protocolType));
}

return SupportedNetworkProtocols[userInputKey];
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// ----------------------------------------------------------------------------------
//
// Copyright Microsoft Corporation
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ----------------------------------------------------------------------------------

using System;
using System.Collections.Generic;
using System.Linq;
using System.Management.Automation;
using System.Text.RegularExpressions;
using Microsoft.Azure.Commands.Network.Models;
using MNM = Microsoft.Azure.Management.Network.Models;

namespace Microsoft.Azure.Commands.Network
{
[Cmdlet(VerbsCommon.New, ResourceManager.Common.AzureRMConstants.AzureRMPrefix + "FirewallNatRule", SupportsShouldProcess = true), OutputType(typeof(PSAzureFirewallNatRule))]
public class NewAzureFirewallNatRuleCommand : NetworkBaseCmdlet
{
[Parameter(
Mandatory = true,
HelpMessage = "The name of the NAT Rule")]
[ValidateNotNullOrEmpty]
public virtual string Name { get; set; }

[Parameter(
Mandatory = false,
HelpMessage = "The description of the rule")]
[ValidateNotNullOrEmpty]
public string Description { get; set; }

[Parameter(
Mandatory = true,
HelpMessage = "The source addresses of the rule")]
[ValidateNotNullOrEmpty]
public List<string> SourceAddress { get; set; }

[Parameter(
Mandatory = true,
HelpMessage = "The destination addresses of the rule")]
[ValidateNotNullOrEmpty]
public List<string> DestinationAddress { get; set; }

[Parameter(
Mandatory = true,
HelpMessage = "The destination ports of the rule")]
[ValidateNotNullOrEmpty]
public List<string> DestinationPort { get; set; }

[Parameter(
Mandatory = true,
HelpMessage = "The protocols of the rule")]
[ValidateSet(
MNM.AzureFirewallNetworkRuleProtocol.Any,
MNM.AzureFirewallNetworkRuleProtocol.TCP,
MNM.AzureFirewallNetworkRuleProtocol.UDP,
IgnoreCase = false)]
public List<string> Protocol { get; set; }

[Parameter(
Mandatory = true,
HelpMessage = "The translated address for this NAT rule")]
[ValidateNotNullOrEmpty]
public string TranslatedAddress { get; set; }

[Parameter(
Mandatory = true,
HelpMessage = "The translated port for this NAT rule")]
[ValidateNotNullOrEmpty]
public string TranslatedPort { get; set; }

public override void Execute()
{
base.Execute();

// Add some validation based on the type of RuleCollection (SNAT will be supported later)
// if (MNM.AzureFirewallNatRCActionType.Dnat.Equals(ActionType))
{
if (DestinationAddress.Count != 1)
{
throw new ArgumentException("Only one destination address is accepted.", nameof(DestinationAddress));
}

if (DestinationPort.Count != 1)
{
throw new ArgumentException("Only one destination port is accepted.", nameof(DestinationPort));
}

ValidateIsSingleIpNotRange(DestinationAddress.Single());
ValidateIsSingleIpNotRange(TranslatedAddress);

ValidateIsSinglePortNotRange(DestinationPort.Single());
ValidateIsSinglePortNotRange(TranslatedPort);
}

var networkRule = new PSAzureFirewallNatRule
{
Name = this.Name,
Description = this.Description,
Protocols = this.Protocol,
SourceAddresses = this.SourceAddress,
DestinationAddresses = this.DestinationAddress,
DestinationPorts = this.DestinationPort,
TranslatedAddress = this.TranslatedAddress,
TranslatedPort = this.TranslatedPort
};
WriteObject(networkRule);
}

private void ValidateIsSingleIpNotRange(string ipStr)
{
var singleIpRegEx = new Regex("^((\\d){1,3}\\.){3}((\\d){1,3})$");

if (!singleIpRegEx.IsMatch(ipStr))
{
throw new ArgumentException($"Invalid value {ipStr}. Only a single IPv4 value is accepted (e.g. 10.1.2.3).");
}
}

private void ValidateIsSinglePortNotRange(string portStr)
{
uint parsed;
if (!uint.TryParse(portStr, out parsed))
{
throw new ArgumentException($"Invalid value {portStr}. Only a single port value is accepted (e.g. 8080).");
}
}
}
}
Loading