🔒 PHASE 7: SECRETS MANAGEMENT & SECURITY (Gün 17-18)
🔐 8.1 HashiCorp Vault Setup
# 8.1.1 Vault Helm kurulumu
helm repo add hashicorp https://helm.releases.hashicorp.com
helm repo update
cat > vault-values.yaml << 'EOF'
global:
enabled: true
tlsDisable: false
injector:
enabled: true
replicas: 1
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 256Mi
cpu: 250m
server:
image:
repository: "vault"
tag: "1.15.0"
resources:
requests:
memory: 256Mi
cpu: 250m
limits:
memory: 256Mi
cpu: 250m
readinessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true&sealedcode=204&uninitcode=204"
livenessProbe:
enabled: true
path: "/v1/sys/health?standbyok=true"
initialDelaySeconds: 60
extraEnvironmentVars:
VAULT_CACERT: /vault/userconfig/vault-ha-tls/vault.ca
VAULT_TLSCERT: /vault/userconfig/vault-ha-tls/vault.crt
VAULT_TLSKEY: /vault/userconfig/vault-ha-tls/vault.key
extraVolumes:
- type: secret
name: vault-ha-tls
path: /vault/userconfig
standalone:
enabled: false
ha:
enabled: true
replicas: 3
raft:
enabled: true
setNodeId: true
config: |
ui = true
listener "tcp" {
tls_disable = 0
address = "[::]:8200"
cluster_address = "[::]:8201"
tls_cert_file = "/vault/userconfig/vault-ha-tls/vault.crt"
tls_key_file = "/vault/userconfig/vault-ha-tls/vault.key"
tls_client_ca_file = "/vault/userconfig/vault-ha-tls/vault.ca"
}
storage "raft" {
path = "/vault/data"
retry_join {
leader_api_addr = "https://vault-0.vault-internal:8200"
leader_ca_cert_file = "/vault/userconfig/vault-ha-tls/vault.ca"
leader_client_cert_file = "/vault/userconfig/vault-ha-tls/vault.crt"
leader_client_key_file = "/vault/userconfig/vault-ha-tls/vault.key"
}
retry_join {
leader_api_addr = "https://vault-1.vault-internal:8200"
leader_ca_cert_file = "/vault/userconfig/vault-ha-tls/vault.ca"
leader_client_cert_file = "/vault/userconfig/vault-ha-tls/vault.crt"
leader_client_key_file = "/vault/userconfig/vault-ha-tls/vault.key"
}
retry_join {
leader_api_addr = "https://vault-2.vault-internal:8200"
leader_ca_cert_file = "/vault/userconfig/vault-ha-tls/vault.ca"
leader_client_cert_file = "/vault/userconfig/vault-ha-tls/vault.crt"
leader_client_key_file = "/vault/userconfig/vault-ha-tls/vault.key"
}
}
service_registration "kubernetes" {}
service:
enabled: true
type: ClusterIP
port: 8200
targetPort: 8200
dataStorage:
enabled: true
size: 10Gi
storageClass: gp3
auditStorage:
enabled: true
size: 10Gi
storageClass: gp3
ui:
enabled: true
serviceType: ClusterIP
EOF
# 8.1.2 TLS sertifikaları oluştur
mkdir -p vault-tls
cd vault-tls
# CA private key
openssl genrsa -out vault-ca.key 2048
# CA certificate
openssl req -new -x509 -key vault-ca.key -out vault-ca.crt -days 365 \
-subj "/C=US/ST=CA/L=San Francisco/O=HashiCorp/CN=Vault CA"
# Vault private key
openssl genrsa -out vault.key 2048
# Vault certificate signing request
cat > vault.conf << 'EOF'
[req]
distinguished_name = req_distinguished_name
req_extensions = v3_req
prompt = no
[req_distinguished_name]
C = US
ST = CA
L = San Francisco
O = HashiCorp
CN = vault
[v3_req]
keyUsage = keyEncipherment, dataEncipherment
extendedKeyUsage = serverAuth
subjectAltName = @alt_names
[alt_names]
DNS.1 = vault
DNS.2 = vault.vault
DNS.3 = vault.vault.svc
DNS.4 = vault.vault.svc.cluster.local
DNS.5 = vault-0.vault-internal
DNS.6 = vault-1.vault-internal
DNS.7 = vault-2.vault-internal
DNS.8 = vault-0.vault-internal.vault.svc.cluster.local
DNS.9 = vault-1.vault-internal.vault.svc.cluster.local
DNS.10 = vault-2.vault-internal.vault.svc.cluster.local
DNS.11 = vault.yourdomain.com
IP.1 = 127.0.0.1
EOF
openssl req -new -key vault.key -out vault.csr -config vault.conf
# Vault certificate
openssl x509 -req -in vault.csr -CA vault-ca.crt -CAkey vault-ca.key \
-CAcreateserial -out vault.crt -days 365 -extensions v3_req -extfile vault.conf
# 8.1.3 Vault namespace ve TLS secret oluştur
kubectl create namespace vault
kubectl create secret generic vault-ha-tls \
--from-file=vault.key=vault.key \
--from-file=vault.crt=vault.crt \
--from-file=vault.ca=vault-ca.crt \
--namespace vault
cd ..
# 8.1.4 Vault kurulumu
helm install vault hashicorp/vault \
--namespace vault \
--values vault-values.yaml
# 8.1.5 Vault'u initialize et ve unseal et
kubectl exec vault-0 -n vault -- vault operator init \
-key-shares=5 \
-key-threshold=3 \
-format=json > cluster-keys.json
# Root token ve unseal key'leri çıkar
VAULT_UNSEAL_KEY_1=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[0]")
VAULT_UNSEAL_KEY_2=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[1]")
VAULT_UNSEAL_KEY_3=$(cat cluster-keys.json | jq -r ".unseal_keys_b64[2]")
CLUSTER_ROOT_TOKEN=$(cat cluster-keys.json | jq -r ".root_token")
# Vault unseal
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_1
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_2
kubectl exec vault-0 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_3
# Diğer node'ları join et
kubectl exec vault-1 -n vault -- vault operator raft join https://vault-0.vault-internal:8200
kubectl exec vault-1 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_1
kubectl exec vault-1 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_2
kubectl exec vault-1 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_3
kubectl exec vault-2 -n vault -- vault operator raft join https://vault-0.vault-internal:8200
kubectl exec vault-2 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_1
kubectl exec vault-2 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_2
kubectl exec vault-2 -n vault -- vault operator unseal $VAULT_UNSEAL_KEY_3
echo "Root Token: $CLUSTER_ROOT_TOKEN"
🔧 8.2 External Secrets Operator
# 8.2.1 External Secrets Operator kurulumu
helm repo add external-secrets https://charts.external-secrets.io
helm repo update
helm install external-secrets external-secrets/external-secrets \
--namespace external-secrets \
--create-namespace
# 8.2.2 Vault'ta Kubernetes auth method aktifleştir
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault auth enable kubernetes
# Service account token path'ini al
TOKEN_REVIEW_JWT=$(kubectl get secret \
$(kubectl get serviceaccount vault -n vault -o jsonpath='{.secrets[0].name}') \
-n vault -o jsonpath='{.data.token}' | base64 --decode)
KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.certificate-authority-data}' | base64 --decode)
KUBE_HOST=$(kubectl config view --raw --minify --flatten -o jsonpath='{.clusters[].cluster.server}')
# Kubernetes auth method konfigüre et
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault write auth/kubernetes/config \
token_reviewer_jwt="$TOKEN_REVIEW_JWT" \
kubernetes_host="$KUBE_HOST" \
kubernetes_ca_cert="$KUBE_CA_CERT"
# 8.2.3 Vault policy ve role oluştur
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault policy write mycompany-dev - <<EOF
path "secret/data/dev/*" {
capabilities = ["read"]
}
EOF
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault write auth/kubernetes/role/mycompany-dev \
bound_service_account_names=external-secrets \
bound_service_account_namespaces=dev \
policies=mycompany-dev \
ttl=24h
# 8.2.4 Vault'ta secret engine aktifleştir
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault secrets enable -path=secret kv-v2
# Test secrets ekle
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault kv put secret/dev/database \
username=myapp \
password=<DB_PASSWORD>
kubectl exec vault-0 -n vault -- env VAULT_TOKEN=$CLUSTER_ROOT_TOKEN vault kv put secret/dev/api-keys \
github-token=ghp_xxxxxxxxxxxx \
slack-webhook=https://hooks.slack.com/services/xxx
# 8.2.5 SecretStore oluştur
cat > vault-secret-store.yaml << 'EOF'
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
name: vault-backend
namespace: dev
spec:
provider:
vault:
server: "https://vault.vault.svc.cluster.local:8200"
path: "secret"
version: "v2"
caBundle: "LS0tLS1CRUdJTi..." # Base64 encoded CA cert
auth:
kubernetes:
mountPath: "kubernetes"
role: "mycompany-dev"
serviceAccountRef:
name: "external-secrets"
EOF
# CA cert'i base64 encode et
CA_BUNDLE=$(cat vault-tls/vault-ca.crt | base64 -w 0)
sed -i "s/LS0tLS1CRUdJTi.../$CA_BUNDLE/g" vault-secret-store.yaml
kubectl apply -f vault-secret-store.yaml
# 8.2.6 ExternalSecret oluştur
cat > external-secret-database.yaml << 'EOF'
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: database-credentials
namespace: dev
spec:
refreshInterval: 1m
secretStoreRef:
name: vault-backend
kind: SecretStore
target:
name: database-secret
creationPolicy: Owner
data:
- secretKey: username
remoteRef:
key: secret/dev/database
property: username
- secretKey: password
remoteRef:
key: secret/dev/database
property: password
EOF
kubectl apply -f external-secret-database.yaml
# Secret'in oluştuğunu kontrol et
kubectl get secrets -n dev
kubectl describe externalsecret database-credentials -n dev
🛡️ 8.3 Pod Security Standards
# 8.3.1 Pod Security Standards uygula
kubectl label --overwrite namespace dev pod-security.kubernetes.io/enforce=restricted
kubectl label --overwrite namespace dev pod-security.kubernetes.io/audit=restricted
kubectl label --overwrite namespace dev pod-security.kubernetes.io/warn=restricted
kubectl label --overwrite namespace staging pod-security.kubernetes.io/enforce=restricted
kubectl label --overwrite namespace staging pod-security.kubernetes.io/audit=restricted
kubectl label --overwrite namespace staging pod-security.kubernetes.io/warn=restricted
kubectl label --overwrite namespace production pod-security.kubernetes.io/enforce=restricted
kubectl label --overwrite namespace production pod-security.kubernetes.io/audit=restricted
kubectl label --overwrite namespace production pod-security.kubernetes.io/warn=restricted
# 8.3.2 Security context template
cat > security-context-template.yaml << 'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
name: secure-app
namespace: dev
spec:
replicas: 1
selector:
matchLabels:
app: secure-app
template:
metadata:
labels:
app: secure-app
spec:
securityContext:
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:alpine
ports:
- containerPort: 8080
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
resources:
requests:
memory: "128Mi"
cpu: "100m"
limits:
memory: "256Mi"
cpu: "200m"
volumeMounts:
- name: tmp
mountPath: /tmp
- name: var-cache-nginx
mountPath: /var/cache/nginx
- name: var-run
mountPath: /var/run
volumes:
- name: tmp
emptyDir: {}
- name: var-cache-nginx
emptyDir: {}
- name: var-run
emptyDir: {}
EOF
🔍 8.4 Falco Runtime Security
# 8.4.1 Falco kurulumu
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm repo update
cat > falco-values.yaml << 'EOF'
falco:
rules_file:
- /etc/falco/falco_rules.yaml
- /etc/falco/falco_rules.local.yaml
- /etc/falco/k8s_audit_rules.yaml
- /etc/falco/rules.d
time_format_iso_8601: true
json_output: true
json_include_output_property: true
json_include_tags_property: true
log_stderr: true
log_syslog: true
log_level: info
priority: debug
buffered_outputs: false
syscall_event_drops:
actions:
- log
- alert
rate: 0.03333
max_burst: 1000
outputs:
rate: 1
max_burst: 1000
syslog_output:
enabled: true
file_output:
enabled: false
stdout_output:
enabled: true
webserver:
enabled: true
listen_port: 8765
k8s_healthz_endpoint: /healthz
ssl_enabled: false
ssl_certificate: /etc/ssl/falco/falco.pem
grpc:
enabled: false
grpc_output:
enabled: false
customRules:
custom-rules.yaml: |-
- rule: Unexpected outbound connection destination
desc: Detect outbound connections to unexpected destinations
condition: >
outbound and not
(fd.sip in (internal_networks))
output: Outbound connection to unexpected destination (command=%proc.cmdline dest=%fd.rip)
priority: WARNING
tags: [network, mitre_exfiltration]
- rule: Suspicious process in container
desc: Detect suspicious processes running in containers
condition: >
spawned_process and container and
(proc.name in (nc, ncat, netcat, nmap, dig, nslookup, tcpdump))
output: Suspicious process in container (command=%proc.cmdline container=%container.name)
priority: WARNING
tags: [process, container]
driver:
enabled: true
kind: ebpf
collectors:
enabled: true
docker:
enabled: true
containerd:
enabled: true
crio:
enabled: false
resources:
requests:
cpu: 100m
memory: 512Mi
limits:
cpu: 200m
memory: 1024Mi
tolerations:
- effect: NoSchedule
key: node-role.kubernetes.io/master
- effect: NoSchedule
key: node-role.kubernetes.io/control-plane
falcosidekick:
enabled: true
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 200m
memory: 256Mi
config:
slack:
webhookurl: "YOUR_SLACK_WEBHOOK_URL"
channel: "#security-alerts"
username: "Falco"
minimumpriority: "warning"
messageformat: "long"
alertmanager:
hostport: "http://kube-prometheus-stack-alertmanager.monitoring.svc.cluster.local:9093"
minimumpriority: "warning"
EOF
kubectl create namespace falco
helm install falco falcosecurity/falco \
--namespace falco \
--values falco-values.yaml
# 8.4.2 Falco durumunu kontrol et
kubectl get pods -n falco
kubectl logs -l app.kubernetes.io/name=falco -n falco