diff options
Diffstat (limited to 'src/Agent.Windows/Network/TaskNetworkPolicy.cs')
| -rw-r--r-- | src/Agent.Windows/Network/TaskNetworkPolicy.cs | 73 |
1 files changed, 73 insertions, 0 deletions
diff --git a/src/Agent.Windows/Network/TaskNetworkPolicy.cs b/src/Agent.Windows/Network/TaskNetworkPolicy.cs new file mode 100644 index 0000000..59b2602 --- /dev/null +++ b/src/Agent.Windows/Network/TaskNetworkPolicy.cs @@ -0,0 +1,73 @@ +using System.Security.Principal; +using Agent.Windows.Recovery; +using Agent.Windows.Security; + +namespace Agent.Windows.Network; + +public sealed class TaskNetworkPolicy : IDisposable +{ + private readonly WfpDynamicSession _session; + private readonly TaskResourceJournal _journal; + private readonly ISecurityAuditSink _audit; + private readonly Guid _entryId; + private readonly SecurityIdentifier _appContainerSid; + private readonly Guid _loopbackEntryId; + private readonly bool _loopbackAdded; + private bool _disposed; + + private TaskNetworkPolicy(WfpDynamicSession session, TaskResourceJournal journal, ISecurityAuditSink audit, Guid entryId, + SecurityIdentifier appContainerSid, Guid loopbackEntryId, bool loopbackAdded) + { + _session = session; _journal = journal; _audit = audit; _entryId = entryId; + _appContainerSid = appContainerSid; _loopbackEntryId = loopbackEntryId; _loopbackAdded = loopbackAdded; + } + + public static TaskNetworkPolicy Apply(TaskResourceJournal journal, SecurityIdentifier appContainerSid, IReadOnlyList<NetworkEndpoint> allowances, ISecurityAuditSink audit) + { + bool originallyExempt = LoopbackExemption.Contains(appContainerSid); + Guid loopbackEntryId = journal.Prepare("network.loopback_exemption", appContainerSid.Value, + new Dictionary<string, string> { ["originally_present"] = originallyExempt.ToString() }); + bool loopbackAdded = false; + Guid entryId = journal.Prepare("wfp.dynamic", appContainerSid.Value, new Dictionary<string, string> + { + ["session"] = "dynamic", ["allowances"] = string.Join(',', allowances.Select(static item => $"{item.Address}:{item.Port}")) + }); + WfpDynamicSession session = WfpDynamicSession.Open(); + try + { + loopbackAdded = LoopbackExemption.Ensure(appContainerSid); + journal.MarkApplied(loopbackEntryId, new Dictionary<string, string> { ["added"] = loopbackAdded.ToString() }); + IReadOnlyList<ulong> ids = session.ApplyDefaultBlock(appContainerSid, allowances); + journal.MarkApplied(entryId, new Dictionary<string, string> { ["filter_ids"] = string.Join(',', ids) }); + audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "wfp.apply", "applied", new Dictionary<string, string> + { + ["ipv4_ipv6_default_block"] = "true", ["filter_count"] = ids.Count.ToString(), ["session"] = "dynamic" + })); + audit.Write(new(DateTimeOffset.UtcNow, journal.TaskId, "network.loopback.apply", loopbackAdded ? "added" : "preserved", + new Dictionary<string, string> { ["app_container_sid"] = appContainerSid.Value })); + return new(session, journal, audit, entryId, appContainerSid, loopbackEntryId, loopbackAdded); + } + catch + { + session.Dispose(); + journal.MarkCleaned(entryId); + if (loopbackAdded) LoopbackExemption.Remove(appContainerSid); + journal.MarkCleaned(loopbackEntryId); + throw; + } + } + + public void Dispose() + { + if (_disposed) return; + _session.RemovePolicy(); + _session.Dispose(); + _journal.MarkCleaned(_entryId); + if (_loopbackAdded) LoopbackExemption.Remove(_appContainerSid); + _journal.MarkCleaned(_loopbackEntryId); + _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "wfp.remove", "removed", new Dictionary<string, string> { ["residual_filters"] = "0" })); + _audit.Write(new(DateTimeOffset.UtcNow, _journal.TaskId, "network.loopback.remove", "removed", + new Dictionary<string, string> { ["residual_exemptions"] = LoopbackExemption.Contains(_appContainerSid) ? "preexisting" : "0" })); + _disposed = true; + } +} |
