Cross-Resource Evaluation
HealthCheck can also look at resources of different types together. This allows to perform more complex evaluations. This can be useful for more sophisticated tasks, such as identifying resources that are related to each other or that have similar properties. This HealthChech instance finds all Pods instances in all namespaces mounting Secrets. It identifies and reports any pods that are accessing Secrets that have been modified since the pod's creation. This is crucial for maintaining data integrity and security, as it prevents pods from accessing potentially outdated or compromised secrets.
# This HealthChech instance finds all Pods instances in all namespaces mounting Secrets.
# It identifies and reports any pods that are accessing Secrets that have been modified
# since the pod's creation.
---
apiVersion: lib.projectsveltos.io/v1beta1
kind: HealthCheck
metadata:
name: list-pods-with-outdated-secret-data
spec:
resourceSelectors:
- kind: Pod
group: ""
version: v1
- kind: Secret
group: ""
version: v1
evaluateHealth: |
function getKey(namespace, name)
return namespace .. ":" .. name
end
-- Convert creationTimestamp "2023-12-12T09:35:56Z"
function convertTimestampString(timestampStr)
local convertedTimestamp = string.gsub(
timestampStr,
'(%d+)-(%d+)-(%d+)T(%d+):(%d+):(%d+)Z',
function(y, mon, d, h, mi, s)
return os.time({
year = tonumber(y),
month = tonumber(mon),
day = tonumber(d),
hour = tonumber(h),
min = tonumber(mi),
sec = tonumber(s)
})
end
)
return convertedTimestamp
end
function getLatestTime(times)
local latestTime = nil
for _, time in ipairs(times) do
if latestTime == nil or os.difftime(tonumber(time), tonumber(latestTime)) > 0 then
latestTime = time
end
end
return latestTime
end
function getSecretUpdateTime(secret)
local times = {}
if secret.metadata.managedFields ~= nil then
for _, mf in ipairs(secret.metadata.managedFields) do
if mf.time ~= nil then
table.insert(times, convertTimestampString(mf.time))
end
end
end
return getLatestTime(times)
end
function isPodOlderThanSecret(podTimestamp, secretTimestamp)
timeDifference = os.difftime(tonumber(podTimestamp), tonumber(secretTimestamp))
return timeDifference < 0
end
function hasOutdatedSecret(pod, secrets)
podTimestamp = convertTimestampString(pod.metadata.creationTimestamp)
if pod.spec.containers ~= nil then
for _, container in ipairs(pod.spec.containers) do
if container.env ~= nil then
for _, env in ipairs(container.env) do
if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then
key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name)
if isPodOlderThanSecret(podTimestamp, secrets[key]) then
return true, "secret " .. key .. " has been updated after pod creation"
end
end
end
end
if container.envFrom ~= nil then
for _, envFrom in ipairs(container.envFrom) do
if envFrom.secretRef ~= nil then
key = getKey(pod.metadata.namespace, envFrom.secretRef.name)
if isPodOlderThanSecret(podTimestamp, secrets[key]) then
return true, "secret " .. key .. " has been updated after pod creation"
end
end
end
end
end
end
if pod.spec.initContainers ~= nil then
for _, initContainer in ipairs(pod.spec.initContainers) do
if initContainer.env ~= nil then
for _, env in ipairs(initContainer.env) do
if env.valueFrom ~= nil and env.valueFrom.secretKeyRef ~= nil then
key = getKey(pod.metadata.namespace, env.valueFrom.secretKeyRef.name)
if isPodOlderThanSecret(podTimestamp, secrets[key]) then
return true, "secret " .. key .. " has been updated after pod creation"
end
end
end
end
end
end
if pod.spec.volumes ~= nil then
for _, volume in ipairs(pod.spec.volumes) do
if volume.secret ~= nil then
key = getKey(pod.metadata.namespace, volume.secret.secretName)
if isPodOlderThanSecret(podTimestamp, secrets[key]) then
return true, "secret " .. key .. " has been updated after pod creation"
end
end
if volume.projected ~= nil and volume.projected.sources ~= nil then
for _, projectedResource in ipairs(volume.projected.sources) do
if projectedResource.secret ~= nil then
key = getKey(pod.metadata.namespace, projectedResource.secret.name)
if isPodOlderThanSecret(podTimestamp, secrets[key]) then
return true, "secret " .. key .. " has been updated after pod creation"
end
end
end
end
end
end
return false
end
function evaluate()
local hs = {}
hs.message = ""
local pods = {}
local secrets = {}
-- Separate secrets and pods
for _, resource in ipairs(resources) do
local kind = resource.kind
if kind == "Secret" then
key = getKey(resource.metadata.namespace, resource.metadata.name)
updateTimestamp = getSecretUpdateTime(resource)
secrets[key] = updateTimestamp
elseif kind == "Pod" then
table.insert(pods, resource)
end
end
local podsWithOutdatedSecret = {}
for _, pod in ipairs(pods) do
outdatedData, message = hasOutdatedSecret(pod, secrets)
if outdatedData then
table.insert(podsWithOutdatedSecret, {resource= pod, status="Degraded", message = message})
end
end
if #podsWithOutdatedSecret > 0 then
hs.resources = podsWithOutdatedSecret
end
return hs
end
Following is a report generated for above HealthCheck
apiVersion: lib.projectsveltos.io/v1beta1
kind: HealthCheckReport
metadata:
labels:
projectsveltos.io/healthcheck-name: list-pods-with-outdated-secret-data
name: list-pods-with-outdated-secret-data
namespace: projectsveltos
...
spec:
...
healthCheckName: list-pods-with-outdated-secret-data
resourceStatuses:
- healthStatus: Degraded
message: secret foo:dotfile-secret has been updated after pod creation
objectRef:
apiVersion: v1
kind: Pod
name: secret-dotfiles-pod
namespace: foo
...