前言
由于疫情的持续肆虐,出行成了大问题。为了尽量减少风险,外地的办公室是能不过去就不过去。一般问题远程就解决了,但碰到系统因故障需重装的情况就比较麻烦,外地的同事并不是IT专业的,在电话指导+Word文档指引下安装完系统,后续仍有一些重复的操作如开启远程桌面、设置默认账号密码及变更防火墙设置(以放开远程桌面连接)等,虽亦可以写份Word文档说明操作步骤,但总感觉这些操作可以通过程序来实现,同时考虑到在远程桌面开通后,仍有一些标准的安全设置需要重复设置,遂用C#写了两个小工具来自动化这些操作。其中主要用到以下这些功能,分享一下。
在此前,我们已通过另一个Console小工具配合Powershell脚本开通了远程桌面,接下来我们可以通过C#来进行防火墙的基本设置。主要通过WindowsFirewallHelper(参考https://github.com/falahati/WindowsFirewallHelper)来操作Windows防火墙。
/// <summary>
/// 建立自定义程序规则
/// </summary>
/// <param name="newAppRuleName ">规则名称</param>
/// <param name="appExeFileName">程序可执行文件完整路径及名称</param>
private void CreateApplicationRule(string newAppRuleName, string appExeFileName)
{
var rule = FirewallManager.Instance.Rules.Where(r =>
r.Direction == FirewallDirection.Outbound && //搜索出站规则
r.Name.Equals(newAppRuleName)
).FirstOrDefault();
if (rule == null) // 指定的规则不存在
{
try
{
rule = FirewallManager.Instance.CreateApplicationRule(
FirewallProfiles.Domain | FirewallProfiles.Private | FirewallProfiles.Public, // 生效的配置文件
newAppRuleName,
FirewallAction.Block, // 阻止
appExeFileName,
FirewallProtocol.TCP //协议
);
rule.Direction = FirewallDirection.Outbound; //方向:出站
FirewallManager.Instance.Rules.Add(rule);
// todo: 输出成功建立规则信息
}
catch (Exception ex)
{
// todo: 输出错误信息
}
}
else
{
// todo: 输出规则已存在信息
}
}
/// <summary>
/// 建立自定义端口规则
/// </summary>
/// <param name="newPortRuleName">规则名称</param>
/// <param name="portNumber">端口:例如3389</param>
/// <param name="firewallAction">动作:FirewallAction.Allow 或 FirewallAction.Block</param>
/// <param name="firewallDirection">方向:FirewallDirection.Inbound 或 FirewallDirection.Outbound</param>
private void CreatePortRule(string newPortRuleName, ushort portNumber, FirewallAction firewallAction, FirewallDirection firewallDirection)
{
//搜索规则
var rule = FirewallManager.Instance.Rules.Where(r =>
r.Direction == firewallDirection &&
r.Name.Equals(newPortRuleName)
).FirstOrDefault();
if (rule == null) // 指定的规则不存在
{
try
{
rule = FirewallManager.Instance.CreatePortRule(
FirewallProfiles.Domain | FirewallProfiles.Private | FirewallProfiles.Public, // 生效的配置文件
newPortRuleName,
firewallAction, // 运作:允许或阻止
portNumber,
FirewallProtocol.TCP //协议
);
rule.Direction = firewallDirection; //方向
FirewallManager.Instance.Rules.Add(rule);
// todo: 输出成功建立规则信息
}
catch (Exception ex)
{
// todo: 输出错误信息
}
}
else
{
// todo: 输出规则已存在信息
}
}
// 定义允许的IP地址列表
private IAddress[] TargetIPAddress = new IAddress[]
{
SingleIP.Parse("192.168.11.248"),
SingleIP.Parse("192.168.10.248")
};
/// <summary>
/// 更新自定义规则
/// </summary>
/// <param name="targetRuleName">规则名称</param>
/// <param name="firewallDirection">入站或出站规则</param>
/// <param name="allowedIPAddress">允许的IP地址</param>
private void UpdateRule(string targetRuleName, FirewallDirection firewallDirection, IAddress[] allowedIPAddress)
{
// 搜索目标规则
var rule = FirewallManager.Instance.Rules.Where(r =>
r.Direction == firewallDirection &&
r.Name.Equals(targetRuleName)
).FirstOrDefault();
if (rule != null)
{
try
{
rule.RemoteAddresses = allowedIPAddress;
// todo: 输出成功更新信息
}
catch (Exception ex)
{
// todo: 输出出错信息
}
}
else
{
// todo: 输出规则不存在信息
}
}
/// <summary>
/// 删除(入站或出站)规则
/// https://github.com/falahati/WindowsFirewallHelper 使用的是 SingleOrDefault
/// 但是,如碰巧有两条同名规则时会报提示:Sequence Contains More Than One Matching Element
/// </summary>
/// <param name="targetRuleName">规则名称</param>
private void RemoveRule(string targetRuleName)
{
var rule2Remove = FirewallManager.Instance.Rules.FirstOrDefault (r => r.Name == targetRuleName);
if (rule2Remove!= null)
{
try
{
FirewallManager.Instance.Rules.Remove(rule2Remove);
// todo: 输出成功删除信息
}
catch (Exception ex)
{
// todo: 输出出错信息
}
}
else
{
// todo: 输出规则不存在信息
}
}
/*
* 禁用或启用规则
* WindowsFirewallHelper并没有提供禁用或启用规则的方法(或许是没有找到?),只能通过迂回的方式(运行advfirewall firewall set 命令)以达到目的。
*/
/// <summary>
/// 修改规则
/// </summary>
/// <param name="targetRuleName">规则名称</param>
/// <param name="actionFlag">bool: True or False</param>
private void ModifyRule(string targetRuleName, bool actionFlag)
{
string cmd = "/C netsh advfirewall firewall set rule name=\"" + targetRuleName + "\" new enable=" + (actionFlag ? "yes" : "no");
RunCMD(cmd);
}
/// <summary>
/// 运行命令行程序
/// </summary>
/// <param name="argument">命令行参数</param>
private static void RunCMD(string argument)
{
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden; // 隐藏运行时窗口
startInfo.FileName = @"C:\Windows\System32\cmd.exe";
startInfo.Arguments = argument;
process.StartInfo = startInfo;
process.Start();
}