Example - Outdated Secret Data
Introduction
There is an easy way to identify unhealthy Kubernetes resources with pods with outdated secrets.
Example - Pod with Outdated Secret Data
The below Cleaner instance finds all Pods 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.
By identifying and reporting pods that are accessing modified secrets, the Cleaner instance helps to safeguard applications and sensitive data.
---
apiVersion: apps.projectsveltos.io/v1alpha1
kind: Cleaner
metadata:
name: list-pods-with-outdated-secret-data
spec:
action: Scan
schedule: "0 * * * *"
notifications:
- name: report
type: CleanerReport
resourcePolicySet:
resourceSelectors:
- kind: Pod
group: ""
version: v1
- kind: Secret
group: ""
version: v1
aggregatedSelection: |
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 getPodTimestamp(pod)
if pod.status ~= nil and pod.status.conditions ~= nil then
for _,condition in ipairs(pod.status.conditions) do
if condition.type == "PodReadyToStartContainers" and condition.status == "True" then
return convertTimestampString(condition.lastTransitionTime)
end
end
end
return convertTimestampString(pod.metadata.creationTimestamp)
end
function hasOutdatedSecret(pod, secrets)
podTimestamp = getPodTimestamp(pod)
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, message = message})
end
end
if #podsWithOutdatedSecret > 0 then
hs.resources = podsWithOutdatedSecret
end
return hs
end