-
Notifications
You must be signed in to change notification settings - Fork 68
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added support for binary data in kamus secret (#248)
* added support for binary data in kamus secret * documentaion * fix the tests * fix the tests build * try to fix the test * fix the tests * rename to encodedData * refactor - add v1alpha2 which is compatible with k8s secrets * update docs * fix the build * try to fix the tests * fix the tests * store only v2 * try to fix the tests * try to fix the tests * clean up * try to fix the build * added support for conversation webhook * revert test changes * try to fix the build * remove encodedData * force https for the controller * temporary - just make it work * fix the tests * ugly patch :praying af * try to fix the build * no judgment * enable CustomResourceWebhookConversion * try to fix the build * maybe :shrug * fix the build * try again * pleaseeee * added logging * clarify docs * remove certificate from the dockerfile * this time create the certificate for the proper name * Update src/crd-controller/Startup.cs Co-Authored-By: Shai Katz <shaikatz@users.noreply.github.com> * Update tests/crd-controller/deployment.yaml Co-Authored-By: Shai Katz <shaikatz@users.noreply.github.com> * Update src/crd-controller/Models/V1Alpha2/KamusSecret.cs Co-Authored-By: Shai Katz <shaikatz@users.noreply.github.com> * fix CR comments * try to fix the build * add comments * version bump
- Loading branch information
Showing
29 changed files
with
708 additions
and
128 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
112 changes: 112 additions & 0 deletions
112
src/crd-controller/Controllers/ConversionWebhookController.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
using System; | ||
using System.Linq; | ||
using CustomResourceDescriptorController.Models; | ||
using k8s.Models; | ||
using Microsoft.AspNetCore.Mvc; | ||
using Newtonsoft.Json.Linq; | ||
using Serilog; | ||
|
||
namespace CustomResourceDescriptorController.Controllers | ||
{ | ||
public class ConversionWebhookController : Controller | ||
{ | ||
private readonly ILogger mLogger = Log.ForContext<ConversionWebhookController>(); | ||
|
||
[HttpPost] | ||
[Route("/api/v1/conversion-webhook")] | ||
public ActionResult<ConversionReview> Convert([FromBody]ConversionReview conversionReview) | ||
{ | ||
ConversionReviewResponse response; | ||
|
||
mLogger.Information("Received conversion request"); | ||
try | ||
{ | ||
response = new ConversionReviewResponse | ||
{ | ||
UID = conversionReview.Request.UID, | ||
ConvertedObjects = conversionReview.Request.Objects.Select(o => Convert(o, conversionReview.Request.DesiredAPIVersion)).ToArray(), | ||
Result = new V1Status | ||
{ | ||
Status = "Success" | ||
} | ||
}; | ||
} | ||
catch (Exception e) | ||
{ | ||
mLogger.Error(e, "Coversation failed"); | ||
response = new ConversionReviewResponse | ||
{ | ||
UID = conversionReview.Request.UID, | ||
Result = new V1Status | ||
{ | ||
Status = "Failure", | ||
Message = "Conversation failed, check logs for more details" | ||
} | ||
}; | ||
} | ||
|
||
return new ConversionReview | ||
{ | ||
Kind = conversionReview.Kind, | ||
ApiVersion = conversionReview.ApiVersion, | ||
Response = response | ||
}; | ||
} | ||
|
||
private object Convert(JObject source, string desiredApiVersion) | ||
{ | ||
var apiVersion = source.Value<string>("apiVersion"); | ||
|
||
mLogger.Information("Starting to convert from {apiVersion} to {desirediVersion}", apiVersion, desiredApiVersion); | ||
|
||
switch (desiredApiVersion) | ||
{ | ||
case "soluto.com/v1alpha1": | ||
switch (apiVersion) | ||
{ | ||
case "soluto.com/v1alpha2": | ||
var sourceKamusSecret = source.ToObject<Models.V1Alpha2.KamusSecret>(); | ||
return new Models.V1Alpha1.KamusSecret | ||
{ | ||
Data = sourceKamusSecret.StringData, | ||
ServiceAccount = sourceKamusSecret.ServiceAccount, | ||
Metadata = sourceKamusSecret.Metadata, | ||
Kind = "KamusSecret", | ||
Type = sourceKamusSecret.Type, | ||
ApiVersion = desiredApiVersion | ||
}; | ||
|
||
default: | ||
throw new InvalidOperationException($"Unsupported conversation from {apiVersion} to {desiredApiVersion}"); | ||
} | ||
|
||
|
||
case "soluto.com/v1alpha2": | ||
|
||
switch (apiVersion) | ||
{ | ||
case "soluto.com/v1alpha1": | ||
var sourceKamusSecret = source.ToObject<Models.V1Alpha1.KamusSecret>(); | ||
return new Models.V1Alpha2.KamusSecret | ||
{ | ||
StringData = sourceKamusSecret.Data, | ||
ServiceAccount = sourceKamusSecret.ServiceAccount, | ||
Metadata = sourceKamusSecret.Metadata, | ||
Kind = "KamusSecret", | ||
Type = sourceKamusSecret.Type, | ||
ApiVersion = desiredApiVersion | ||
}; | ||
|
||
default: | ||
throw new InvalidOperationException($"Unsupported conversation from {apiVersion} to {desiredApiVersion}"); | ||
} | ||
|
||
default: | ||
throw new InvalidOperationException($"Unsupported conversation from {apiVersion} to {desiredApiVersion}"); | ||
} | ||
|
||
} | ||
} | ||
|
||
|
||
} |
180 changes: 180 additions & 0 deletions
180
src/crd-controller/HostedServices/V1Alpha1Controller.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
using System; | ||
using System.Linq; | ||
using System.Reactive.Linq; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using CustomResourceDescriptorController.Models.V1Alpha1; | ||
using k8s; | ||
using k8s.Models; | ||
using Kamus.KeyManagement; | ||
using CustomResourceDescriptorController.Extensions; | ||
using Microsoft.AspNetCore.JsonPatch; | ||
using Microsoft.Extensions.Hosting; | ||
using Serilog; | ||
using CustomResourceDescriptorController.utils; | ||
|
||
namespace CustomResourceDescriptorController.HostedServices | ||
{ | ||
public class V1Alpha1Controller : IHostedService | ||
{ | ||
private readonly IKubernetes mKubernetes; | ||
private readonly IKeyManagement mKeyManagement; | ||
private IDisposable mSubscription; | ||
private readonly ILogger mAuditLogger = Log.ForContext<V1Alpha1Controller>().AsAudit(); | ||
private readonly ILogger mLogger = Log.ForContext<V1Alpha1Controller>(); | ||
|
||
public V1Alpha1Controller(IKubernetes kubernetes, IKeyManagement keyManagement) | ||
{ | ||
this.mKubernetes = kubernetes; | ||
this.mKeyManagement = keyManagement; | ||
} | ||
|
||
public Task StopAsync(CancellationToken cancellationToken) | ||
{ | ||
if (mSubscription != null) | ||
{ | ||
mSubscription.Dispose(); | ||
} | ||
return Task.CompletedTask; | ||
} | ||
|
||
public Task StartAsync(CancellationToken token) | ||
{ | ||
mSubscription = | ||
mKubernetes.ObserveClusterCustomObject<KamusSecret>( | ||
"soluto.com", | ||
"v1alpha1", | ||
"kamussecrets", | ||
token) | ||
.SelectMany(x => | ||
Observable.FromAsync(async () => await HandleEvent(x.Item1, x.Item2)) | ||
) | ||
.Subscribe( | ||
onNext: t => { }, | ||
onError: e => | ||
{ | ||
mLogger.Error(e, "Unexpected error occured while watching KamusSecret events"); | ||
Environment.Exit(1); | ||
}, | ||
onCompleted: () => | ||
{ | ||
mLogger.Information("Watching KamusSecret events completed, terminating process"); | ||
Environment.Exit(0); | ||
}); | ||
|
||
mLogger.Information("Starting watch for KamusSecret V1Alpha1 events"); | ||
|
||
return Task.CompletedTask; | ||
} | ||
|
||
private async Task HandleEvent(WatchEventType @event, KamusSecret kamusSecret) | ||
{ | ||
try | ||
{ | ||
mLogger.Information("Handling event of type {type}. KamusSecret {name} in namespace {namespace}", | ||
@event.ToString(), | ||
kamusSecret.Metadata.Name, | ||
kamusSecret.Metadata.NamespaceProperty ?? "default"); | ||
|
||
switch (@event) | ||
{ | ||
case WatchEventType.Added: | ||
await HandleAdd(kamusSecret); | ||
return; | ||
|
||
case WatchEventType.Deleted: | ||
await HandleDelete(kamusSecret); | ||
return; | ||
|
||
case WatchEventType.Modified: | ||
await HandleModify(kamusSecret); | ||
return; | ||
default: | ||
mLogger.Warning( | ||
"Event of type {type} is not supported. KamusSecret {name} in namespace {namespace}", | ||
@event.ToString(), | ||
kamusSecret.Metadata.Name, | ||
kamusSecret.Metadata.NamespaceProperty ?? "default"); | ||
return; | ||
|
||
} | ||
} | ||
catch (Exception e) | ||
{ | ||
mLogger.Error(e, | ||
"Error while handling KamusSecret event of type {eventType}, for KamusSecret {name} on namespace {namespace}", | ||
@event.ToString(), | ||
kamusSecret.Metadata.Name, | ||
kamusSecret.Metadata.NamespaceProperty ?? "default"); | ||
} | ||
} | ||
|
||
private async Task<V1Secret> CreateSecret(KamusSecret kamusSecret) | ||
{ | ||
var @namespace = kamusSecret.Metadata.NamespaceProperty ?? "default"; | ||
var serviceAccount = kamusSecret.ServiceAccount; | ||
var id = $"{@namespace}:{serviceAccount}"; | ||
|
||
mLogger.Debug("Starting decrypting KamusSecret items. KamusSecret {name} in namespace {namespace}", | ||
kamusSecret.Metadata.Name, | ||
@namespace); | ||
|
||
Action<Exception, string> errorHandler = (e, key) => mLogger.Error(e, | ||
"Failed to decrypt KamusSecret key {key}. KamusSecret {name} in namespace {namespace}", | ||
key, | ||
kamusSecret.Metadata.Name, | ||
@namespace); | ||
|
||
var decryptedStrings = await mKeyManagement.DecryptItems(kamusSecret.Data, id, errorHandler, x => x); | ||
|
||
mLogger.Debug("KamusSecret items decrypted successfully. KamusSecret {name} in namespace {namespace}", | ||
kamusSecret.Metadata.Name, | ||
@namespace); | ||
|
||
return new V1Secret | ||
{ | ||
Metadata = new V1ObjectMeta | ||
{ | ||
Name = kamusSecret.Metadata.Name, | ||
NamespaceProperty = @namespace | ||
}, | ||
Type = kamusSecret.Type, | ||
StringData = decryptedStrings | ||
}; | ||
} | ||
|
||
private async Task HandleAdd(KamusSecret kamusSecret, bool isUpdate = false) | ||
{ | ||
var secret = await CreateSecret(kamusSecret); | ||
var createdSecret = | ||
await mKubernetes.CreateNamespacedSecretAsync(secret, secret.Metadata.NamespaceProperty); | ||
|
||
mAuditLogger.Information("Created a secret from KamusSecret {name} in namespace {namespace} successfully.", | ||
kamusSecret.Metadata.Name, | ||
secret.Metadata.NamespaceProperty); | ||
} | ||
|
||
private async Task HandleModify(KamusSecret kamusSecret) | ||
{ | ||
var secret = await CreateSecret(kamusSecret); | ||
var secretPatch = new JsonPatchDocument<V1Secret>(); | ||
secretPatch.Replace(e => e.StringData, secret.StringData); | ||
var createdSecret = await mKubernetes.PatchNamespacedSecretAsync( | ||
new V1Patch(secretPatch), | ||
kamusSecret.Metadata.Name, | ||
secret.Metadata.NamespaceProperty | ||
); | ||
|
||
mAuditLogger.Information("Updated a secret from KamusSecret {name} in namespace {namespace} successfully.", | ||
kamusSecret.Metadata.Name, | ||
secret.Metadata.NamespaceProperty); | ||
} | ||
|
||
private async Task HandleDelete(KamusSecret kamusSecret) | ||
{ | ||
var @namespace = kamusSecret.Metadata.NamespaceProperty ?? "default"; | ||
|
||
await mKubernetes.DeleteNamespacedSecretAsync(kamusSecret.Metadata.Name, @namespace); | ||
} | ||
} | ||
} |
Oops, something went wrong.