Skip to content

Commit 08cb762

Browse files
committed
Host keys race fix
1 parent 266ad46 commit 08cb762

File tree

1 file changed

+44
-32
lines changed

1 file changed

+44
-32
lines changed

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

Lines changed: 44 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
using System.IO;
88
using System.Linq;
99
using System.Security.Cryptography;
10+
using System.Threading;
1011
using System.Threading.Tasks;
1112
using Microsoft.Azure.WebJobs.Script.Config;
1213
using Microsoft.Azure.WebJobs.Script.WebHost.Properties;
@@ -21,6 +22,7 @@ public class SecretManager : IDisposable, ISecretManager
2122
private readonly ILogger _logger;
2223
private readonly ISecretsRepository _repository;
2324
private HostSecretsInfo _hostSecrets;
25+
private SemaphoreSlim _semaphoreSlim = new SemaphoreSlim(1, 1);
2426

2527
// for testing
2628
public SecretManager()
@@ -65,43 +67,53 @@ public async virtual Task<HostSecretsInfo> GetHostSecretsAsync()
6567
{
6668
if (_hostSecrets == null)
6769
{
68-
HostSecrets hostSecrets = await LoadSecretsAsync<HostSecrets>();
69-
70-
if (hostSecrets == null)
71-
{
72-
// host secrets do not yet exist so generate them
73-
_logger.LogDebug(Resources.TraceHostSecretGeneration);
74-
hostSecrets = GenerateHostSecrets();
75-
await PersistSecretsAsync(hostSecrets);
76-
}
77-
70+
HostSecrets hostSecrets;
71+
// Allow only one thread to modify the secrets
72+
await _semaphoreSlim.WaitAsync();
7873
try
7974
{
80-
// Host secrets will be in the original persisted state at this point (e.g. encrypted),
81-
// so we read the secrets running them through the appropriate readers
82-
hostSecrets = ReadHostSecrets(hostSecrets);
83-
}
84-
catch (CryptographicException)
85-
{
86-
_logger?.LogDebug(Resources.TraceNonDecryptedHostSecretRefresh);
87-
await PersistSecretsAsync(hostSecrets, null, true);
88-
await RefreshSecretsAsync(hostSecrets);
89-
}
75+
hostSecrets = await LoadSecretsAsync<HostSecrets>();
9076

91-
// If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
92-
// the state and persist the secrets
93-
if (hostSecrets.HasStaleKeys)
94-
{
95-
_logger.LogDebug(Resources.TraceStaleHostSecretRefresh);
96-
await RefreshSecretsAsync(hostSecrets);
97-
}
77+
if (hostSecrets == null)
78+
{
79+
// host secrets do not yet exist so generate them
80+
_logger.LogDebug(Resources.TraceHostSecretGeneration);
81+
hostSecrets = GenerateHostSecrets();
82+
await PersistSecretsAsync(hostSecrets);
83+
}
84+
85+
try
86+
{
87+
// Host secrets will be in the original persisted state at this point (e.g. encrypted),
88+
// so we read the secrets running them through the appropriate readers
89+
hostSecrets = ReadHostSecrets(hostSecrets);
90+
}
91+
catch (CryptographicException)
92+
{
93+
_logger?.LogDebug(Resources.TraceNonDecryptedHostSecretRefresh);
94+
await PersistSecretsAsync(hostSecrets, null, true);
95+
await RefreshSecretsAsync(hostSecrets);
96+
}
97+
98+
// If the persistence state of any of our secrets is stale (e.g. the encryption key has been rotated), update
99+
// the state and persist the secrets
100+
if (hostSecrets.HasStaleKeys)
101+
{
102+
_logger.LogDebug(Resources.TraceStaleHostSecretRefresh);
103+
await RefreshSecretsAsync(hostSecrets);
104+
}
98105

99-
_hostSecrets = new HostSecretsInfo
106+
_hostSecrets = new HostSecretsInfo
107+
{
108+
MasterKey = hostSecrets.MasterKey.Value,
109+
FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value),
110+
SystemKeys = hostSecrets.SystemKeys.ToDictionary(s => s.Name, s => s.Value)
111+
};
112+
}
113+
finally
100114
{
101-
MasterKey = hostSecrets.MasterKey.Value,
102-
FunctionKeys = hostSecrets.FunctionKeys.ToDictionary(s => s.Name, s => s.Value),
103-
SystemKeys = hostSecrets.SystemKeys.ToDictionary(s => s.Name, s => s.Value)
104-
};
115+
_semaphoreSlim.Release();
116+
}
105117
}
106118

107119
return _hostSecrets;

0 commit comments

Comments
 (0)