Skip to content
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

Add Audit Logging To SecreteManager.exe #4183

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
Open

Conversation

davfost
Copy link
Contributor

@davfost davfost commented Sep 27, 2024

Add Audit Logging To SecreteManager.exe

  • Adds the OpenTelemetry.Audit.Geneva package and dependencies
  • Following logging recommendations from Geneva team
  • Added non-volatile logging operations to secret update and create operations
  • Adds new command line options for setting the target service ID to used when performing audit logging

Part Of GitHub Issue https://github.com/dotnet/arcade/issues/13217

{
_console = console;
}

public override Task RunAsync(CancellationToken cancellationToken)
{
// Provides a curtisy warning message if the ServiceTreeId option is set to a empty guid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Provides a curtisy warning message if the ServiceTreeId option is set to a empty guid
// Provides a courtesy warning message if the ServiceTreeId option is set to a empty guid

{
{"servicetreeid=", "The service tree ID (must be a valid GUID id from aka.ms/servicetree)", id =>
{
if (Guid.TryParse(id, out var guid))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since someone took the time to specify a service tree ID here, I would just error out if it's not a valid GUID.

var auditFactory = AuditLoggerFactory.Create(options =>
{
// We use ETW as the destination for the audit logs becsue the application is not gurenteed to run on windows
options.Destination = AuditLogDestination.ETW;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ETW is available on Linux?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1. We also don't typically capture ETW data. Where does audit information ultimately end up?

@@ -1,32 +1,33 @@
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DncEng.CommandLineLib;
using Microsoft.VisualStudio.Services.Common;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Necessary?

var createdSecret = await client.SetSecretAsync(name, value.Value ?? "");
var properties = createdSecret.Value.Properties;
foreach (var (k, v) in value.Tags)
// The default audit state should alwasy be failure and overwritten with success at the end of the operation.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The default audit state should alwasy be failure and overwritten with success at the end of the operation.
// The default audit state should always be failure and overwritten with success at the end of the operation.

// The hope is that app insights will also catch the base exception for debugging.
catch (Exception ex)
{
Console.WriteLine($"Failed to add audit log for secret update!: <{ex.Message}>");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will the audit message always be safe for public view in a console?

/// </summary>
public class SecurityAuditLogger
{
private ILogger ControlPanelLogger;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private ILogger ControlPanelLogger;
private ILogger ControlPlaneLogger;

I think that's what you meant?

{
LogSecretAction(OperationType.Update, operationName, credentialProvider, secretName, secretStoreType, secretLocation, result, resultMessage);
}
// Audit logging is a 'volitile' operation meaning it can throw exceptions if logging fails.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Audit logging is a 'volitile' operation meaning it can throw exceptions if logging fails.
// Audit logging is a 'volatile' operation meaning it can throw exceptions if logging fails.

return;
}

// The token applicatoin id of the client running the assembly.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// The token applicatoin id of the client running the assembly.
// The token application id of the client running the assembly.

Comment on lines +68 to +70
// NOTE: The user identity here should be something 'dynamic'.
// If you are hard coding this value you should question if this Audit Log is useful
// as it is likly redundant to lower level permission change logging that is already occuring.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these comments be here? They look like copy-paste from an example?

<configuration>
<packageSources>
<clear />
<add key="AzureGenevaMonitoring" value="https://msblox.pkgs.visualstudio.com/_packaging/AzureGenevaMonitoring/nuget/v3/index.json" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What permissions are required to access this feed? Will there be issues with CI and individual users getting access?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is currently an issue with this feed and it blocked the PR build from completing. I have been discussing it with Matt Mitchell to see how it can be resolved. As you observed this feed requires external permissions that are not granted so the package restore is failing.

The basic issue is that the Audit team says we have to use the version of the package that comes from 'this' feed and not the public version of the package.

  • I need to investigate if the 'public' feed version of the package has the same class and methods defined. If so, we can do something where the 'internal feed' is different than the feed used in the 'public' processes.
  • I also have open questions to the Audit team to verify if the package from 'this' feed CAN be exposed publicly. If so we may be able to just manually move target versions into one of the feeds we manage directly.

Matt said these two options have been used in the past to deal with other packages that have similar issues.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good good, Matt should have good ideas here.

Just to say it explicitly, do not merge this PR until there's a good solution for public CI and for local dev.

/// </summary>
public SecurityAuditLogger(Guid serviceTreeId)
{
var auditFactory = AuditLoggerFactory.Create(options =>
Copy link
Member

@garath garath Oct 8, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does this logger do if the Service ID is Guid.Empty? Does it still try to log or is that enough to make it silent?

@@ -26,6 +24,11 @@ private async Task TestCommand(DateTimeOffset now, string manifestText, string l
var cancellationToken = cts.Token;

var services = new ServiceCollection();
// Dependency injection instruction needed to support properties used for Geneval Logging operations
services.AddSingleton(new GlobalCommand());
services.AddSingleton(new SecurityAuditLogger(Guid.Empty));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these are test classes, would it be better to make a do-nothing version of SecurityAuditLogger? (I'm not sure what SecurityAuditLogger does.)

services.AddSingleton(new GlobalCommand());
services.AddSingleton(new SecurityAuditLogger(Guid.Empty));

// Original dependency injection instructions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't thin kthis comment (or the previous comment) is necessary. The code shouldn't have a sense of "human time". That's what version control handles. The code should always just be "this is what it is right now".

using Microsoft.DncEng.CommandLineLib;
using Microsoft.DncEng.SecretManager.StorageTypes;
using Microsoft.VisualStudio.Services.Common;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's bringing this in?

@@ -5,5 +5,15 @@ namespace Microsoft.DncEng.SecretManager;

public interface ITokenCredentialProvider
{
/// <summary>
/// The applicatoin ID for the credential provider.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The applicatoin ID for the credential provider.
/// The application ID for the credential provider.

@@ -57,15 +57,15 @@
<PackageVersion Include="Microsoft.DotNet.SwaggerGenerator.MSBuild" Version="$(MicrosoftDotNetSwaggerGeneratorMSBuildVersion)" />
<PackageVersion Include="Microsoft.DotNet.VersionTools" Version="$(MicrosoftDotNetVersionToolsVersion)" />
<PackageVersion Include="Microsoft.DotNet.Web.Authentication" Version="$(MicrosoftDotNetWebAuthenticationVersion)" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="6.0.1" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Json" Version="6.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I recognize that these updates are safe because they all implement netstandard2.0, but I'm wondering what motivates the bump in this PR?

{

/// <summary>
/// Indictes if the global option for 'quiet' is set
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Indictes if the global option for 'quiet' is set
/// Indicates if the global option for 'quiet' is set


/// <summary>
/// Provides the ServiceTreeId set with global options
/// The ID is a goid and is set to Guid.Empty if not set
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// The ID is a goid and is set to Guid.Empty if not set
/// The ID is a guid and is set to Guid.Empty if not set

}

/// <summary>
/// Overides the GetOptions method from the base class to add a cusotm option for the ServiceTreeId
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Overides the GetOptions method from the base class to add a cusotm option for the ServiceTreeId
/// Overrides the GetOptions method from the base class to add a custom option for the ServiceTreeId

@@ -45,6 +47,9 @@ public override bool AreRequiredOptionsSet()

public override async Task RunAsync(CancellationToken cancellationToken)
{
// Provides a curtisy warning message if the ServiceTreeId option is set to a empty guid
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// Provides a curtisy warning message if the ServiceTreeId option is set to a empty guid
// Provides a courtesy warning message if the ServiceTreeId option is set to an empty guid

var ipAddress = host.AddressList.FirstOrDefault(ip => ip.AddressFamily == AddressFamily.InterNetwork);
if (ipAddress == null)
{
throw new Exception("No network adapters with an IPv4 address in the system!");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No IPv6?

_auditLogger.LogSecretUpdate(
credentialProvider: _tokenCredentialProvider,
secretName: name,
secretStoreType: "AzureKeyVault",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
secretStoreType: "AzureKeyVault",
secretStoreType: nameof(AzureKeyVault),

Compiler fanciness.

Copy link
Member

@garath garath left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Marking to hold at least until the questions about how CI will handle the new packaging are answered.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants