UiPath Documentation
automation-suite
2023.10
false
Automation Suite on Linux installation guide

Manual ArgoCD NetworkPolicy mitigation (GHSA-47m3-95c7-g2g8)

Solutions for unauthenticated network access to the ArgoCD repo-server and Redis in Automation Suite, with a manual NetworkPolicy mitigation procedure.

Description​

A vulnerability affecting the ArgoCD repo-server component was recently published and is not fixed by ArgoCD at this moment.

The ArgoCD repo-server (port 8081) and ArgoCD Redis are reachable from any pod in the cluster network, because no NetworkPolicy restricts ingress to these components by default. As a result, any pod within the cluster network can connect to them directly, since ArgoCD does not require authentication for these internal components. For details, refer to GHSA-47m3-95c7-g2g8.

To mitigate the issue, you must manually apply the NetworkPolicy described in the Solution section on this page.

Prerequisites​

  • Run this procedure from a host with kubectl access to the cluster.
  • The procedure assumes the default Automation Suite argocd namespace. If ArgoCD uses a different namespace, set ARGOCD_NS before running the script.
  • This mitigation relies on Kubernetes NetworkPolicy enforcement. Automation Suite supported clusters use a compatible CNI (Container Network Interface) configuration.

Solution​

  1. Run the following script. It detects your ArgoCD release and Redis mode (standalone or high availability, HA), and applies the matching NetworkPolicy automatically:

    export ARGOCD_NS="${ARGOCD_NS:-argocd}"
    export ARGOCD_RELEASE="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].metadata.labels.app\.kubernetes\.io/instance}')"
    
    test -n "$ARGOCD_RELEASE" || { echo "Cannot detect Argo CD Helm release"; exit 1; }
    
    if kubectl -n "$ARGOCD_NS" get pod -l "release=${ARGOCD_RELEASE},app=redis-ha-haproxy" --no-headers 2>/dev/null | grep -q .; then
      REDIS_MODE=ha
    else
      REDIS_MODE=standalone
    fi
    
    echo "Argo CD namespace: $ARGOCD_NS"
    echo "Argo CD release: $ARGOCD_RELEASE"
    echo "Redis mode: $REDIS_MODE"
    
    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-repo-server-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: argocd-repo-server
          app.kubernetes.io/instance: ${ARGOCD_RELEASE}
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-application-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-applicationset-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-notifications-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
          ports:
            - protocol: TCP
              port: 8081
        - from:
            - namespaceSelector: {}
          ports:
            - protocol: TCP
              port: 8084
    EOF
    
    if [ "$REDIS_MODE" = "ha" ]; then
      cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-redis-ha-server-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          release: ${ARGOCD_RELEASE}
          app: redis-ha
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  release: ${ARGOCD_RELEASE}
                  app: redis-ha
            - podSelector:
                matchLabels:
                  release: ${ARGOCD_RELEASE}
                  app: redis-ha-haproxy
          ports:
            - protocol: TCP
              port: 6379
            - protocol: TCP
              port: 26379
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-redis-ha-haproxy-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          release: ${ARGOCD_RELEASE}
          app: redis-ha-haproxy
          app.kubernetes.io/name: argocd-redis-ha-haproxy
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-repo-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-application-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
          ports:
            - protocol: TCP
              port: 6379
    EOF
    else
      cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-redis-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: argocd-redis
          app.kubernetes.io/instance: ${ARGOCD_RELEASE}
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-repo-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-application-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
          ports:
            - protocol: TCP
              port: 6379
        - from:
            - namespaceSelector: {}
          ports:
            - protocol: TCP
              port: 9121
    EOF
    fi
    export ARGOCD_NS="${ARGOCD_NS:-argocd}"
    export ARGOCD_RELEASE="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].metadata.labels.app\.kubernetes\.io/instance}')"
    
    test -n "$ARGOCD_RELEASE" || { echo "Cannot detect Argo CD Helm release"; exit 1; }
    
    if kubectl -n "$ARGOCD_NS" get pod -l "release=${ARGOCD_RELEASE},app=redis-ha-haproxy" --no-headers 2>/dev/null | grep -q .; then
      REDIS_MODE=ha
    else
      REDIS_MODE=standalone
    fi
    
    echo "Argo CD namespace: $ARGOCD_NS"
    echo "Argo CD release: $ARGOCD_RELEASE"
    echo "Redis mode: $REDIS_MODE"
    
    cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-repo-server-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: argocd-repo-server
          app.kubernetes.io/instance: ${ARGOCD_RELEASE}
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-application-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-applicationset-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-notifications-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
          ports:
            - protocol: TCP
              port: 8081
        - from:
            - namespaceSelector: {}
          ports:
            - protocol: TCP
              port: 8084
    EOF
    
    if [ "$REDIS_MODE" = "ha" ]; then
      cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-redis-ha-server-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          release: ${ARGOCD_RELEASE}
          app: redis-ha
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  release: ${ARGOCD_RELEASE}
                  app: redis-ha
            - podSelector:
                matchLabels:
                  release: ${ARGOCD_RELEASE}
                  app: redis-ha-haproxy
          ports:
            - protocol: TCP
              port: 6379
            - protocol: TCP
              port: 26379
    ---
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-redis-ha-haproxy-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          release: ${ARGOCD_RELEASE}
          app: redis-ha-haproxy
          app.kubernetes.io/name: argocd-redis-ha-haproxy
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-repo-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-application-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
          ports:
            - protocol: TCP
              port: 6379
    EOF
    else
      cat <<EOF | kubectl apply -f -
    apiVersion: networking.k8s.io/v1
    kind: NetworkPolicy
    metadata:
      name: argocd-redis-manual-mitigation
      namespace: ${ARGOCD_NS}
    spec:
      podSelector:
        matchLabels:
          app.kubernetes.io/name: argocd-redis
          app.kubernetes.io/instance: ${ARGOCD_RELEASE}
      policyTypes:
        - Ingress
      ingress:
        - from:
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-repo-server
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
            - podSelector:
                matchLabels:
                  app.kubernetes.io/name: argocd-application-controller
                  app.kubernetes.io/instance: ${ARGOCD_RELEASE}
          ports:
            - protocol: TCP
              port: 6379
        - from:
            - namespaceSelector: {}
          ports:
            - protocol: TCP
              port: 9121
    EOF
    fi
    

Verify​

Run the following commands to confirm the mitigation was applied successfully:

kubectl -n "$ARGOCD_NS" get networkpolicy
kubectl -n "$ARGOCD_NS" get pods
kubectl get applications.argoproj.io -A
kubectl -n "$ARGOCD_NS" get networkpolicy
kubectl -n "$ARGOCD_NS" get pods
kubectl get applications.argoproj.io -A

Confirm the following:

  • argocd-repo-server-manual-mitigation exists.
  • For Redis HA, argocd-redis-ha-server-manual-mitigation and argocd-redis-ha-haproxy-manual-mitigation exist.
  • For standalone Redis, argocd-redis-manual-mitigation exists.
  • ArgoCD pods are Running or Completed.
  • ArgoCD Applications remain Synced and Healthy.

Confirm Mitigation​

Run these checks after applying the policies. They verify that required ArgoCD traffic still works and disallowed traffic is blocked.

  1. Run the following script:

    export ARGOCD_NS="${ARGOCD_NS:-argocd}"
    export ARGOCD_RELEASE="${ARGOCD_RELEASE:-$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].metadata.labels.app\.kubernetes\.io/instance}')}"
    
    if kubectl -n "$ARGOCD_NS" get pod -l "release=${ARGOCD_RELEASE},app=redis-ha-haproxy" --no-headers 2>/dev/null | grep -q .; then
      REDIS_MODE=ha
    else
      REDIS_MODE=standalone
    fi
    
    SERVER_POD="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].metadata.name}')"
    APPSET_POD="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-applicationset-controller -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)"
    FAILED_CHECKS=""
    
    tcp_check() {
      pod="$1"
      host="$2"
      port="$3"
      expected="$4"
      name="$5"
    
      code="$(kubectl -n "$ARGOCD_NS" exec "$pod" -- sh -c '
        host="$1"
        port="$2"
        if command -v nc >/dev/null 2>&1; then
          timeout 5 nc -z -w 3 "$host" "$port" >/dev/null 2>&1
          printf "%s\n" "$?"
        elif command -v bash >/dev/null 2>&1; then
          timeout 5 bash -c "</dev/tcp/${host}/${port}" >/dev/null 2>&1
          printf "%s\n" "$?"
        else
          printf "%s\n" "NO_TCP_TOOL"
        fi
      ' sh "$host" "$port" 2>/dev/null | tail -n 1)"
    
      if [ "$expected" = "allow" ] && [ "$code" = "0" ]; then
        return
      elif [ "$expected" = "deny" ] && [ "$code" != "0" ] && [ "$code" != "NO_TCP_TOOL" ] && [ -n "$code" ]; then
        return
      else
        FAILED_CHECKS="${FAILED_CHECKS}
    - ${name}: expected ${expected}, got exit code ${code}"
      fi
    }
    
    tcp_check "$SERVER_POD" argocd-repo-server 8081 allow "argocd-server to repo-server:8081"
    
    if [ "$REDIS_MODE" = "ha" ]; then
      HAPROXY_POD="$(kubectl -n "$ARGOCD_NS" get pod -l "release=${ARGOCD_RELEASE},app=redis-ha-haproxy" -o jsonpath='{.items[0].metadata.name}')"
    
      tcp_check "$SERVER_POD" argocd-redis-ha-haproxy 6379 allow "argocd-server to redis-ha-haproxy:6379"
      tcp_check "$HAPROXY_POD" argocd-redis-ha 6379 allow "redis-ha-haproxy to redis-ha:6379"
      tcp_check "$HAPROXY_POD" argocd-repo-server 8081 deny "redis-ha-haproxy to repo-server:8081"
    
      if [ -n "$APPSET_POD" ]; then
        tcp_check "$APPSET_POD" argocd-redis-ha-haproxy 6379 deny "applicationset-controller to redis-ha-haproxy:6379"
      fi
    else
      REDIS_POD="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-redis -o jsonpath='{.items[0].metadata.name}')"
    
      tcp_check "$SERVER_POD" argocd-redis 6379 allow "argocd-server to redis:6379"
      tcp_check "$REDIS_POD" argocd-repo-server 8081 deny "redis to repo-server:8081"
    
      if [ -n "$APPSET_POD" ]; then
        tcp_check "$APPSET_POD" argocd-redis 6379 deny "applicationset-controller to redis:6379"
      fi
    fi
    
    if [ -z "$FAILED_CHECKS" ]; then
      echo "MITIGATION CHECK: PASS"
    else
      echo "MITIGATION CHECK: FAIL"
      echo "$FAILED_CHECKS"
      exit 1
    fi
    export ARGOCD_NS="${ARGOCD_NS:-argocd}"
    export ARGOCD_RELEASE="${ARGOCD_RELEASE:-$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].metadata.labels.app\.kubernetes\.io/instance}')}"
    
    if kubectl -n "$ARGOCD_NS" get pod -l "release=${ARGOCD_RELEASE},app=redis-ha-haproxy" --no-headers 2>/dev/null | grep -q .; then
      REDIS_MODE=ha
    else
      REDIS_MODE=standalone
    fi
    
    SERVER_POD="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-server -o jsonpath='{.items[0].metadata.name}')"
    APPSET_POD="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-applicationset-controller -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)"
    FAILED_CHECKS=""
    
    tcp_check() {
      pod="$1"
      host="$2"
      port="$3"
      expected="$4"
      name="$5"
    
      code="$(kubectl -n "$ARGOCD_NS" exec "$pod" -- sh -c '
        host="$1"
        port="$2"
        if command -v nc >/dev/null 2>&1; then
          timeout 5 nc -z -w 3 "$host" "$port" >/dev/null 2>&1
          printf "%s\n" "$?"
        elif command -v bash >/dev/null 2>&1; then
          timeout 5 bash -c "</dev/tcp/${host}/${port}" >/dev/null 2>&1
          printf "%s\n" "$?"
        else
          printf "%s\n" "NO_TCP_TOOL"
        fi
      ' sh "$host" "$port" 2>/dev/null | tail -n 1)"
    
      if [ "$expected" = "allow" ] && [ "$code" = "0" ]; then
        return
      elif [ "$expected" = "deny" ] && [ "$code" != "0" ] && [ "$code" != "NO_TCP_TOOL" ] && [ -n "$code" ]; then
        return
      else
        FAILED_CHECKS="${FAILED_CHECKS}
    - ${name}: expected ${expected}, got exit code ${code}"
      fi
    }
    
    tcp_check "$SERVER_POD" argocd-repo-server 8081 allow "argocd-server to repo-server:8081"
    
    if [ "$REDIS_MODE" = "ha" ]; then
      HAPROXY_POD="$(kubectl -n "$ARGOCD_NS" get pod -l "release=${ARGOCD_RELEASE},app=redis-ha-haproxy" -o jsonpath='{.items[0].metadata.name}')"
    
      tcp_check "$SERVER_POD" argocd-redis-ha-haproxy 6379 allow "argocd-server to redis-ha-haproxy:6379"
      tcp_check "$HAPROXY_POD" argocd-redis-ha 6379 allow "redis-ha-haproxy to redis-ha:6379"
      tcp_check "$HAPROXY_POD" argocd-repo-server 8081 deny "redis-ha-haproxy to repo-server:8081"
    
      if [ -n "$APPSET_POD" ]; then
        tcp_check "$APPSET_POD" argocd-redis-ha-haproxy 6379 deny "applicationset-controller to redis-ha-haproxy:6379"
      fi
    else
      REDIS_POD="$(kubectl -n "$ARGOCD_NS" get pod -l app.kubernetes.io/name=argocd-redis -o jsonpath='{.items[0].metadata.name}')"
    
      tcp_check "$SERVER_POD" argocd-redis 6379 allow "argocd-server to redis:6379"
      tcp_check "$REDIS_POD" argocd-repo-server 8081 deny "redis to repo-server:8081"
    
      if [ -n "$APPSET_POD" ]; then
        tcp_check "$APPSET_POD" argocd-redis 6379 deny "applicationset-controller to redis:6379"
      fi
    fi
    
    if [ -z "$FAILED_CHECKS" ]; then
      echo "MITIGATION CHECK: PASS"
    else
      echo "MITIGATION CHECK: FAIL"
      echo "$FAILED_CHECKS"
      exit 1
    fi
    

    Expected result: MITIGATION CHECK: PASS

Rollback​

If required, take the following steps to remove the mitigation:

  1. Delete the manual mitigation policies:

    kubectl -n "$ARGOCD_NS" delete networkpolicy \
      argocd-repo-server-manual-mitigation \
      argocd-redis-manual-mitigation \
      argocd-redis-ha-server-manual-mitigation \
      argocd-redis-ha-haproxy-manual-mitigation \
      --ignore-not-found
    kubectl -n "$ARGOCD_NS" delete networkpolicy \
      argocd-repo-server-manual-mitigation \
      argocd-redis-manual-mitigation \
      argocd-redis-ha-server-manual-mitigation \
      argocd-redis-ha-haproxy-manual-mitigation \
      --ignore-not-found
    
  2. Confirm the rollback completed successfully:

    kubectl -n "$ARGOCD_NS" get networkpolicy
    kubectl -n "$ARGOCD_NS" get pods
    kubectl get applications.argoproj.io -A
    kubectl -n "$ARGOCD_NS" get networkpolicy
    kubectl -n "$ARGOCD_NS" get pods
    kubectl get applications.argoproj.io -A
    

    Confirm the following:

    • The manual mitigation policies are removed.
    • ArgoCD pods remain Running or Completed.
    • ArgoCD Applications remain Synced and Healthy.
  • Description​
  • Prerequisites​
  • Solution​
  • Verify​
  • Confirm Mitigation​
  • Rollback​

Was this page helpful?

Connect

Need help? Support

Want to learn? UiPath Academy

Have questions? UiPath Forum

Stay updated