Chapter 9: Argo Security and Permission Management

Deep dive into security configuration, RBAC permission management, SSO integration, and security best practices for the Argo ecosystem

作者
28min

Argo Security and Permission Management

Chapter 9: Securing the Argo Ecosystem

This chapter will detail how to secure the Argo ecosystem, including authentication, authorization, secret management, and security best practices.

9.1 Security Architecture Overview

9.1.1 Argo Security Layers

🔄 正在渲染 Mermaid 图表...

9.1.2 Security Component Relations

🔄 正在渲染 Mermaid 图表...

9.2 Argo CD Security Configuration

9.2.1 RBAC Configuration

# argocd-rbac-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-rbac-cm
  namespace: argo
data:
  # Policy definition
  policy.csv: |
    # Role definition
    # p, <role/user/group>, <resource>, <action>, <project>/<object>

    # Admin role - full access
    p, role:admin, applications, *, */*, allow
    p, role:admin, clusters, *, *, allow
    p, role:admin, repositories, *, *, allow
    p, role:admin, projects, *, *, allow
    p, role:admin, accounts, *, *, allow
    p, role:admin, certificates, *, *, allow
    p, role:admin, gpgkeys, *, *, allow
    p, role:admin, logs, *, */*, allow
    p, role:admin, exec, *, */*, allow

    # Developer role - read + sync permission
    p, role:developer, applications, get, */*, allow
    p, role:developer, applications, sync, */*, allow
    p, role:developer, applications, action/*, */*, allow
    p, role:developer, logs, get, */*, allow
    p, role:developer, repositories, get, *, allow
    p, role:developer, projects, get, *, allow

    # Read-only role
    p, role:readonly, applications, get, */*, allow
    p, role:readonly, projects, get, *, allow
    p, role:readonly, repositories, get, *, allow
    p, role:readonly, clusters, get, *, allow
    p, role:readonly, logs, get, */*, allow

    # Production admin - manage production project only
    p, role:prod-admin, applications, *, production/*, allow
    p, role:prod-admin, logs, get, production/*, allow
    p, role:prod-admin, exec, create, production/*, allow

    # Group mapping
    g, admin-team, role:admin
    g, dev-team, role:developer
    g, ops-team, role:prod-admin
    g, viewer-team, role:readonly

  # Default policy
  policy.default: role:readonly

  # Match mode (glob or regex)
  policy.matchMode: glob

  # Scopes
  scopes: '[groups, email]'

9.2.2 SSO/OIDC Configuration

# argocd-cm.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argo
data:
  # OIDC configuration (using Keycloak as example)
  oidc.config: |
    name: Keycloak
    issuer: https://keycloak.example.com/realms/argo
    clientID: argocd
    clientSecret: $oidc.keycloak.clientSecret
    requestedScopes: ["openid", "profile", "email", "groups"]
    requestedIDTokenClaims:
      groups:
        essential: true

  # Dex configuration (multiple authentication sources)
  dex.config: |
    connectors:
    # GitHub
    - type: github
      id: github
      name: GitHub
      config:
        clientID: $dex.github.clientID
        clientSecret: $dex.github.clientSecret
        orgs:
        - name: myorg
          teams:
          - admin-team
          - dev-team

    # LDAP
    - type: ldap
      id: ldap
      name: LDAP
      config:
        host: ldap.example.com:636
        insecureNoSSL: false
        insecureSkipVerify: false
        rootCAData: $dex.ldap.rootCA
        bindDN: cn=admin,dc=example,dc=com
        bindPW: $dex.ldap.bindPW
        userSearch:
          baseDN: ou=users,dc=example,dc=com
          filter: "(objectClass=person)"
          username: uid
          idAttr: uid
          emailAttr: mail
          nameAttr: cn
        groupSearch:
          baseDN: ou=groups,dc=example,dc=com
          filter: "(objectClass=groupOfNames)"
          userMatchers:
          - userAttr: DN
            groupAttr: member
          nameAttr: cn

    # SAML
    - type: saml
      id: okta
      name: Okta
      config:
        ssoURL: https://mycompany.okta.com/app/xxx/sso/saml
        caData: $dex.okta.caData
        redirectURI: https://argocd.example.com/api/dex/callback
        usernameAttr: email
        emailAttr: email
        groupsAttr: groups

  # URL configuration
  url: https://argocd.example.com

  # Admin user
  admin.enabled: "false"
---
# Secret configuration
apiVersion: v1
kind: Secret
metadata:
  name: argocd-secret
  namespace: argo
type: Opaque
stringData:
  # OIDC client secret
  oidc.keycloak.clientSecret: "your-client-secret"
  # Dex secrets
  dex.github.clientID: "your-github-client-id"
  dex.github.clientSecret: "your-github-client-secret"
  dex.ldap.bindPW: "ldap-bind-password"

9.2.3 Project Permission Isolation

# argocd-project.yaml
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: production
  namespace: argo
spec:
  description: "Production environment project"

  # Allowed source repositories
  sourceRepos:
  - https://github.com/myorg/gitops-config.git
  - https://github.com/myorg/helm-charts.git

  # Allowed destination clusters and namespaces
  destinations:
  - namespace: production
    server: https://kubernetes.default.svc
  - namespace: production-*
    server: https://kubernetes.default.svc

  # Allowed Kubernetes resource types
  clusterResourceWhitelist:
  - group: ''
    kind: Namespace
  - group: 'rbac.authorization.k8s.io'
    kind: ClusterRole
  - group: 'rbac.authorization.k8s.io'
    kind: ClusterRoleBinding

  # Denied namespace resources
  namespaceResourceBlacklist:
  - group: ''
    kind: ResourceQuota
  - group: ''
    kind: LimitRange
  - group: ''
    kind: NetworkPolicy

  # Allowed namespace resources
  namespaceResourceWhitelist:
  - group: ''
    kind: '*'
  - group: 'apps'
    kind: '*'
  - group: 'argoproj.io'
    kind: '*'

  # Role definitions
  roles:
  - name: prod-developer
    description: "Production environment developer"
    policies:
    - p, proj:production:prod-developer, applications, get, production/*, allow
    - p, proj:production:prod-developer, applications, sync, production/*, allow
    - p, proj:production:prod-developer, logs, get, production/*, allow
    groups:
    - prod-dev-team

  - name: prod-admin
    description: "Production environment admin"
    policies:
    - p, proj:production:prod-admin, applications, *, production/*, allow
    - p, proj:production:prod-admin, logs, get, production/*, allow
    - p, proj:production:prod-admin, exec, create, production/*, allow
    groups:
    - prod-admin-team

  # Sync windows (maintenance windows)
  syncWindows:
  - kind: allow
    schedule: '0 9-18 * * 1-5'  # Weekdays 9:00-18:00
    duration: 9h
    applications:
    - '*'
    manualSync: true
  - kind: deny
    schedule: '0 0 * * 0'  # Sunday all day
    duration: 24h
    applications:
    - '*'

  # Signature keys (for verifying Git commit signatures)
  signatureKeys:
  - keyID: 1234567890ABCDEF

  # Orphaned resources monitoring
  orphanedResources:
    warn: true
    ignore:
    - group: ''
      kind: ConfigMap
      name: kube-root-ca.crt
---
# Development environment project
apiVersion: argoproj.io/v1alpha1
kind: AppProject
metadata:
  name: development
  namespace: argo
spec:
  description: "Development environment project"

  sourceRepos:
  - '*'  # Development environment allows all repositories

  destinations:
  - namespace: dev-*
    server: https://kubernetes.default.svc
  - namespace: development
    server: https://kubernetes.default.svc

  # Development environment restricts certain dangerous resources
  namespaceResourceBlacklist:
  - group: ''
    kind: Secret
    name: '*-production-*'

  roles:
  - name: developer
    description: "Developer"
    policies:
    - p, proj:development:developer, applications, *, development/*, allow
    groups:
    - developers

9.2.4 TLS Configuration

# argocd-tls.yaml
apiVersion: v1
kind: Secret
metadata:
  name: argocd-server-tls
  namespace: argo
type: kubernetes.io/tls
data:
  tls.crt: <base64-encoded-cert>
  tls.key: <base64-encoded-key>
---
# Ingress with TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server
  namespace: argo
  annotations:
    nginx.ingress.kubernetes.io/ssl-passthrough: "true"
    nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
  tls:
  - hosts:
    - argocd.example.com
    secretName: argocd-server-tls
  rules:
  - host: argocd.example.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: argocd-server
            port:
              number: 443

9.3 Argo Workflows Security Configuration

9.3.1 ServiceAccount Permissions

# workflow-rbac.yaml
# Minimal permission ServiceAccount
apiVersion: v1
kind: ServiceAccount
metadata:
  name: workflow-minimal
  namespace: argo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: workflow-minimal-role
  namespace: argo
rules:
# Only allow Pod operations
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
# Allow reading Pod logs
- apiGroups: [""]
  resources: ["pods/log"]
  verbs: ["get"]
# Allow reading ConfigMaps
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: workflow-minimal-binding
  namespace: argo
subjects:
- kind: ServiceAccount
  name: workflow-minimal
  namespace: argo
roleRef:
  kind: Role
  name: workflow-minimal-role
  apiGroup: rbac.authorization.k8s.io
---
# Builder-specific ServiceAccount (needs more permissions)
apiVersion: v1
kind: ServiceAccount
metadata:
  name: workflow-builder
  namespace: argo
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: workflow-builder-role
  namespace: argo
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "pods/exec"]
  verbs: ["*"]
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get"]
  resourceNames: ["docker-credentials", "git-credentials"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "create", "update"]
- apiGroups: [""]
  resources: ["persistentvolumeclaims"]
  verbs: ["get", "create", "delete"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: workflow-builder-binding
  namespace: argo
subjects:
- kind: ServiceAccount
  name: workflow-builder
  namespace: argo
roleRef:
  kind: Role
  name: workflow-builder-role
  apiGroup: rbac.authorization.k8s.io

9.3.2 Workflow Security Policies

# workflow-controller-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: workflow-controller-configmap
  namespace: argo
data:
  config: |
    # Executor configuration
    containerRuntimeExecutor: emissary

    # Security context
    securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      fsGroup: 1000

    # Pod security context
    podSecurityContext:
      runAsNonRoot: true
      seccompProfile:
        type: RuntimeDefault

    # Resource rate limit
    resourceRateLimit:
      limit: 100
      burst: 200

    # Parallelism limit
    parallelism: 50

    # Namespace parallelism
    namespaceParallelism: 10

    # Workflow defaults
    workflowDefaults:
      spec:
        # Default ServiceAccount
        serviceAccountName: workflow-minimal
        # Timeout settings
        activeDeadlineSeconds: 3600
        # TTL strategy
        ttlStrategy:
          secondsAfterCompletion: 86400
          secondsAfterSuccess: 3600
          secondsAfterFailure: 172800
        # Pod GC strategy
        podGC:
          strategy: OnPodCompletion
        # Security context
        securityContext:
          runAsNonRoot: true
          runAsUser: 1000

    # Archive configuration
    persistence:
      archive: true
      archiveTTL: 30d

    # Metrics configuration
    metricsConfig:
      enabled: true
      path: /metrics
      port: 9090

9.3.3 SSO Configuration

# argo-server-sso.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: workflow-controller-configmap
  namespace: argo
data:
  sso: |
    issuer: https://keycloak.example.com/realms/argo
    clientId:
      name: argo-workflows-sso
      key: client-id
    clientSecret:
      name: argo-workflows-sso
      key: client-secret
    redirectUrl: https://argo-workflows.example.com/oauth2/callback
    scopes:
    - openid
    - profile
    - email
    - groups
    rbac:
      enabled: true
    insecureSkipVerify: false
---
apiVersion: v1
kind: Secret
metadata:
  name: argo-workflows-sso
  namespace: argo
type: Opaque
stringData:
  client-id: "argo-workflows"
  client-secret: "your-client-secret"

9.3.4 Network Policy

# workflow-network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: workflow-pods
  namespace: argo
spec:
  podSelector:
    matchLabels:
      workflows.argoproj.io/workflow: ""
  policyTypes:
  - Ingress
  - Egress
  ingress:
  # Only allow traffic from workflow-controller
  - from:
    - podSelector:
        matchLabels:
          app: workflow-controller
  egress:
  # Allow DNS queries
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53
  # Allow access to internal services
  - to:
    - namespaceSelector:
        matchLabels:
          name: internal-services
  # Allow access to specific external addresses
  - to:
    - ipBlock:
        cidr: 10.0.0.0/8
    ports:
    - protocol: TCP
      port: 443

9.4 Secret Management

9.4.1 Sealed Secrets

# sealed-secrets installation and usage
# Install Sealed Secrets Controller
# kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml

# Create SealedSecret
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  name: my-secret
  namespace: argo
spec:
  encryptedData:
    password: AgBY2L...  # Encrypted password
    api-key: AgCX3M...   # Encrypted API key
  template:
    metadata:
      name: my-secret
      namespace: argo
    type: Opaque
# Using kubeseal to encrypt secrets
# Install kubeseal
brew install kubeseal

# Create regular Secret
kubectl create secret generic my-secret \
  --dry-run=client \
  --from-literal=password=mysecretpassword \
  -o yaml > secret.yaml

# Encrypt Secret
kubeseal --format=yaml < secret.yaml > sealed-secret.yaml

# Apply SealedSecret
kubectl apply -f sealed-secret.yaml

9.4.2 External Secrets Operator

# external-secrets configuration
# Install External Secrets Operator
# helm install external-secrets external-secrets/external-secrets -n external-secrets --create-namespace

# SecretStore - HashiCorp Vault
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: vault-backend
  namespace: argo
spec:
  provider:
    vault:
      server: "https://vault.example.com"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "argo-role"
          serviceAccountRef:
            name: "argo-vault-sa"
---
# SecretStore - AWS Secrets Manager
apiVersion: external-secrets.io/v1beta1
kind: SecretStore
metadata:
  name: aws-secrets
  namespace: argo
spec:
  provider:
    aws:
      service: SecretsManager
      region: us-east-1
      auth:
        jwt:
          serviceAccountRef:
            name: aws-secrets-sa
---
# ExternalSecret
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: database-credentials
  namespace: argo
spec:
  refreshInterval: 1h
  secretStoreRef:
    kind: SecretStore
    name: vault-backend
  target:
    name: database-credentials
    creationPolicy: Owner
  data:
  - secretKey: username
    remoteRef:
      key: database/credentials
      property: username
  - secretKey: password
    remoteRef:
      key: database/credentials
      property: password

9.4.3 Argo CD Secret Management

# argocd-secrets-plugin.yaml
# Using Argo CD Vault Plugin
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-cm
  namespace: argo
data:
  configManagementPlugins: |
    - name: argocd-vault-plugin
      generate:
        command: ["argocd-vault-plugin"]
        args: ["generate", "./"]
    - name: argocd-vault-plugin-helm
      init:
        command: ["/bin/sh", "-c"]
        args: ["helm dependency build"]
      generate:
        command: ["/bin/sh", "-c"]
        args: ["helm template $ARGOCD_APP_NAME . | argocd-vault-plugin generate -"]
---
# Application using Vault Plugin
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
  namespace: argo
spec:
  source:
    repoURL: https://github.com/myorg/my-app.git
    path: k8s
    plugin:
      name: argocd-vault-plugin
  destination:
    server: https://kubernetes.default.svc
    namespace: production

9.4.4 Secret Rotation

# secret-rotation-workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: secret-rotation
  namespace: argo
spec:
  schedule: "0 0 1 * *"  # Run on 1st of each month
  workflowSpec:
    entrypoint: rotate-secrets
    serviceAccountName: secret-rotator
    templates:
    - name: rotate-secrets
      dag:
        tasks:
        - name: rotate-db-password
          template: rotate-password
          arguments:
            parameters:
            - name: secret-name
              value: database-credentials
            - name: key
              value: password
        - name: rotate-api-keys
          template: rotate-api-key
          arguments:
            parameters:
            - name: secret-name
              value: api-credentials

    - name: rotate-password
      inputs:
        parameters:
        - name: secret-name
        - name: key
      script:
        image: bitnami/kubectl:latest
        command: ["/bin/bash"]
        source: |
          # Generate new password
          NEW_PASSWORD=$(openssl rand -base64 32)

          # Update database password (actual database update logic needed here)
          # mysql -u admin -p$OLD_PASSWORD -e "ALTER USER 'app'@'%' IDENTIFIED BY '$NEW_PASSWORD'"

          # Update Kubernetes Secret
          kubectl patch secret {{inputs.parameters.secret-name}} \
            -p "{\"stringData\":{\"{{inputs.parameters.key}}\":\"$NEW_PASSWORD\"}}"

          # Restart related application to get new secret
          kubectl rollout restart deployment/myapp

    - name: rotate-api-key
      inputs:
        parameters:
        - name: secret-name
      container:
        image: curlimages/curl:latest
        command: ["/bin/sh", "-c"]
        args:
        - |
          # Call API to generate new key
          NEW_KEY=$(curl -X POST https://api.example.com/rotate-key -H "Authorization: Bearer $OLD_KEY")

          # Update Secret
          kubectl patch secret {{inputs.parameters.secret-name}} \
            -p "{\"stringData\":{\"api-key\":\"$NEW_KEY\"}}"

9.5 Audit and Compliance

9.5.1 Audit Log Configuration

# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
# Record all Argo resource operations
- level: RequestResponse
  resources:
  - group: argoproj.io
    resources: ["*"]
  verbs: ["create", "update", "patch", "delete"]

# Record Secret access
- level: Metadata
  resources:
  - group: ""
    resources: ["secrets"]
  verbs: ["get", "list", "watch"]

# Record RBAC changes
- level: RequestResponse
  resources:
  - group: "rbac.authorization.k8s.io"
    resources: ["*"]
  verbs: ["*"]

# Ignore health checks
- level: None
  users: ["system:kube-proxy"]
  verbs: ["watch"]
  resources:
  - group: ""
    resources: ["endpoints", "services", "services/status"]

9.5.2 Argo CD Audit

# argocd-notifications audit
apiVersion: v1
kind: ConfigMap
metadata:
  name: argocd-notifications-cm
  namespace: argo
data:
  # Trigger - record all sync operations
  trigger.on-sync-succeeded: |
    - description: Application syncing has succeeded
      send:
      - audit-log
      when: app.status.operationState.phase in ['Succeeded']

  trigger.on-sync-failed: |
    - description: Application syncing has failed
      send:
      - audit-log
      when: app.status.operationState.phase in ['Error', 'Failed']

  trigger.on-health-degraded: |
    - description: Application has degraded
      send:
      - audit-log
      when: app.status.health.status == 'Degraded'

  # Template - audit log format
  template.audit-log: |
    webhook:
      audit:
        method: POST
        path: /audit
        body: |
          {
            "timestamp": "{{.app.status.operationState.finishedAt}}",
            "application": "{{.app.metadata.name}}",
            "project": "{{.app.spec.project}}",
            "action": "sync",
            "status": "{{.app.status.operationState.phase}}",
            "revision": "{{.app.status.sync.revision}}",
            "initiatedBy": "{{.app.status.operationState.operation.initiatedBy.username}}",
            "message": "{{.app.status.operationState.message}}"
          }

  # Service - audit log collection
  service.webhook.audit: |
    url: https://audit.example.com
    headers:
    - name: Authorization
      value: Bearer $audit-token

9.5.3 Compliance Check Workflow

# compliance-check-workflow.yaml
apiVersion: argoproj.io/v1alpha1
kind: CronWorkflow
metadata:
  name: compliance-check
  namespace: argo
spec:
  schedule: "0 6 * * *"  # Daily at 6 AM
  workflowSpec:
    entrypoint: compliance-pipeline
    templates:
    - name: compliance-pipeline
      dag:
        tasks:
        - name: check-rbac
          template: rbac-audit
        - name: check-secrets
          template: secret-audit
        - name: check-network-policies
          template: network-audit
        - name: generate-report
          template: generate-report
          dependencies: [check-rbac, check-secrets, check-network-policies]

    - name: rbac-audit
      container:
        image: bitnami/kubectl:latest
        command: ["/bin/sh", "-c"]
        args:
        - |
          echo "=== RBAC Audit ===" > /tmp/rbac-report.txt

          # Check overly permissive ClusterRoleBindings
          echo "Checking for overly permissive ClusterRoleBindings..."
          kubectl get clusterrolebindings -o json | jq -r '
            .items[] |
            select(.roleRef.name == "cluster-admin") |
            "WARNING: \(.metadata.name) has cluster-admin role"
          ' >> /tmp/rbac-report.txt

          # Check ServiceAccount permissions
          echo "Checking ServiceAccount permissions..."
          kubectl get rolebindings,clusterrolebindings -A -o json | jq -r '
            .items[] |
            select(.subjects[]?.kind == "ServiceAccount") |
            "\(.metadata.namespace)/\(.metadata.name): \(.roleRef.name)"
          ' >> /tmp/rbac-report.txt

          cat /tmp/rbac-report.txt
      outputs:
        artifacts:
        - name: rbac-report
          path: /tmp/rbac-report.txt

    - name: secret-audit
      container:
        image: bitnami/kubectl:latest
        command: ["/bin/sh", "-c"]
        args:
        - |
          echo "=== Secret Audit ===" > /tmp/secret-report.txt

          # Check for unencrypted Secrets
          echo "Checking for potential plaintext secrets..."
          kubectl get secrets -A -o json | jq -r '
            .items[] |
            select(.type == "Opaque") |
            select(.data | to_entries | any(.value | @base64d | test("password|secret|key|token"; "i"))) |
            "WARNING: \(.metadata.namespace)/\(.metadata.name) may contain sensitive data"
          ' >> /tmp/secret-report.txt

          # Check certificate expiration
          echo "Checking certificate expiration..."
          kubectl get secrets -A -o json | jq -r '
            .items[] |
            select(.type == "kubernetes.io/tls") |
            "\(.metadata.namespace)/\(.metadata.name)"
          ' >> /tmp/secret-report.txt

          cat /tmp/secret-report.txt
      outputs:
        artifacts:
        - name: secret-report
          path: /tmp/secret-report.txt

    - name: network-audit
      container:
        image: bitnami/kubectl:latest
        command: ["/bin/sh", "-c"]
        args:
        - |
          echo "=== Network Policy Audit ===" > /tmp/network-report.txt

          # Check namespaces without NetworkPolicy
          echo "Namespaces without NetworkPolicy..."
          for ns in $(kubectl get ns -o jsonpath='{.items[*].metadata.name}'); do
            if [ $(kubectl get networkpolicies -n $ns 2>/dev/null | wc -l) -eq 0 ]; then
              echo "WARNING: Namespace $ns has no NetworkPolicy" >> /tmp/network-report.txt
            fi
          done

          cat /tmp/network-report.txt
      outputs:
        artifacts:
        - name: network-report
          path: /tmp/network-report.txt

    - name: generate-report
      inputs:
        artifacts:
        - name: rbac-report
          path: /tmp/rbac-report.txt
        - name: secret-report
          path: /tmp/secret-report.txt
        - name: network-report
          path: /tmp/network-report.txt
      container:
        image: curlimages/curl:latest
        command: ["/bin/sh", "-c"]
        args:
        - |
          # Merge reports
          cat /tmp/*.txt > /tmp/full-report.txt

          # Send to Slack
          curl -X POST $SLACK_WEBHOOK \
            -H "Content-Type: application/json" \
            -d "{\"text\": \"Daily Compliance Report\", \"attachments\": [{\"text\": \"$(cat /tmp/full-report.txt | head -100)\"}]}"

9.6 Chapter Summary

This chapter detailed security configuration for the Argo ecosystem:

🔄 正在渲染 Mermaid 图表...

Key Points:

  1. Authentication: Use SSO/OIDC for unified user authentication
  2. Authorization: Implement fine-grained permission control through RBAC and Projects
  3. Secret Management: Use Sealed Secrets or External Secrets for secure secret management
  4. Network Security: Configure TLS and NetworkPolicy to protect communication
  5. Audit & Compliance: Establish audit logging and compliance checking mechanisms

In the next chapter, we will learn about Argo production environment best practices.