Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
.vscode

.vs
**/bin
**/obj
18 changes: 18 additions & 0 deletions example/HostCapabilitiesPolicy/HostCapabilitiesPolicy.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>warnings</Nullable> <!-- donet wasi sdk doesn't fully support nullable types -->
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\KubewardenPolicySDK\KubewardenPolicySDK.csproj" />
<PackageReference Include="KubernetesClient" Version="14.0.9" />
<PackageReference Include="WapcGuest" Version="0.1.1" />
<PackageReference Include="Wasi.Sdk" Version="0.1.1" />
</ItemGroup>


</Project>
16 changes: 16 additions & 0 deletions example/HostCapabilitiesPolicy/Policy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using KubewardenPolicySDK;
using WapcGuest;

namespace Policy
{
public class HostCapPolicy
{
public static void Main()
{
var wapc = new Wapc();
wapc.RegisterFunction("protocol_version", Kubewarden.ProtocolVersionGuest);
wapc.RegisterFunction("validate", PolicyRules.Validate);
wapc.RegisterFunction("validate_settings", PolicySettings.Validate);
}
}
}
81 changes: 81 additions & 0 deletions example/HostCapabilitiesPolicy/PolicyRules.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@

using k8s.Models;
using KubewardenPolicySDK;
using System.Text.Json;


namespace Policy
{
public class PolicyRules
{
public static byte[] Validate(byte[] payload)
{
try
{
ValidationRequest? validationRequest = JsonSerializer.Deserialize<ValidationRequest>(payload);

if (validationRequest is ValidationRequest req)
{
PolicySettings? policySettings = req.Settings.Deserialize<PolicySettings>();

return ProcessValidationRequest(ref req, policySettings);
}
else
{
return Kubewarden.RejectRequest("Invalid payload", 400, null, null);
}

}
catch (Exception e)
{
return Kubewarden.RejectRequest($"Internal errror: {e}", 500, null, null);
}
}


private static byte[] ProcessValidationRequest(ref ValidationRequest req, PolicySettings ps)
{
V1Pod maybePod = Kubewarden.GetResource<V1Pod>(req.Request.Namespace, "iliketobealone", true);
if(maybePod != null)
{
return Kubewarden.RejectRequest("A Pod that wants to be alone already exists!", 400, null, null);
}

KubernetesList<V1Namespace> nses = Kubewarden.ListResourcesAll<V1Namespace>("", "");
if(nses == null)
{
return Kubewarden.RejectRequest($"Internal errror", 500, null, null);
}
foreach(var ns in nses.Items)
{
Console.WriteLine($"Looking at namespace: {ns.Name()}");
//Ideally you would GetResource the actual namespace instead of all of them, but for demo purposes lets look at them all
if (ns.Name() == req.Request.Namespace && ns.Labels()?.ContainsKey("nopodsplease") == true)
{
return Kubewarden.RejectRequest("The namespace doesn't want any pods!", 400, null, null);
}
}

KubernetesList<V1ConfigMap> configMaps = Kubewarden.ListResourcesAll<V1ConfigMap>("", "");
if(configMaps == null)
{
return Kubewarden.RejectRequest($"Internal errror", 500, null, null);
}
foreach(var cm in configMaps.Items)
{
Console.WriteLine($"Looking at configmap: {cm.Name()}");
if(cm.Name().Contains("nopodsplease") == true)
{
return Kubewarden.RejectRequest("The configmap told us no pods!", 400, null, null);
}
}



return Kubewarden.AcceptRequest();
}


}

}
13 changes: 13 additions & 0 deletions example/HostCapabilitiesPolicy/PolicySettings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Policy;

using KubewardenPolicySDK;

public class PolicySettings
{

public static byte[] Validate(byte[] payload)
{
return Kubewarden.AcceptSettings();
}
}

23 changes: 23 additions & 0 deletions example/HostCapabilitiesPolicy/metadata.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
rules:
- apiGroups: [""]
apiVersions: ["v1"]
resources: ["Pods"]
operations: ["CREATE", "UPDATE"]
mutating: true
contextAwareResources:
- apiVersion: v1
kind: Pod
- apiVersion: v1
kind: Namespace
- apiVersion: v1
kind: ConfigMap
annotations:
io.kubewarden.policy.title: host-capabilities-policy
io.kubewarden.policy.description: Demonstrate how to use host capabilities to examine cluster objects and reject them if they don't meet certain criteria.
io.kubewarden.policy.author: Zach Brown
io.kubewarden.policy.url: https://github.com/kubewarden
io.kubewarden.policy.source: https://github.com/kubewarden
io.kubewarden.policy.license: Apache-2.0
io.kubewarden.policy.usage: |
This policy demonstrates leveraging host capabilities/contextAware policies to deny select pods

116 changes: 116 additions & 0 deletions src/KubewardenPolicySDK/Kubewarden.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
namespace KubewardenPolicySDK;

using k8s.Models;
using k8s;
using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
using WapcGuest;
using System.Reflection;

/// <summary>
/// Set of helper methods used to write Kubewarden policies.
Expand Down Expand Up @@ -112,5 +117,116 @@ public static byte[] ProtocolVersionGuest(byte[] _payload)
string protocol_version = "v1";
return JsonSerializer.SerializeToUtf8Bytes(protocol_version);
}

/// <summary>
/// Invokes the `list_resources` capability of the host
/// </summary>
/// <param name="labelSelector"></param>
/// <param name="fieldSelector"></param>
public static KubernetesList<T>? ListResourcesAll<T>(string labelSelector, string fieldSelector) where T : class, IKubernetesObject
{
var input = JsonSerializer.Serialize(new
{
api_version = ExtractAPIVersion<T>(),
kind = ExtractKubeKind<T>(),
label_selector = labelSelector,
field_selector = fieldSelector
});

var resp = Wapc.HostCall("kubewarden", "kubernetes", "list_resources_all", Encoding.UTF8.GetBytes(input));
var responseString = Encoding.UTF8.GetString(resp);
if (String.IsNullOrEmpty(responseString))
{
return null;
}
try
{
return JsonSerializer.Deserialize<KubernetesList<T>>(responseString);
}
catch (JsonException e)
{
throw new JsonException($"Error deserializing response: {resp}", e);
}
}

/// <summary>
/// Invokes the `list_resources_by_namespace` capability of the host
/// </summary>
/// <param name="labelSelector"></param>
/// <param name="fieldSelector"></param>
/// <param name="k8sNamespace"></param>
public static KubernetesList<T>? ListResourcesByNamespace<T>(string k8sNamespace, string labelSelector, string fieldSelector) where T : class, IKubernetesObject
{
var input = JsonSerializer.Serialize(new
{
api_version = ExtractAPIVersion<T>(),
kind = ExtractKubeKind<T>(),
label_selector = labelSelector,
field_selector = fieldSelector,
@namespace = k8sNamespace
});

var resp = Wapc.HostCall("kubewarden", "kubernetes", "list_resources", Encoding.UTF8.GetBytes(input));
var responseString = Encoding.UTF8.GetString(resp);
if (String.IsNullOrEmpty(responseString))
{
return null;
}
try
{
return JsonSerializer.Deserialize<KubernetesList<T>>(responseString);
}
catch (JsonException e)
{
throw new JsonException($"Error deserializing response: {resp}", e);
}
}

/// <summary>
/// Invokes the `list_resources_by_namespace` capability of the host
/// </summary>
/// <param name="k8sNamespace"></param>
/// <param name="name"></param>
/// <param name="disable_cache"></param>
public static T? GetResource<T>(string k8sNamespace, string name, bool disable_cache) where T : class, IKubernetesObject
{
var input = JsonSerializer.Serialize(new
{
api_version = ExtractAPIVersion<T>(),
kind = ExtractKubeKind<T>(),
@namespace = k8sNamespace,
name = name,
disable_cache = disable_cache
});

var resp = Wapc.HostCall("kubewarden", "kubernetes", "get_resource", Encoding.UTF8.GetBytes(input));
var responseString = Encoding.UTF8.GetString(resp);
if (String.IsNullOrEmpty(responseString))
{
return null;
}
try
{
return JsonSerializer.Deserialize<T>(responseString);
}
catch (JsonException e)
{
throw new JsonException($"Error deserializing response: {resp}", e);
}
}

private static string ExtractKubeKind<T>() where T : IKubernetesObject
{
var type = typeof(T);
var kea = type.GetCustomAttribute<KubernetesEntityAttribute>();
return kea?.Kind ?? type.Name;
}

private static string ExtractAPIVersion<T>() where T : IKubernetesObject
{
var type = typeof(T);
var kea = type.GetCustomAttribute<KubernetesEntityAttribute>();
return kea?.ApiVersion ?? type.Name;
}
}

9 changes: 7 additions & 2 deletions src/KubewardenPolicySDK/KubewardenPolicySDK.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,18 @@
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
</PropertyGroup>

<PropertyGroup Condition= " '$(Configuration)' == 'Debug' ">
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
<DocumentationFile>bin\Debug\net7.0\KubewardenPolicySDK.xml</DocumentationFile>
</PropertyGroup>

<ItemGroup>
<!-- Add README.md to nuget gallery -->
<None Include="README.md" Pack="true" PackagePath="\"/>
<None Include="README.md" Pack="true" PackagePath="\" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="KubernetesClient" Version="14.0.9" />
<PackageReference Include="WapcGuest" Version="0.1.1" />
</ItemGroup>

<Target Name="PackTaskDependencies" BeforeTargets="GenerateNuspec">
Expand Down