add openhack files
This commit is contained in:
1
support/sqlsecretrotation/src/.dockerignore
Normal file
1
support/sqlsecretrotation/src/.dockerignore
Normal file
@ -0,0 +1 @@
|
||||
local.settings.json
|
28
support/sqlsecretrotation/src/AKVSQLRotation.cs
Normal file
28
support/sqlsecretrotation/src/AKVSQLRotation.cs
Normal file
@ -0,0 +1,28 @@
|
||||
// Default URL for triggering event grid function in the local environment.
|
||||
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
|
||||
using Azure.Messaging.EventGrid;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Microsoft.KeyVault
|
||||
{
|
||||
public static class AKVSQLRotation
|
||||
{
|
||||
|
||||
[FunctionName("AKVSQLRotation")]
|
||||
public static void Run([EventGridTrigger] EventGridEvent eventGridEvent, ILogger log)
|
||||
{
|
||||
log.LogInformation("C# Event trigger function processed a request.");
|
||||
var secretName = eventGridEvent.Subject;
|
||||
var secretVersion = Regex.Match(eventGridEvent.Data.ToString(), "Version\":\"([a-z0-9]*)").Groups[1].ToString();
|
||||
var keyVaultName = Regex.Match(eventGridEvent.Topic, ".vaults.(.*)").Groups[1].ToString();
|
||||
log.LogInformation($"Key Vault Name: {keyVaultName}");
|
||||
log.LogInformation($"Secret Name: {secretName}");
|
||||
log.LogInformation($"Secret Version: {secretVersion}");
|
||||
|
||||
SecretRotator.RotateSecret(log, secretName, keyVaultName);
|
||||
}
|
||||
}
|
||||
}
|
31
support/sqlsecretrotation/src/AKVSQLRotationHttp.cs
Normal file
31
support/sqlsecretrotation/src/AKVSQLRotationHttp.cs
Normal file
@ -0,0 +1,31 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Azure.WebJobs;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Azure.WebJobs.Extensions.Http;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Microsoft.KeyVault
|
||||
{
|
||||
public static class AKVSQLRotationHttp
|
||||
{
|
||||
[FunctionName("AKVSQLRotationHttp")]
|
||||
public static IActionResult Run(
|
||||
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
|
||||
ILogger log)
|
||||
{
|
||||
string keyVaultName = req.Query["KeyVaultName"];
|
||||
string secretName = req.Query["SecretName"];
|
||||
if (string.IsNullOrEmpty(keyVaultName) || string.IsNullOrEmpty(secretName))
|
||||
{
|
||||
return new BadRequestObjectResult("Please pass a KeyVaultName and SecretName on the query string");
|
||||
}
|
||||
|
||||
log.LogInformation(req.ToString());
|
||||
|
||||
log.LogInformation("C# Http trigger function processed a request.");
|
||||
SecretRotator.RotateSecret(log, secretName, keyVaultName);
|
||||
|
||||
return new OkObjectResult($"Secret Rotated Successfully");
|
||||
}
|
||||
}
|
||||
}
|
14
support/sqlsecretrotation/src/Dockerfile
Normal file
14
support/sqlsecretrotation/src/Dockerfile
Normal file
@ -0,0 +1,14 @@
|
||||
FROM mcr.microsoft.com/dotnet/core/sdk:3.1 AS installer-env
|
||||
|
||||
COPY . /src/dotnet-function-app
|
||||
RUN cd /src/dotnet-function-app && \
|
||||
mkdir -p /home/site/wwwroot && \
|
||||
dotnet publish *.csproj --output /home/site/wwwroot
|
||||
|
||||
# To enable ssh & remote debugging on app service change the base image to the one below
|
||||
# FROM mcr.microsoft.com/azure-functions/dotnet:3.0-appservice
|
||||
FROM mcr.microsoft.com/azure-functions/dotnet:3.0-slim
|
||||
ENV AzureWebJobsScriptRoot=/home/site/wwwroot \
|
||||
AzureFunctionsJobHost__Logging__Console__IsEnabled=true
|
||||
|
||||
COPY --from=installer-env ["/home/site/wwwroot", "/home/site/wwwroot"]
|
@ -0,0 +1,25 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
|
||||
<RootNamespace>Microsoft.KeyVault</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Azure.Messaging.EventGrid" Version="4.7.0" />
|
||||
<PackageReference Include="Microsoft.Azure.WebJobs.Extensions.EventGrid" Version="3.0.0" />
|
||||
<PackageReference Include="Microsoft.NET.Sdk.Functions" Version="3.0.13" />
|
||||
<PackageReference Include="Azure.Identity" Version="1.5.0" />
|
||||
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.2.0" />
|
||||
<PackageReference Include="Microsoft.Data.SqlClient" Version="3.0.1" />
|
||||
<PackageReference Include="Azure.Core" Version="1.20.0" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Update="host.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Update="local.settings.json">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
116
support/sqlsecretrotation/src/SecretRotator.cs
Normal file
116
support/sqlsecretrotation/src/SecretRotator.cs
Normal file
@ -0,0 +1,116 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
11
support/sqlsecretrotation/src/host.json
Normal file
11
support/sqlsecretrotation/src/host.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"version": "2.0",
|
||||
"logging": {
|
||||
"applicationInsights": {
|
||||
"samplingExcludedTypes": "Request",
|
||||
"samplingSettings": {
|
||||
"isEnabled": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
7
support/sqlsecretrotation/src/local.settings.json
Normal file
7
support/sqlsecretrotation/src/local.settings.json
Normal file
@ -0,0 +1,7 @@
|
||||
{
|
||||
"IsEncrypted": false,
|
||||
"Values": {
|
||||
"AzureWebJobsStorage": "UseDevelopmentStorage=true",
|
||||
"FUNCTIONS_WORKER_RUNTIME": "dotnet"
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user