@@ -164,91 +164,83 @@ jobs:
164164 - name : Generate kubeconfig for spoke clusters
165165 run : |
166166 echo "🔧 Generating kubeconfig for local spoke clusters..."
167- echo "Spoke clusters are pre-configured in Netbird with known IPs"
168- echo ""
169-
167+
170168 # Read spoke cluster list from secrets
171- # Expected format: SPOKE_CLUSTERS=spoke-1,spoke-2,spoke-3
172169 SPOKE_CLUSTERS="${{ secrets.SPOKE_CLUSTERS }}"
173-
174170 if [ -z "$SPOKE_CLUSTERS" ]; then
175- echo "⚠ SPOKE_CLUSTERS secret not set"
176- echo " Using default: spoke-1"
171+ echo "⚠ SPOKE_CLUSTERS secret not set, using default: spoke-1"
177172 SPOKE_CLUSTERS="spoke-1"
178173 fi
179174
180175 IFS=',' read -ra CLUSTERS <<< "$SPOKE_CLUSTERS"
181176
182177 for cluster in "${CLUSTERS[@]}"; do
183- cluster=$(echo "$cluster" | xargs) # Trim whitespace
184-
185- echo "Processing cluster: '$cluster'"
178+ cluster=$(echo "$cluster" | xargs)
179+ echo "--- Processing cluster: $cluster ---"
186180
187- # Get Netbird IP and certificates based on cluster name
181+ # Get secrets based on cluster name
188182 case "$cluster" in
189183 spoke-1)
190184 NETBIRD_IP="${{ secrets.SPOKE_1_NETBIRD_IP }}"
191- CA_CERT ="${{ secrets.SPOKE_1_CA_CERT }}"
192- CLIENT_CERT ="${{ secrets.SPOKE_1_CLIENT_CERT }}"
193- CLIENT_KEY ="${{ secrets.SPOKE_1_CLIENT_KEY }}"
185+ CA_CERT_B64 ="${{ secrets.SPOKE_1_CA_CERT }}"
186+ CLIENT_CERT_B64 ="${{ secrets.SPOKE_1_CLIENT_CERT }}"
187+ CLIENT_KEY_B64 ="${{ secrets.SPOKE_1_CLIENT_KEY }}"
194188 ;;
195189 spoke-2)
196190 NETBIRD_IP="${{ secrets.SPOKE_2_NETBIRD_IP }}"
197- CA_CERT ="${{ secrets.SPOKE_2_CA_CERT }}"
198- CLIENT_CERT ="${{ secrets.SPOKE_2_CLIENT_CERT }}"
199- CLIENT_KEY ="${{ secrets.SPOKE_2_CLIENT_KEY }}"
191+ CA_CERT_B64 ="${{ secrets.SPOKE_2_CA_CERT }}"
192+ CLIENT_CERT_B64 ="${{ secrets.SPOKE_2_CLIENT_CERT }}"
193+ CLIENT_KEY_B64 ="${{ secrets.SPOKE_2_CLIENT_KEY }}"
200194 ;;
201195 spoke-3)
202196 NETBIRD_IP="${{ secrets.SPOKE_3_NETBIRD_IP }}"
203- CA_CERT ="${{ secrets.SPOKE_3_CA_CERT }}"
204- CLIENT_CERT ="${{ secrets.SPOKE_3_CLIENT_CERT }}"
205- CLIENT_KEY ="${{ secrets.SPOKE_3_CLIENT_KEY }}"
197+ CA_CERT_B64 ="${{ secrets.SPOKE_3_CA_CERT }}"
198+ CLIENT_CERT_B64 ="${{ secrets.SPOKE_3_CLIENT_CERT }}"
199+ CLIENT_KEY_B64 ="${{ secrets.SPOKE_3_CLIENT_KEY }}"
206200 ;;
207201 *)
208202 echo " ⚠ Unknown cluster: $cluster"
209- echo " Expected cluster name: spoke-1, spoke-2, or spoke-3"
210- echo " Please check your SPOKE_CLUSTERS secret"
211203 continue
212204 ;;
213205 esac
214206
215- # Use GitHub secrets syntax to get values
216- echo "Configuring kubectl for $cluster..."
217-
218- if [ ! -z "$NETBIRD_IP" ]; then
219- echo " Netbird IP: $NETBIRD_IP"
220-
221- # Create kubeconfig entries
222- kubectl config set-cluster $cluster \
223- --server=https://$NETBIRD_IP:6443 \
224- --certificate-authority=<(echo "$CA_CERT" | base64 -d) \
225- --embed-certs=true
226-
227- kubectl config set-credentials $cluster-admin \
228- --client-certificate=<(echo "$CLIENT_CERT" | base64 -d) \
229- --client-key=<(echo "$CLIENT_KEY" | base64 -d) \
230- --embed-certs=true
207+ # Validation: Fail early if required secrets are missing
208+ if [ -z "$NETBIRD_IP" ] || [ -z "$CA_CERT_B64" ] || [ -z "$CLIENT_CERT_B64" ] || [ -z "$CLIENT_KEY_B64" ]; then
209+ echo " ❌ ERROR: Missing secrets for $cluster. Ensure NETBIRD_IP, CA_CERT, CLIENT_CERT, and CLIENT_KEY are set."
210+ exit 1
211+ fi
231212
232- kubectl config set-context $cluster \
233- --cluster=$cluster \
234- --user=$cluster-admin
213+ # Clean IPs
214+ NETBIRD_IP="${NETBIRD_IP%/32}"
235215
236- # Verify connectivity via Netbird
237- echo " Testing cluster API access..."
238- if kubectl cluster-info --context $cluster >/dev/null 2>&1; then
239- echo " ✅ Cluster API accessible: $cluster"
240- else
241- echo " ⚠ Cannot access cluster API: $cluster"
242- fi
243- else
244- echo " ⚠ Netbird IP not configured for $cluster"
216+ # Create temporary files for certificates to ensure reliable kubeconfig embedding
217+ CA_FILE=$(mktemp)
218+ CERT_FILE=$(mktemp)
219+ KEY_FILE=$(mktemp)
220+
221+ echo "$CA_CERT_B64" | base64 -d > "$CA_FILE"
222+ echo "$CLIENT_CERT_B64" | base64 -d > "$CERT_FILE"
223+ echo "$CLIENT_KEY_B64" | base64 -d > "$KEY_FILE"
224+
225+ echo " Configuring kubectl context..."
226+ kubectl config set-cluster "$cluster" --server="https://$NETBIRD_IP:6443" --certificate-authority="$CA_FILE" --embed-certs=true
227+ kubectl config set-credentials "$cluster-admin" --client-certificate="$CERT_FILE" --client-key="$KEY_FILE" --embed-certs=true
228+ kubectl config set-context "$cluster" --cluster="$cluster" --user="$cluster-admin"
229+
230+ # Cleanup temp files
231+ rm -f "$CA_FILE" "$CERT_FILE" "$KEY_FILE"
232+
233+ # Mandatory verification: Fail early if unauthorized
234+ echo " Verifying API access to $cluster..."
235+ if ! kubectl cluster-info --context "$cluster" --insecure-skip-tls-verify=true > /dev/null 2>&1; then
236+ echo " ❌ ERROR: Authentication failed for $cluster (Unauthorized). Verify secrets are correct."
237+ kubectl cluster-info --context "$cluster" --insecure-skip-tls-verify=true || true
238+ exit 1
245239 fi
246-
247- echo ""
240+ echo " ✅ Cluster $cluster is reachable and authorized"
248241 done
249242
250- echo "Kubeconfig contexts configured:"
251- kubectl config get-contexts
243+ echo "✅ Kubeconfig configured for all spoke clusters"
252244
253245 # Validate that all clusters in SPOKE_CLUSTERS have their contexts configured
254246 - name : Validate spoke cluster contexts
@@ -563,70 +555,83 @@ jobs:
563555 - name : Generate kubeconfig for spoke clusters
564556 run : |
565557 echo "🔧 Generating kubeconfig for local spoke clusters..."
566-
558+
559+ # Read spoke cluster list from secrets
567560 SPOKE_CLUSTERS="${{ secrets.SPOKE_CLUSTERS }}"
568561 if [ -z "$SPOKE_CLUSTERS" ]; then
569- SPOKE_CLUSTERS="spoke-1,spoke-2,spoke-3"
562+ echo "⚠ SPOKE_CLUSTERS secret not set, using default: spoke-1"
563+ SPOKE_CLUSTERS="spoke-1"
570564 fi
571565
572566 IFS=',' read -ra CLUSTERS <<< "$SPOKE_CLUSTERS"
573567
574568 for cluster in "${CLUSTERS[@]}"; do
575569 cluster=$(echo "$cluster" | xargs)
570+ echo "--- Processing cluster: $cluster ---"
576571
577- # Get Netbird IP and certificates based on cluster name
572+ # Get secrets based on cluster name
578573 case "$cluster" in
579574 spoke-1)
580575 NETBIRD_IP="${{ secrets.SPOKE_1_NETBIRD_IP }}"
581- # Strip /32 suffix if present (CIDR notation)
582- NETBIRD_IP="${NETBIRD_IP%/32}"
583- CA_CERT="${{ secrets.SPOKE_1_CA_CERT }}"
584- CLIENT_CERT="${{ secrets.SPOKE_1_CLIENT_CERT }}"
585- CLIENT_KEY="${{ secrets.SPOKE_1_CLIENT_KEY }}"
576+ CA_CERT_B64="${{ secrets.SPOKE_1_CA_CERT }}"
577+ CLIENT_CERT_B64="${{ secrets.SPOKE_1_CLIENT_CERT }}"
578+ CLIENT_KEY_B64="${{ secrets.SPOKE_1_CLIENT_KEY }}"
586579 ;;
587580 spoke-2)
588581 NETBIRD_IP="${{ secrets.SPOKE_2_NETBIRD_IP }}"
589- NETBIRD_IP="${NETBIRD_IP%/32}"
590- CA_CERT="${{ secrets.SPOKE_2_CA_CERT }}"
591- CLIENT_CERT="${{ secrets.SPOKE_2_CLIENT_CERT }}"
592- CLIENT_KEY="${{ secrets.SPOKE_2_CLIENT_KEY }}"
582+ CA_CERT_B64="${{ secrets.SPOKE_2_CA_CERT }}"
583+ CLIENT_CERT_B64="${{ secrets.SPOKE_2_CLIENT_CERT }}"
584+ CLIENT_KEY_B64="${{ secrets.SPOKE_2_CLIENT_KEY }}"
593585 ;;
594586 spoke-3)
595587 NETBIRD_IP="${{ secrets.SPOKE_3_NETBIRD_IP }}"
596- NETBIRD_IP="${NETBIRD_IP%/32}"
597- CA_CERT="${{ secrets.SPOKE_3_CA_CERT }}"
598- CLIENT_CERT="${{ secrets.SPOKE_3_CLIENT_CERT }}"
599- CLIENT_KEY="${{ secrets.SPOKE_3_CLIENT_KEY }}"
588+ CA_CERT_B64="${{ secrets.SPOKE_3_CA_CERT }}"
589+ CLIENT_CERT_B64="${{ secrets.SPOKE_3_CLIENT_CERT }}"
590+ CLIENT_KEY_B64="${{ secrets.SPOKE_3_CLIENT_KEY }}"
600591 ;;
601592 *)
602593 echo " ⚠ Unknown cluster: $cluster"
603- echo " Expected cluster name: spoke-1, spoke-2, or spoke-3"
604- echo " Please check your SPOKE_CLUSTERS secret"
605594 continue
606595 ;;
607596 esac
608597
609- echo "Configuring $cluster..."
610-
611- if [ ! -z "$NETBIRD_IP" ]; then
612- echo " Netbird IP: $NETBIRD_IP"
613-
614- kubectl config set-cluster $cluster \
615- --server=https://$NETBIRD_IP:6443 \
616- --certificate-authority=<(echo "$CA_CERT" | base64 -d) \
617- --embed-certs=true
598+ # Validation: Fail early if required secrets are missing
599+ if [ -z "$NETBIRD_IP" ] || [ -z "$CA_CERT_B64" ] || [ -z "$CLIENT_CERT_B64" ] || [ -z "$CLIENT_KEY_B64" ]; then
600+ echo " ❌ ERROR: Missing secrets for $cluster. Ensure NETBIRD_IP, CA_CERT, CLIENT_CERT, and CLIENT_KEY are set."
601+ exit 1
602+ fi
618603
619- kubectl config set-credentials $cluster-admin \
620- --client-certificate=<(echo "$CLIENT_CERT" | base64 -d) \
621- --client-key=<(echo "$CLIENT_KEY" | base64 -d) \
622- --embed-certs=true
604+ # Clean IPs
605+ NETBIRD_IP="${NETBIRD_IP%/32}"
623606
624- kubectl config set-context $cluster \
625- --cluster=$cluster \
626- --user=$cluster-admin
607+ # Create temporary files for certificates to ensure reliable kubeconfig embedding
608+ CA_FILE=$(mktemp)
609+ CERT_FILE=$(mktemp)
610+ KEY_FILE=$(mktemp)
611+
612+ echo "$CA_CERT_B64" | base64 -d > "$CA_FILE"
613+ echo "$CLIENT_CERT_B64" | base64 -d > "$CERT_FILE"
614+ echo "$CLIENT_KEY_B64" | base64 -d > "$KEY_FILE"
615+
616+ echo " Configuring kubectl context..."
617+ kubectl config set-cluster "$cluster" --server="https://$NETBIRD_IP:6443" --certificate-authority="$CA_FILE" --embed-certs=true
618+ kubectl config set-credentials "$cluster-admin" --client-certificate="$CERT_FILE" --client-key="$KEY_FILE" --embed-certs=true
619+ kubectl config set-context "$cluster" --cluster="$cluster" --user="$cluster-admin"
620+
621+ # Cleanup temp files
622+ rm -f "$CA_FILE" "$CERT_FILE" "$KEY_FILE"
623+
624+ # Mandatory verification: Fail early if unauthorized
625+ echo " Verifying API access to $cluster..."
626+ if ! kubectl cluster-info --context "$cluster" --insecure-skip-tls-verify=true > /dev/null 2>&1; then
627+ echo " ❌ ERROR: Authentication failed for $cluster (Unauthorized). Verify secrets are correct."
628+ kubectl cluster-info --context "$cluster" --insecure-skip-tls-verify=true || true
629+ exit 1
627630 fi
631+ echo " ✅ Cluster $cluster is reachable and authorized"
628632 done
629633
634+ echo "✅ Kubeconfig configured for all spoke clusters"
630635 kubectl config get-contexts
631636
632637 # Get hub context and dynamically resolve principal address
0 commit comments