This repository has been archived on 2022-11-03. You can view files and clone it, but cannot push or open issues or pull requests.
DevOpsOpenHack/support/sqlsecretrotation/src/SecretRotator.cs
2022-11-03 16:41:13 -04:00

117 lines
5.6 KiB
C#

using Azure.Security.KeyVault.Secrets;
using Microsoft.Data.SqlClient;
using System;
using Microsoft.Extensions.Logging;
using Azure.Identity;
using System.Security.Cryptography;
namespace Microsoft.KeyVault
{
public class SecretRotator
{
private const string CredentialIdTag = "CredentialId";
private const string ProviderAddressTag = "ProviderAddress";
private const string ValidityPeriodDaysTag = "ValidityPeriodDays";
public static void RotateSecret(ILogger log, string secretName, string keyVaultName)
{
//Retrieve Current Secret
var kvUri = "https://" + keyVaultName + ".vault.azure.net";
var client = new SecretClient(new Uri(kvUri), new DefaultAzureCredential());
KeyVaultSecret secret = client.GetSecret(secretName);
log.LogInformation("Secret Info Retrieved");
//Retrieve Secret Info
var credentialId = secret.Properties.Tags.ContainsKey(CredentialIdTag) ? secret.Properties.Tags[CredentialIdTag] : "";
var providerAddress = secret.Properties.Tags.ContainsKey(ProviderAddressTag) ? secret.Properties.Tags[ProviderAddressTag] : "";
var validityPeriodDays = secret.Properties.Tags.ContainsKey(ValidityPeriodDaysTag) ? secret.Properties.Tags[ValidityPeriodDaysTag] : "";
log.LogInformation($"Provider Address: {providerAddress}");
log.LogInformation($"Credential Id: {credentialId}");
//Check Service Provider connection
CheckServiceConnection(secret);
log.LogInformation("Service Connection Validated");
//Create new password
var randomPassword = CreateRandomPassword();
log.LogInformation("New Password Generated");
//Add secret version with new password to Key Vault
CreateNewSecretVersion(client, secret, randomPassword);
log.LogInformation("New Secret Version Generated");
//Update Service Provider with new password
UpdateServicePassword(secret, randomPassword);
log.LogInformation("Password Changed");
log.LogInformation($"Secret Rotated Successfully");
}
private static void CreateNewSecretVersion(SecretClient client, KeyVaultSecret secret, string newSecretValue)
{
var credentialId = secret.Properties.Tags.ContainsKey(CredentialIdTag) ? secret.Properties.Tags[CredentialIdTag] : "";
var providerAddress = secret.Properties.Tags.ContainsKey(ProviderAddressTag) ? secret.Properties.Tags[ProviderAddressTag] : "";
var validityPeriodDays = secret.Properties.Tags.ContainsKey(ValidityPeriodDaysTag) ? secret.Properties.Tags[ValidityPeriodDaysTag] : "60";
//add new secret version to key vault
var newSecret = new KeyVaultSecret(secret.Name, newSecretValue);
newSecret.Properties.Tags.Add(CredentialIdTag, credentialId);
newSecret.Properties.Tags.Add(ProviderAddressTag, providerAddress);
newSecret.Properties.Tags.Add(ValidityPeriodDaysTag, validityPeriodDays);
newSecret.Properties.ExpiresOn = DateTime.UtcNow.AddDays(Int32.Parse(validityPeriodDays));
client.SetSecret(newSecret);
}
private static void UpdateServicePassword(KeyVaultSecret secret, string newpassword)
{
var userId = secret.Properties.Tags.ContainsKey(CredentialIdTag) ? secret.Properties.Tags[CredentialIdTag] : "";
var datasource = secret.Properties.Tags.ContainsKey(ProviderAddressTag) ? secret.Properties.Tags[ProviderAddressTag] : "";
var dbResourceId = secret.Properties.Tags.ContainsKey(ProviderAddressTag) ? secret.Properties.Tags[ProviderAddressTag] : "";
var dbName = dbResourceId.Split('/')[8];
var password = secret.Value;
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = $"{dbName}.database.windows.net";
builder.UserID = userId;
builder.Password = password;
//Update password
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
connection.Open();
using (SqlCommand command = new SqlCommand($"ALTER LOGIN {userId} WITH Password='{newpassword}';", connection))
{
command.ExecuteNonQuery();
}
}
}
private static string CreateRandomPassword()
{
const int length = 60;
byte[] randomBytes = new byte[length];
RNGCryptoServiceProvider rngCrypt = new RNGCryptoServiceProvider();
rngCrypt.GetBytes(randomBytes);
return Convert.ToBase64String(randomBytes);
}
private static void CheckServiceConnection(KeyVaultSecret secret)
{
var userId = secret.Properties.Tags.ContainsKey(CredentialIdTag) ? secret.Properties.Tags[CredentialIdTag] : "";
var dbResourceId = secret.Properties.Tags.ContainsKey(ProviderAddressTag) ? secret.Properties.Tags[ProviderAddressTag] : "";
var dbName = dbResourceId.Split('/')[8];
var password = secret.Value;
SqlConnectionStringBuilder builder = new SqlConnectionStringBuilder();
builder.DataSource = $"{dbName}.database.windows.net";
builder.UserID = userId;
builder.Password = password;
using (SqlConnection connection = new SqlConnection(builder.ConnectionString))
{
connection.Open();
}
}
}
}