Skip to content

Commit 9395a7e

Browse files
committed
Added logging capabilities for all secrets repos. Implemented logging for blob storage repo.
1 parent ebe4739 commit 9395a7e

File tree

5 files changed

+117
-16
lines changed

5 files changed

+117
-16
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright (c) .NET Foundation. All rights reserved.
2+
// Licensed under the MIT License. See License.txt in the project root for license information.
3+
4+
using System;
5+
using Microsoft.Extensions.Logging;
6+
7+
namespace Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.Extensions
8+
{
9+
public static class ScriptHostServiceSecurityExtension
10+
{
11+
// EventId range is 600-699
12+
13+
private static readonly Action<ILogger, string, string, Exception> _blobStorageSecretRepoError =
14+
LoggerMessage.Define<string, string>(
15+
LogLevel.Error,
16+
new EventId(600, nameof(BlobStorageSecretRepoError)),
17+
"There was an error performing a {operation} operation on the Blob Storage Secret Repository. Please ensure the '{appSettingName}' connection string is valid.");
18+
19+
private static readonly Action<ILogger, string, string, Exception> _blobStorageSecretSasRepoError =
20+
LoggerMessage.Define<string, string>(
21+
LogLevel.Error,
22+
new EventId(601, nameof(BlobStorageSecretSasRepoError)),
23+
"There was an error performing a {operation} operation on the Blob Storage Secret Repository. Please ensure the '{appSettingName}' SAS URL has Read, Write, and List permissions.");
24+
25+
public static void BlobStorageSecretRepoError(this ILogger logger, string operation, string appSettingName)
26+
{
27+
_blobStorageSecretRepoError(logger, operation, appSettingName, null);
28+
}
29+
30+
public static void BlobStorageSecretSasRepoError(this ILogger logger, string operation, string appSettingName)
31+
{
32+
_blobStorageSecretSasRepoError(logger, operation, appSettingName, null);
33+
}
34+
}
35+
}

src/WebJobs.Script.WebHost/Security/KeyManagement/BaseSecretsRepository.cs

+7
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,15 @@ public BaseSecretsRepository(string secretsSentinelFilePath)
3535
_sentinelFileWatcher.Changed += OnChanged;
3636
}
3737

38+
public BaseSecretsRepository(string secretsSentinelFilePath, ILogger logger) : this(secretsSentinelFilePath)
39+
{
40+
Logger = logger;
41+
}
42+
3843
public event EventHandler<SecretsChangedEventArgs> SecretsChanged;
3944

45+
protected ILogger Logger { get; }
46+
4047
public abstract bool IsEncryptionSupported { get; }
4148

4249
protected string GetSecretsSentinelFilePath(ScriptSecretsType secretsType, string functionName = null)

src/WebJobs.Script.WebHost/Security/KeyManagement/BlobStorageSasSecretsRepository.cs

+16-2
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
// Licensed under the MIT License. See License.txt in the project root for license information.
33

44
using System;
5+
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.Extensions;
6+
using Microsoft.Azure.WebJobs.Script.WebHost.Properties;
7+
using Microsoft.Extensions.Logging;
58
using Microsoft.WindowsAzure.Storage.Blob;
69

710
namespace Microsoft.Azure.WebJobs.Script.WebHost
@@ -12,12 +15,23 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost
1215
public sealed class BlobStorageSasSecretsRepository : BlobStorageSecretsRepository
1316
{
1417
public BlobStorageSasSecretsRepository(string secretSentinelDirectoryPath, string containerSasUri, string siteSlotName)
15-
: base(secretSentinelDirectoryPath, containerSasUri, siteSlotName)
16-
{ }
18+
: this(secretSentinelDirectoryPath, containerSasUri, siteSlotName, null)
19+
{
20+
}
21+
22+
public BlobStorageSasSecretsRepository(string secretSentinelDirectoryPath, string containerSasUri, string siteSlotName, ILogger logger)
23+
: base(secretSentinelDirectoryPath, containerSasUri, siteSlotName, logger)
24+
{
25+
}
1726

1827
protected override CloudBlobContainer CreateBlobContainer(string containerSasUri)
1928
{
2029
return new CloudBlobContainer(new Uri(containerSasUri));
2130
}
31+
32+
protected override void LogErrorMessage(string operation)
33+
{
34+
Logger?.BlobStorageSecretSasRepoError(operation, EnvironmentSettingNames.AzureWebJobsSecretStorageSas);
35+
}
2236
}
2337
}

src/WebJobs.Script.WebHost/Security/KeyManagement/BlobStorageSecretsRepository.cs

+54-8
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using System.Linq;
99
using System.Threading.Tasks;
1010
using Microsoft.Azure.WebJobs.Script.IO;
11+
using Microsoft.Azure.WebJobs.Script.WebHost.Diagnostics.Extensions;
12+
using Microsoft.Azure.WebJobs.Script.WebHost.Properties;
1113
using Microsoft.Extensions.Logging;
1214
using Microsoft.WindowsAzure.Storage;
1315
using Microsoft.WindowsAzure.Storage.Blob;
@@ -25,7 +27,13 @@ public class BlobStorageSecretsRepository : BaseSecretsRepository
2527
private readonly string _secretsContainerName = "azure-webjobs-secrets";
2628
private readonly string _accountConnectionString;
2729

28-
public BlobStorageSecretsRepository(string secretSentinelDirectoryPath, string accountConnectionString, string siteSlotName) : base(secretSentinelDirectoryPath)
30+
public BlobStorageSecretsRepository(string secretSentinelDirectoryPath, string accountConnectionString, string siteSlotName)
31+
: this(secretSentinelDirectoryPath, accountConnectionString, siteSlotName, null)
32+
{
33+
}
34+
35+
public BlobStorageSecretsRepository(string secretSentinelDirectoryPath, string accountConnectionString, string siteSlotName, ILogger logger)
36+
: base(secretSentinelDirectoryPath, logger)
2937
{
3038
if (secretSentinelDirectoryPath == null)
3139
{
@@ -71,11 +79,18 @@ public override async Task<ScriptSecrets> ReadAsync(ScriptSecretsType type, stri
7179
{
7280
string secretsContent = null;
7381
string blobPath = GetSecretsBlobPath(type, functionName);
74-
CloudBlockBlob secretBlob = _blobContainer.GetBlockBlobReference(blobPath);
75-
76-
if (await secretBlob.ExistsAsync())
82+
try
83+
{
84+
CloudBlockBlob secretBlob = _blobContainer.GetBlockBlobReference(blobPath);
85+
if (await secretBlob.ExistsAsync())
86+
{
87+
secretsContent = await secretBlob.DownloadTextAsync();
88+
}
89+
}
90+
catch (Exception e)
7791
{
78-
secretsContent = await secretBlob.DownloadTextAsync();
92+
LogErrorMessage("read");
93+
throw e;
7994
}
8095

8196
return string.IsNullOrEmpty(secretsContent) ? null : ScriptSecretSerializer.DeserializeSecrets(type, secretsContent);
@@ -89,7 +104,15 @@ public override async Task WriteAsync(ScriptSecretsType type, string functionNam
89104
}
90105

91106
string blobPath = GetSecretsBlobPath(type, functionName);
92-
await WriteToBlobAsync(blobPath, ScriptSecretSerializer.SerializeSecrets(secrets));
107+
try
108+
{
109+
await WriteToBlobAsync(blobPath, ScriptSecretSerializer.SerializeSecrets(secrets));
110+
}
111+
catch (Exception e)
112+
{
113+
LogErrorMessage("write");
114+
throw e;
115+
}
93116

94117
string filePath = GetSecretsSentinelFilePath(type, functionName);
95118
await FileUtility.WriteAsync(filePath, DateTime.UtcNow.ToString());
@@ -104,7 +127,16 @@ public override async Task WriteSnapshotAsync(ScriptSecretsType type, string fun
104127

105128
string blobPath = GetSecretsBlobPath(type, functionName);
106129
blobPath = SecretsUtility.GetNonDecryptableName(blobPath);
107-
await WriteToBlobAsync(blobPath, ScriptSecretSerializer.SerializeSecrets(secrets));
130+
131+
try
132+
{
133+
await WriteToBlobAsync(blobPath, ScriptSecretSerializer.SerializeSecrets(secrets));
134+
}
135+
catch (Exception e)
136+
{
137+
LogErrorMessage("write");
138+
throw e;
139+
}
108140
}
109141

110142
public override async Task PurgeOldSecretsAsync(IList<string> currentFunctions, ILogger logger)
@@ -118,7 +150,16 @@ public override async Task<string[]> GetSecretSnapshots(ScriptSecretsType type,
118150
// Prefix is secret blob path without extension
119151
string prefix = Path.GetFileNameWithoutExtension(GetSecretsBlobPath(type, functionName)) + $".{ScriptConstants.Snapshot}";
120152

121-
BlobResultSegment segmentResult = await _blobContainer.ListBlobsSegmentedAsync(string.Format("{0}/{1}", _secretsBlobPath, prefix.ToLowerInvariant()), null);
153+
BlobResultSegment segmentResult;
154+
try
155+
{
156+
segmentResult = await _blobContainer.ListBlobsSegmentedAsync(string.Format("{0}/{1}", _secretsBlobPath, prefix.ToLowerInvariant()), null);
157+
}
158+
catch (Exception e)
159+
{
160+
LogErrorMessage("list");
161+
throw e;
162+
}
122163
return segmentResult.Results.Select(x => x.Uri.ToString()).ToArray();
123164
}
124165

@@ -137,5 +178,10 @@ private async Task WriteToBlobAsync(string blobPath, string secretsContent)
137178
await writer.WriteAsync(secretsContent);
138179
}
139180
}
181+
182+
protected virtual void LogErrorMessage(string operation)
183+
{
184+
Logger?.BlobStorageSecretRepoError(operation, "AzureWebJobsStorage");
185+
}
140186
}
141187
}

src/WebJobs.Script.WebHost/Security/KeyManagement/DefaultSecretManagerProvider.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ namespace Microsoft.Azure.WebJobs.Script.WebHost
1515
public sealed class DefaultSecretManagerProvider : ISecretManagerProvider
1616
{
1717
private const string FileStorage = "Files";
18-
private const string BlobStorage = "Blob";
19-
private readonly ILogger _logger;
18+
private readonly ILoggerFactory _loggerFactory;
2019
private readonly IMetricsLogger _metricsLogger;
2120
private readonly IOptionsMonitor<ScriptApplicationHostOptions> _options;
2221
private readonly IHostIdProvider _hostIdProvider;
@@ -39,7 +38,7 @@ public DefaultSecretManagerProvider(IOptionsMonitor<ScriptApplicationHostOptions
3938
_environment = environment ?? throw new ArgumentNullException(nameof(environment));
4039
_hostNameProvider = hostNameProvider ?? throw new ArgumentNullException(nameof(hostNameProvider));
4140

42-
_logger = loggerFactory.CreateLogger(ScriptConstants.LogCategoryHostGeneral);
41+
_loggerFactory = loggerFactory;
4342
_metricsLogger = metricsLogger ?? throw new ArgumentNullException(nameof(metricsLogger));
4443
_secretManagerLazy = new Lazy<ISecretManager>(Create);
4544

@@ -51,7 +50,7 @@ public DefaultSecretManagerProvider(IOptionsMonitor<ScriptApplicationHostOptions
5150

5251
private void ResetSecretManager() => Interlocked.Exchange(ref _secretManagerLazy, new Lazy<ISecretManager>(Create));
5352

54-
private ISecretManager Create() => new SecretManager(CreateSecretsRepository(), _logger, _metricsLogger, _hostNameProvider);
53+
private ISecretManager Create() => new SecretManager(CreateSecretsRepository(), _loggerFactory.CreateLogger<SecretManager>(), _metricsLogger, _hostNameProvider);
5554

5655
internal ISecretsRepository CreateSecretsRepository()
5756
{
@@ -75,12 +74,12 @@ internal ISecretsRepository CreateSecretsRepository()
7574
else if (secretStorageSas != null)
7675
{
7776
string siteSlotName = _environment.GetAzureWebsiteUniqueSlotName() ?? _hostIdProvider.GetHostIdAsync(CancellationToken.None).GetAwaiter().GetResult();
78-
return new BlobStorageSasSecretsRepository(Path.Combine(_options.CurrentValue.SecretsPath, "Sentinels"), secretStorageSas, siteSlotName);
77+
return new BlobStorageSasSecretsRepository(Path.Combine(_options.CurrentValue.SecretsPath, "Sentinels"), secretStorageSas, siteSlotName, _loggerFactory.CreateLogger<BlobStorageSasSecretsRepository>());
7978
}
8079
else if (storageString != null)
8180
{
8281
string siteSlotName = _environment.GetAzureWebsiteUniqueSlotName() ?? _hostIdProvider.GetHostIdAsync(CancellationToken.None).GetAwaiter().GetResult();
83-
return new BlobStorageSecretsRepository(Path.Combine(_options.CurrentValue.SecretsPath, "Sentinels"), storageString, siteSlotName);
82+
return new BlobStorageSecretsRepository(Path.Combine(_options.CurrentValue.SecretsPath, "Sentinels"), storageString, siteSlotName, _loggerFactory.CreateLogger<BlobStorageSecretsRepository>());
8483
}
8584
else
8685
{

0 commit comments

Comments
 (0)