Skip to content

Commit 5196de1

Browse files
committed
terraform: fix security rule reconciliation on Azure (#3454)
* fix security rule reconciliation on azure * fix simulated patch version upgrade
1 parent 97ae5d8 commit 5196de1

File tree

6 files changed

+97
-121
lines changed

6 files changed

+97
-121
lines changed

.github/workflows/e2e-upgrade.yml

Lines changed: 28 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -132,57 +132,6 @@ jobs:
132132
133133
echo "cloudProvider=${cloudProvider}" | tee -a "$GITHUB_OUTPUT"
134134
135-
build-target-cli:
136-
name: Build upgrade target version CLI
137-
runs-on: ubuntu-24.04
138-
permissions:
139-
id-token: write
140-
checks: write
141-
contents: read
142-
packages: write
143-
steps:
144-
- name: Checkout
145-
if: inputs.gitRef == 'head'
146-
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
147-
with:
148-
fetch-depth: 0
149-
ref: ${{ !github.event.pull_request.head.repo.fork && github.head_ref || '' }}
150-
151-
- name: Checkout ref
152-
if: inputs.gitRef != 'head'
153-
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
154-
with:
155-
fetch-depth: 0
156-
ref: ${{ inputs.gitRef }}
157-
158-
- name: Setup Bazel & Nix
159-
uses: ./.github/actions/setup_bazel_nix
160-
161-
- name: Log in to the Container registry
162-
uses: ./.github/actions/container_registry_login
163-
with:
164-
registry: ghcr.io
165-
username: ${{ github.actor }}
166-
password: ${{ secrets.GITHUB_TOKEN }}
167-
168-
- name: Simulate patch upgrade
169-
if: inputs.simulatedTargetVersion != ''
170-
run: |
171-
echo ${{ inputs.simulatedTargetVersion }} > version.txt
172-
173-
- name: Build CLI
174-
uses: ./.github/actions/build_cli
175-
with:
176-
enterpriseCLI: true
177-
outputPath: "build/constellation"
178-
push: true
179-
180-
- name: Upload CLI binary
181-
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
182-
with:
183-
name: constellation-upgrade-${{ inputs.attestationVariant }}
184-
path: build/constellation
185-
186135
create-cluster:
187136
name: Create upgrade origin version cluster
188137
runs-on: ubuntu-24.04
@@ -279,7 +228,6 @@ jobs:
279228
packages: write
280229
needs:
281230
- generate-input-parameters
282-
- build-target-cli
283231
- create-cluster
284232
steps:
285233
- name: Checkout
@@ -299,6 +247,32 @@ jobs:
299247
- name: Setup Bazel & Nix
300248
uses: ./.github/actions/setup_bazel_nix
301249

250+
- name: Log in to the Container registry
251+
uses: ./.github/actions/container_registry_login
252+
with:
253+
registry: ghcr.io
254+
username: ${{ github.actor }}
255+
password: ${{ secrets.GITHUB_TOKEN }}
256+
257+
# applying the version manipulation here so that the upgrade test tool is also on the simulated target version
258+
- name: Simulate patch upgrade
259+
if: inputs.simulatedTargetVersion != ''
260+
run: |
261+
echo ${{ inputs.simulatedTargetVersion }} > version.txt
262+
263+
- name: Build CLI
264+
uses: ./.github/actions/build_cli
265+
with:
266+
enterpriseCLI: true
267+
outputPath: "build/constellation"
268+
push: true
269+
270+
- name: Upload CLI binary # is needed for the cleanup step
271+
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # v4.3.4
272+
with:
273+
name: constellation-upgrade-${{ inputs.attestationVariant }}
274+
path: build/constellation
275+
302276
- name: Login to AWS
303277
uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
304278
with:
@@ -335,11 +309,6 @@ jobs:
335309
with:
336310
azure_credentials: ${{ secrets.AZURE_E2E_IAM_CREDENTIALS }}
337311

338-
- name: Download CLI
339-
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
340-
with:
341-
name: constellation-upgrade-${{ inputs.attestationVariant }}
342-
path: build
343312

344313
- name: Download Working Directory (Pre-test)
345314
uses: ./.github/actions/artifact_download
@@ -404,15 +373,9 @@ jobs:
404373
echo "K8s target: $KUBERNETES"
405374
echo "Microservice target: $MICROSERVICES"
406375
407-
if [[ -n ${MICROSERVICES} ]]; then
408-
MICROSERVICES_FLAG="--target-microservices=$MICROSERVICES"
409-
fi
410-
if [[ -n ${KUBERNETES} ]]; then
411-
KUBERNETES_FLAG="--target-kubernetes=$KUBERNETES"
412-
fi
413-
414376
sudo sh -c 'echo "127.0.0.1 license.confidential.cloud" >> /etc/hosts'
415-
bazel run --test_timeout=14400 //e2e/internal/upgrade:upgrade_test -- --want-worker "$WORKERNODES" --want-control "$CONTROLNODES" --target-image "$IMAGE" "$KUBERNETES_FLAG" "$MICROSERVICES_FLAG"
377+
CLI=$(realpath ./build/constellation)
378+
bazel run --test_timeout=14400 //e2e/internal/upgrade:upgrade_test -- --want-worker "$WORKERNODES" --want-control "$CONTROLNODES" --target-image "$IMAGE" --target-kubernetes "$KUBERNETES" --target-microservices "$MICROSERVICES" --cli "$CLI"
416379
417380
- name: Remove Terraform plugin cache
418381
if: always()

docs/docs/reference/migration.md

Lines changed: 52 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,51 +3,81 @@
33
This document describes breaking changes and migrations between Constellation releases.
44
Use [`constellation config migrate`](./cli.md#constellation-config-migrate) to automatically update an old config file to a new format.
55

6+
## Migrations to v2.19.1
7+
8+
### Azure
9+
10+
* During the upgrade, security rules are migrated and the old ones need to be cleaned up manually by the user. The below script shows how to delete them through the Azure CLI:
11+
12+
```bash
13+
#!/usr/bin/env bash
14+
name="<insert>" # the name provided in the config
15+
uid="<insert>" # the cluster id can be retrieved via `yq '.infrastructure.uid' constellation-state.yaml`
16+
resource_group="<insert>" # the RG can be retrieved via `yq '.provider.azure.resourceGroup' constellation-conf.yaml`
17+
18+
rules=(
19+
"kubernetes"
20+
"bootstrapper"
21+
"verify"
22+
"recovery"
23+
"join"
24+
"debugd"
25+
"konnectivity"
26+
)
27+
28+
for rule in "${rules[@]}"; do
29+
echo "Deleting rule: ${rule}"
30+
az network nsg rule delete \
31+
--resource-group "${resource_group}" \
32+
--nsg-name "${name}-${uid}" \
33+
--name "${rule}"
34+
done
35+
36+
echo "All specified rules have been deleted."
37+
```
638

739
## Migrations to v2.19.0
840

941
### Azure
1042

11-
* To allow seamless upgrades on Azure when Kubernetes services of type `LoadBalancer` are deployed, the target
43+
* To allow seamless upgrades on Azure when Kubernetes services of type `LoadBalancer` are deployed, the target
1244
load balancer in which the `cloud-controller-manager` creates load balancing rules was changed. Instead of using the load balancer
1345
created and maintained by the CLI's Terraform code, the `cloud-controller-manager` now creates its own load balancer in Azure.
1446
If your Constellation has services of type `LoadBalancer`, please remove them before the upgrade and re-apply them
15-
afterward.
16-
47+
afterward.
1748

1849
## Migrating from Azure's service principal authentication to managed identity authentication (during the upgrade to Constellation v2.8.0)
1950

20-
- The `provider.azure.appClientID` and `provider.azure.appClientSecret` fields are no longer supported and should be removed.
21-
- To keep using an existing UAMI, add the `Owner` permission with the scope of your `resourceGroup`.
22-
- Otherwise, simply [create new Constellation IAM credentials](../workflows/config.md#creating-an-iam-configuration) and use the created UAMI.
23-
- To migrate the authentication for an existing cluster on Azure to an UAMI with the necessary permissions:
51+
* The `provider.azure.appClientID` and `provider.azure.appClientSecret` fields are no longer supported and should be removed.
52+
* To keep using an existing UAMI, add the `Owner` permission with the scope of your `resourceGroup`.
53+
* Otherwise, simply [create new Constellation IAM credentials](../workflows/config.md#creating-an-iam-configuration) and use the created UAMI.
54+
* To migrate the authentication for an existing cluster on Azure to an UAMI with the necessary permissions:
2455
1. Remove the `aadClientId` and `aadClientSecret` from the azureconfig secret.
2556
2. Set `useManagedIdentityExtension` to `true` and use the `userAssignedIdentity` from the Constellation config for the value of `userAssignedIdentityID`.
2657
3. Restart the CSI driver, cloud controller manager, cluster autoscaler, and Constellation operator pods.
2758

28-
2959
## Migrating from CLI versions before 2.10
3060

31-
- AWS cluster upgrades require additional IAM permissions for the newly introduced `aws-load-balancer-controller`. Please upgrade your IAM roles using `iam upgrade apply`. This will show necessary changes and apply them, if desired.
32-
- The global `nodeGroups` field was added.
33-
- The fields `instanceType`, `stateDiskSizeGB`, and `stateDiskType` for each cloud provider are now part of the configuration of individual node groups.
34-
- The `constellation create` command no longer uses the flags `--control-plane-count` and `--worker-count`. Instead, the initial node count is configured per node group in the `nodeGroups` field.
61+
* AWS cluster upgrades require additional IAM permissions for the newly introduced `aws-load-balancer-controller`. Please upgrade your IAM roles using `iam upgrade apply`. This will show necessary changes and apply them, if desired.
62+
* The global `nodeGroups` field was added.
63+
* The fields `instanceType`, `stateDiskSizeGB`, and `stateDiskType` for each cloud provider are now part of the configuration of individual node groups.
64+
* The `constellation create` command no longer uses the flags `--control-plane-count` and `--worker-count`. Instead, the initial node count is configured per node group in the `nodeGroups` field.
3565

3666
## Migrating from CLI versions before 2.9
3767

38-
- The `provider.azure.appClientID` and `provider.azure.clientSecretValue` fields were removed to enforce migration to managed identity authentication
68+
* The `provider.azure.appClientID` and `provider.azure.clientSecretValue` fields were removed to enforce migration to managed identity authentication
3969

4070
## Migrating from CLI versions before 2.8
4171

42-
- The `measurements` field for each cloud service provider was replaced with a global `attestation` field.
43-
- The `confidentialVM`, `idKeyDigest`, and `enforceIdKeyDigest` fields for the Azure cloud service provider were removed in favor of using the global `attestation` field.
44-
- The optional global field `attestationVariant` was replaced by the now required `attestation` field.
72+
* The `measurements` field for each cloud service provider was replaced with a global `attestation` field.
73+
* The `confidentialVM`, `idKeyDigest`, and `enforceIdKeyDigest` fields for the Azure cloud service provider were removed in favor of using the global `attestation` field.
74+
* The optional global field `attestationVariant` was replaced by the now required `attestation` field.
4575

4676
## Migrating from CLI versions before 2.3
4777

48-
- The `sshUsers` field was deprecated in v2.2 and has been removed from the configuration in v2.3.
78+
* The `sshUsers` field was deprecated in v2.2 and has been removed from the configuration in v2.3.
4979
As an alternative for SSH, check the workflow section [Connect to nodes](../workflows/troubleshooting.md#node-shell-access).
50-
- The `image` field for each cloud service provider has been replaced with a global `image` field. Use the following mapping to migrate your configuration:
80+
* The `image` field for each cloud service provider has been replaced with a global `image` field. Use the following mapping to migrate your configuration:
5181
<details>
5282
<summary>Show all</summary>
5383

@@ -77,10 +107,11 @@ Use [`constellation config migrate`](./cli.md#constellation-config-migrate) to a
77107
| GCP | `projects/constellation-images/global/images/constellation-v2-2-0` | `v2.2.0` |
78108
| GCP | `projects/constellation-images/global/images/constellation-v2-1-0` | `v2.1.0` |
79109
| GCP | `projects/constellation-images/global/images/constellation-v2-0-0` | `v2.0.0` |
110+
80111
</details>
81-
- The `enforcedMeasurements` field has been removed and merged with the `measurements` field.
82-
- To migrate your config containing a new image (`v2.3` or greater), remove the old `measurements` and `enforcedMeasurements` entries from your config and run `constellation fetch-measurements`
83-
- To migrate your config containing an image older than `v2.3`, remove the `enforcedMeasurements` entry and replace the entries in `measurements` as shown in the example below:
112+
* The `enforcedMeasurements` field has been removed and merged with the `measurements` field.
113+
* To migrate your config containing a new image (`v2.3` or greater), remove the old `measurements` and `enforcedMeasurements` entries from your config and run `constellation fetch-measurements`
114+
* To migrate your config containing an image older than `v2.3`, remove the `enforcedMeasurements` entry and replace the entries in `measurements` as shown in the example below:
84115

85116
```diff
86117
measurements:

e2e/internal/upgrade/BUILD.bazel

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ go_test(
4747
"//e2e/internal/kubectl",
4848
"//internal/constants",
4949
"//internal/versions",
50+
"@com_github_stretchr_testify//assert",
5051
"@com_github_stretchr_testify//require",
5152
"@io_k8s_api//core/v1:core",
5253
"@io_k8s_apimachinery//pkg/apis/meta/v1:meta",

e2e/internal/upgrade/upgrade.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -297,10 +297,10 @@ func getCLIPath(cliPathFlag string) (string, error) {
297297
pathCLI := os.Getenv("PATH_CLI")
298298
var relCLIPath string
299299
switch {
300-
case pathCLI != "":
301-
relCLIPath = pathCLI
302300
case cliPathFlag != "":
303301
relCLIPath = cliPathFlag
302+
case pathCLI != "":
303+
relCLIPath = pathCLI
304304
default:
305305
return "", errors.New("neither 'PATH_CLI' nor 'cli' flag set")
306306
}

e2e/internal/upgrade/upgrade_test.go

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"github.com/edgelesssys/constellation/v2/e2e/internal/kubectl"
2424
"github.com/edgelesssys/constellation/v2/internal/constants"
2525
"github.com/edgelesssys/constellation/v2/internal/versions"
26+
"github.com/stretchr/testify/assert"
2627
"github.com/stretchr/testify/require"
2728
coreV1 "k8s.io/api/core/v1"
2829
metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -81,7 +82,8 @@ func TestUpgrade(t *testing.T) {
8182
log.Println(string(data))
8283

8384
log.Println("Checking upgrade.")
84-
runUpgradeCheck(require, cli, *targetKubernetes)
85+
assert := assert.New(t) // use assert because this part is more brittle and should not fail the entire test
86+
runUpgradeCheck(assert, cli, *targetKubernetes)
8587

8688
log.Println("Triggering upgrade.")
8789
runUpgradeApply(require, cli)
@@ -170,25 +172,25 @@ func testNodesEventuallyAvailable(t *testing.T, k *kubernetes.Clientset, wantCon
170172

171173
// runUpgradeCheck executes 'upgrade check' and does basic checks on the output.
172174
// We can not check images upgrades because we might use unpublished images. CLI uses public CDN to check for available images.
173-
func runUpgradeCheck(require *require.Assertions, cli, targetKubernetes string) {
175+
func runUpgradeCheck(assert *assert.Assertions, cli, targetKubernetes string) {
174176
cmd := exec.CommandContext(context.Background(), cli, "upgrade", "check", "--debug")
175177
stdout, stderr, err := runCommandWithSeparateOutputs(cmd)
176-
require.NoError(err, "Stdout: %s\nStderr: %s", string(stdout), string(stderr))
178+
assert.NoError(err, "Stdout: %s\nStderr: %s", string(stdout), string(stderr))
177179

178-
require.Contains(string(stdout), "The following updates are available with this CLI:")
179-
require.Contains(string(stdout), "Kubernetes:")
180+
assert.Contains(string(stdout), "The following updates are available with this CLI:")
181+
assert.Contains(string(stdout), "Kubernetes:")
180182
log.Printf("targetKubernetes: %s\n", targetKubernetes)
181183

182184
if targetKubernetes == "" {
183185
log.Printf("true\n")
184-
require.True(containsAny(string(stdout), versions.SupportedK8sVersions()))
186+
assert.True(containsAny(string(stdout), versions.SupportedK8sVersions()))
185187
} else {
186188
log.Printf("false. targetKubernetes: %s\n", targetKubernetes)
187-
require.Contains(string(stdout), targetKubernetes, fmt.Sprintf("Expected Kubernetes version %s in output.", targetKubernetes))
189+
assert.Contains(string(stdout), targetKubernetes, fmt.Sprintf("Expected Kubernetes version %s in output.", targetKubernetes))
188190
}
189191

190-
require.Contains(string(stdout), "Services:")
191-
require.Contains(string(stdout), fmt.Sprintf("--> %s", constants.BinaryVersion().String()))
192+
assert.Contains(string(stdout), "Services:")
193+
assert.Contains(string(stdout), fmt.Sprintf("--> %s", constants.BinaryVersion().String()))
192194

193195
log.Println(string(stdout))
194196
}

terraform/infrastructure/azure/main.tf

Lines changed: 3 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -227,36 +227,15 @@ resource "azurerm_network_security_group" "security_group" {
227227
location = var.location
228228
resource_group_name = var.resource_group
229229
tags = local.tags
230-
231-
dynamic "security_rule" {
232-
# we keep this rule for one last release since the azurerm provider does not
233-
# support moving security rules that are inlined (like this) to the external resource one.
234-
# Even worse, just defining the azurerm_network_security_group without the
235-
# "security_rule" block will NOT remove all the rules but do nothing.
236-
# TODO(@3u13r): remove the "security_rule" block in the next release after this code has landed.
237-
# So either after 2.19 or after 2.18.X if cherry-picked release.
238-
for_each = [{ name = "konnectivity", priority = 1000, port = 8132 }]
239-
content {
240-
name = security_rule.value.name
241-
priority = security_rule.value.priority
242-
direction = "Inbound"
243-
access = "Allow"
244-
protocol = "Tcp"
245-
source_port_range = "*"
246-
destination_port_range = security_rule.value.port
247-
source_address_prefix = "*"
248-
destination_address_prefix = "*"
249-
}
250-
}
251230
}
252231

253232
resource "azurerm_network_security_rule" "nsg_rule" {
254233
for_each = {
255234
for o in local.ports : o.name => o
256235
}
257-
258-
name = each.value.name
259-
priority = each.value.priority
236+
# TODO(elchead): v2.20.0: remove name suffix and priority offset. Might need to add create_before_destroy to the NSG rule.
237+
name = "${each.value.name}-new"
238+
priority = each.value.priority + 10 # offset to not overlap with old rules
260239
direction = "Inbound"
261240
access = "Allow"
262241
protocol = "Tcp"

0 commit comments

Comments
 (0)