Skip to content

Commit a470dc6

Browse files
committed
Harden firewall existence check and validation in ensure-integration-network
1 parent 19ac1aa commit a470dc6

1 file changed

Lines changed: 69 additions & 42 deletions

File tree

ci/tasks/gcp/ensure-integration-network.sh

Lines changed: 69 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -60,51 +60,78 @@ else
6060
fi
6161

6262
echo "Checking for firewall rule '${SUBNET_NAME}'..."
63-
current_fw="$(gcloud compute firewall-rules describe "${SUBNET_NAME}" \
63+
existing_fw_name="$(gcloud compute firewall-rules list \
6464
--project="${GCP_PROJECT_ID}" \
65-
--format='csv[no-heading](network.basename(),direction,allowed[0].IPProtocol,sourceRanges[0],disabled)' \
66-
2>"${gcloud_stderr}")" && fw_exists=true || fw_exists=false
67-
68-
if ${fw_exists}; then
69-
expected_fw="${GCP_NETWORK_NAME},INGRESS,all,${SUBNET_CIDR},False"
70-
if [[ "${current_fw}" != "${expected_fw}" ]]; then
71-
echo "ERROR: Firewall rule '${SUBNET_NAME}' exists but is misconfigured."
72-
echo " Expected: ${expected_fw}"
73-
echo " Actual: ${current_fw}"
74-
exit 1
75-
fi
76-
# Validate target tags independently; sort before comparing since order is not deterministic
77-
current_tags="$(gcloud compute firewall-rules describe "${SUBNET_NAME}" \
78-
--project="${GCP_PROJECT_ID}" \
79-
--format='value(targetTags.list())' \
80-
2>"${gcloud_stderr}" \
81-
| tr ',;' '\n' | LC_ALL=C sort | tr '\n' ',' | sed 's/,$//')" && current_tags_read=true || current_tags_read=false
82-
if ! ${current_tags_read}; then
83-
echo "ERROR: gcloud describe failed while reading target tags for firewall rule '${SUBNET_NAME}':"
84-
cat "${gcloud_stderr}" >&2
85-
exit 1
86-
fi
87-
expected_tags="$(printf '%s\n' ${FIREWALL_TAGS//,/ } | LC_ALL=C sort | tr '\n' ',' | sed 's/,$//')"
88-
if [[ "${current_tags}" != "${expected_tags}" ]]; then
89-
echo "ERROR: Firewall rule '${SUBNET_NAME}' has wrong target tags."
90-
echo " Expected: ${expected_tags}"
91-
echo " Actual: ${current_tags}"
92-
exit 1
65+
--filter="name=('${SUBNET_NAME}')" \
66+
--format='value(name)' \
67+
2>"${gcloud_stderr}")" && fw_lookup_ok=true || fw_lookup_ok=false
68+
69+
if ${fw_lookup_ok}; then
70+
if [[ -n "${existing_fw_name}" ]]; then
71+
current_fw_json="$(gcloud compute firewall-rules describe "${SUBNET_NAME}" \
72+
--project="${GCP_PROJECT_ID}" \
73+
--format=json \
74+
2>"${gcloud_stderr}")"
75+
76+
# Validate network, direction, disabled
77+
actual_network="$(echo "${current_fw_json}" | jq -r '.network | split("/") | last')"
78+
actual_direction="$(echo "${current_fw_json}" | jq -r '.direction')"
79+
actual_disabled="$(echo "${current_fw_json}" | jq -r '.disabled')"
80+
81+
if [[ "${actual_network}" != "${GCP_NETWORK_NAME}" ]] || \
82+
[[ "${actual_direction}" != "INGRESS" ]] || \
83+
[[ "${actual_disabled}" != "false" ]]; then
84+
echo "ERROR: Firewall rule '${SUBNET_NAME}' exists but is misconfigured."
85+
echo " Expected network=${GCP_NETWORK_NAME}, direction=INGRESS, disabled=false"
86+
echo " Actual network=${actual_network}, direction=${actual_direction}, disabled=${actual_disabled}"
87+
exit 1
88+
fi
89+
90+
# Validate allowed (should be exactly [{IPProtocol: "all"}])
91+
actual_allowed="$(echo "${current_fw_json}" | jq -c '[.allowed[] | {protocol: .IPProtocol, ports: (.ports // [])}] | sort_by(.protocol)')"
92+
expected_allowed='[{"protocol":"all","ports":[]}]'
93+
if [[ "${actual_allowed}" != "${expected_allowed}" ]]; then
94+
echo "ERROR: Firewall rule '${SUBNET_NAME}' has wrong allowed configuration."
95+
echo " Expected: ${expected_allowed}"
96+
echo " Actual: ${actual_allowed}"
97+
exit 1
98+
fi
99+
100+
# Validate sourceRanges (should be exactly the subnet CIDR)
101+
actual_ranges="$(echo "${current_fw_json}" | jq -c '(.sourceRanges // []) | sort')"
102+
expected_ranges="$(printf '["%s"]' "${SUBNET_CIDR}")"
103+
if [[ "${actual_ranges}" != "${expected_ranges}" ]]; then
104+
echo "ERROR: Firewall rule '${SUBNET_NAME}' has wrong source ranges."
105+
echo " Expected: ${expected_ranges}"
106+
echo " Actual: ${actual_ranges}"
107+
exit 1
108+
fi
109+
110+
# Validate targetTags (order-insensitive)
111+
actual_tags="$(echo "${current_fw_json}" | jq -c '(.targetTags // []) | sort')"
112+
expected_tags="$(printf '%s\n' ${FIREWALL_TAGS//,/ } | jq -R . | jq -sc 'sort')"
113+
if [[ "${actual_tags}" != "${expected_tags}" ]]; then
114+
echo "ERROR: Firewall rule '${SUBNET_NAME}' has wrong target tags."
115+
echo " Expected: ${expected_tags}"
116+
echo " Actual: ${actual_tags}"
117+
exit 1
118+
fi
119+
120+
echo "Firewall rule '${SUBNET_NAME}' already exists and matches expected configuration."
121+
else
122+
echo "Creating firewall rule '${SUBNET_NAME}'..."
123+
gcloud compute firewall-rules create "${SUBNET_NAME}" \
124+
--network="${GCP_NETWORK_NAME}" \
125+
--project="${GCP_PROJECT_ID}" \
126+
--direction=INGRESS \
127+
--priority=1000 \
128+
--allow=all \
129+
--source-ranges="${SUBNET_CIDR}" \
130+
--target-tags="${FIREWALL_TAGS}"
131+
echo "Firewall rule '${SUBNET_NAME}' created."
93132
fi
94-
echo "Firewall rule '${SUBNET_NAME}' already exists and matches expected configuration."
95-
elif grep -q "was not found" "${gcloud_stderr}"; then
96-
echo "Creating firewall rule '${SUBNET_NAME}'..."
97-
gcloud compute firewall-rules create "${SUBNET_NAME}" \
98-
--network="${GCP_NETWORK_NAME}" \
99-
--project="${GCP_PROJECT_ID}" \
100-
--direction=INGRESS \
101-
--priority=1000 \
102-
--allow=all \
103-
--source-ranges="${SUBNET_CIDR}" \
104-
--target-tags="${FIREWALL_TAGS}"
105-
echo "Firewall rule '${SUBNET_NAME}' created."
106133
else
107-
echo "ERROR: gcloud describe failed for firewall rule '${SUBNET_NAME}':"
134+
echo "ERROR: gcloud firewall-rules lookup failed for '${SUBNET_NAME}':"
108135
cat "${gcloud_stderr}" >&2
109136
exit 1
110137
fi

0 commit comments

Comments
 (0)