Custom Development
This guide covers custom development and extension of the Secure CINC Auditor Kubernetes Container Scanning solution.
Overview
The scanner solution can be extended and customized to meet specific requirements. This guide covers custom script development, plugin creation, and integration with other systems.
Custom Script Development
For advanced users who need to develop custom deployment scripts:
| #!/bin/bash
# custom-deployment.sh
# Configuration variables
NAMESPACE="security-scanner"
SCANNER_IMAGE="cinc/auditor:latest"
PROFILE_PATH="/path/to/profiles"
THRESHOLD_FILE="/path/to/thresholds.yml"
TOKEN_DURATION="1h"
# Create namespace
kubectl create namespace $NAMESPACE
# Create RBAC
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: ServiceAccount
metadata:
name: scanner-sa
namespace: $NAMESPACE
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: scanner-role
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: scanner-rolebinding
subjects:
- kind: ServiceAccount
name: scanner-sa
namespace: $NAMESPACE
roleRef:
kind: ClusterRole
name: scanner-role
apiGroup: rbac.authorization.k8s.io
EOF
# Create scanner job
cat <<EOF | kubectl apply -f -
apiVersion: batch/v1
kind: Job
metadata:
name: scanner-job
namespace: $NAMESPACE
spec:
template:
spec:
serviceAccountName: scanner-sa
containers:
- name: scanner
image: $SCANNER_IMAGE
command:
- /bin/sh
- -c
- |
cinc-auditor exec $PROFILE_PATH -t k8s-container://target-container --namespace default
restartPolicy: Never
backoffLimit: 1
EOF
# Wait for job completion
kubectl wait --for=condition=complete job/scanner-job -n $NAMESPACE --timeout=300s
# Get results
pod=$(kubectl get pods -n $NAMESPACE -l job-name=scanner-job -o jsonpath='{.items[0].metadata.name}')
kubectl logs $pod -n $NAMESPACE > results.json
# Validate results
echo "Validating results against thresholds..."
saf validate -i results.json -f $THRESHOLD_FILE
# Cleanup
kubectl delete job scanner-job -n $NAMESPACE
|
Best Practices for Custom Scripts
Follow these best practices when developing custom scripts:
- Error Handling: Implement robust error handling and logging
- Idempotency: Make scripts idempotent to allow retries
- Parameterization: Make scripts configurable through parameters
- Documentation: Document script purpose, parameters, and usage
- Testing: Test scripts in isolated environments before production use
| #!/bin/bash
# Example of a well-structured custom script
set -e # Exit on error
set -o pipefail # Exit on pipe failure
# Script information
# Name: enhanced-scanner.sh
# Description: Enhanced scanner deployment with custom configurations
# Usage: ./enhanced-scanner.sh [options]
# Options:
# -n, --namespace NAMESPACE Namespace to deploy scanner (default: scanner-system)
# -i, --image IMAGE Scanner image to use (default: cinc/auditor:latest)
# -p, --profile PROFILE Profile path (default: profiles/baseline)
# -t, --timeout SECONDS Scan timeout in seconds (default: 300)
# -h, --help Show this help message
# Default values
NAMESPACE="scanner-system"
IMAGE="cinc/auditor:latest"
PROFILE="profiles/baseline"
TIMEOUT=300
# Parse arguments
while [[ $# -gt 0 ]]; do
key="$1"
case $key in
-n|--namespace)
NAMESPACE="$2"
shift 2
;;
-i|--image)
IMAGE="$2"
shift 2
;;
-p|--profile)
PROFILE="$2"
shift 2
;;
-t|--timeout)
TIMEOUT="$2"
shift 2
;;
-h|--help)
echo "Usage: ./enhanced-scanner.sh [options]"
echo "Options:"
echo " -n, --namespace NAMESPACE Namespace to deploy scanner (default: scanner-system)"
echo " -i, --image IMAGE Scanner image to use (default: cinc/auditor:latest)"
echo " -p, --profile PROFILE Profile path (default: profiles/baseline)"
echo " -t, --timeout SECONDS Scan timeout in seconds (default: 300)"
echo " -h, --help Show this help message"
exit 0
;;
*)
echo "Unknown option: $1"
exit 1
;;
esac
done
# Logging function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $1"
}
# Error handling function
handle_error() {
log "ERROR: $1"
exit 1
}
# Verify kubectl is installed
if ! command -v kubectl &> /dev/null; then
handle_error "kubectl not found, please install kubectl"
fi
# Main functionality
log "Starting scanner deployment in namespace $NAMESPACE"
# Create namespace if it doesn't exist
kubectl get namespace $NAMESPACE &> /dev/null || kubectl create namespace $NAMESPACE
# Rest of the script...
|
Custom Profile Development
Develop custom security profiles for specific requirements:
| # Example custom profile: kubernetes-custom.rb
control 'K8S-CUSTOM-1' do
impact 1.0
title 'Ensure containers do not run with privileged flag'
desc 'Running containers with the privileged flag gives them full access to the host system.'
containers = json('/api/v1/pods').pod.each_with_object([]) do |pod, arr|
pod.spec.containers.each do |container|
arr << {
'pod' => pod.metadata.name,
'container' => container.name,
'privileged' => container.securityContext.privileged
}
end
end
containers.each do |container|
describe "Container #{container['pod']}/#{container['container']}" do
it 'should not have privileged flag set to true' do
expect(container['privileged']).not_to eq true
end
end
end
end
|
Custom Integration Development
Develop custom integrations with other systems:
| # Example custom integration: slack-notifier.rb
require 'uri'
require 'net/http'
require 'json'
class SlackNotifier
def initialize(webhook_url)
@webhook_url = webhook_url
end
def notify(message, severity = :info)
color = case severity
when :info then "#36a64f"
when :warning then "#ffcc00"
when :critical then "#ff0000"
else "#eeeeee"
end
payload = {
text: "Scanner Notification",
attachments: [
{
color: color,
text: message,
ts: Time.now.to_i
}
]
}
send_notification(payload)
end
def notify_scan_results(results)
stats = calculate_statistics(results)
color = if stats[:critical] > 0
"#ff0000"
elsif stats[:high] > 0
"#ff9900"
elsif stats[:medium] > 0
"#ffcc00"
else
"#36a64f"
end
payload = {
text: "Scan Completed",
attachments: [
{
color: color,
title: "Scan Results Summary",
fields: [
{
title: "Critical Findings",
value: stats[:critical],
short: true
},
{
title: "High Findings",
value: stats[:high],
short: true
},
{
title: "Medium Findings",
value: stats[:medium],
short: true
},
{
title: "Low Findings",
value: stats[:low],
short: true
}
],
ts: Time.now.to_i
}
]
}
send_notification(payload)
end
private
def send_notification(payload)
uri = URI.parse(@webhook_url)
http = Net::HTTP.new(uri.host, uri.port)
http.use_ssl = (uri.scheme == 'https')
request = Net::HTTP::Post.new(uri.request_uri)
request.content_type = 'application/json'
request.body = payload.to_json
response = http.request(request)
unless response.code.to_i == 200
puts "Error sending notification: #{response.code} - #{response.body}"
end
end
def calculate_statistics(results)
stats = { critical: 0, high: 0, medium: 0, low: 0 }
results['profiles'].each do |profile|
profile['controls'].each do |control|
control['results'].each do |result|
if result['status'] == 'failed'
case control['impact']
when 0.9..1.0
stats[:critical] += 1
when 0.7...0.9
stats[:high] += 1
when 0.4...0.7
stats[:medium] += 1
else
stats[:low] += 1
end
end
end
end
end
stats
end
end
# Usage
notifier = SlackNotifier.new('https://hooks.slack.com/services/YOUR/WEBHOOK/URL')
notifier.notify('Scanner started', :info)
# After scan completes
results = JSON.parse(File.read('results.json'))
notifier.notify_scan_results(results)
|
Custom Helm Chart Development
Develop custom Helm charts for specialized deployments:
| # Example custom-scanner/Chart.yaml
apiVersion: v2
name: custom-scanner
description: A custom scanner for specialized environments
type: application
version: 0.1.0
appVersion: 1.0.0
dependencies:
- name: scanner-infrastructure
version: ">=1.0.0"
repository: "file://../scanner-infrastructure"
|
| # Example custom-scanner/values.yaml
global:
environment: production
namespace: custom-scanner
scanner:
image:
repository: custom/scanner
tag: latest
customization:
enabled: true
features:
- name: specialized-scans
enabled: true
- name: custom-reporting
enabled: true
configuration:
specializedMode: true
reportFormat: "custom-format"
|
| # Example custom-scanner/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ include "custom-scanner.fullname" . }}
labels:
{{- include "custom-scanner.labels" . | nindent 4 }}
spec:
replicas: {{ .Values.replicaCount }}
selector:
matchLabels:
{{- include "custom-scanner.selectorLabels" . | nindent 6 }}
template:
metadata:
labels:
{{- include "custom-scanner.selectorLabels" . | nindent 8 }}
spec:
serviceAccountName: {{ include "custom-scanner.serviceAccountName" . }}
containers:
- name: {{ .Chart.Name }}
image: "{{ .Values.scanner.image.repository }}:{{ .Values.scanner.image.tag }}"
imagePullPolicy: {{ .Values.scanner.image.pullPolicy }}
env:
- name: CUSTOM_MODE
value: "{{ .Values.scanner.customization.configuration.specializedMode }}"
- name: REPORT_FORMAT
value: "{{ .Values.scanner.customization.configuration.reportFormat }}"
volumeMounts:
- name: config-volume
mountPath: /config
- name: custom-profiles
mountPath: /profiles
volumes:
- name: config-volume
configMap:
name: {{ include "custom-scanner.fullname" . }}-config
- name: custom-profiles
configMap:
name: {{ include "custom-scanner.fullname" . }}-profiles
|