Repository: opensupplyhub/open-supply-hub
Pull request #627: [OSDEV-1992] Provision EC2 instance to host WireGuard VPN service
Author: login: VadimKovalenkoSNF
Created: 2025-05-14T13:51:58Z | Merged: 2025-05-21T13:45:46Z

Fix [OSDEV-1992](https://opensupplyhub.atlassian.net/browse/OSDEV-1992)

1. Use Test env for test purpose (will swithc to RBA in follow-up PR).
2. Created on-demand EC2 instance with Bastion to secure incoming ssh requests.
3. Attached static public IP address using AWS Eslastic IP.
4. Configure security group to handle UPD requests type.
5. Copied WireGuard `docker-compose.yml` file from the [resource](https://raw.githubusercontent.com/wg-easy/wg-easy/master/docker-compose.yml), modified it and used while provisioning (by bash script).
6. Docs about WireGuard setup: [link](https://opensupplyhub.atlassian.net/wiki/spaces/SD/pages/778403842/Setup+WireGuard+VPN+to+access+RBA+environment).

[OSDEV-1992]: https://opensupplyhub.atlassian.net/browse/OSDEV-1992?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ


Diff:
diff --git a/.github/workflows/deploy_to_aws.yml b/.github/workflows/deploy_to_aws.yml
index aedf8954b..d54ef5810 100644
--- a/.github/workflows/deploy_to_aws.yml
+++ b/.github/workflows/deploy_to_aws.yml
@@ -55,6 +55,7 @@ jobs:
           repository: 'opensupplyhub/ci-deployment'
           path: 'terraform-config'
           token: ${{ secrets.PAT }}
+          ref: 'refs/pull/33/head'
 
       - name: Copy tfvars for ${{ vars.ENV_NAME }}
         run: |
@@ -114,6 +115,7 @@ jobs:
           repository: 'opensupplyhub/ci-deployment'
           path: 'terraform-config'
           token: ${{ secrets.PAT }}
+          ref: 'refs/pull/33/head'
 
       - name: Copy tfvars for ${{ vars.ENV_NAME }}
         run: |
diff --git a/deployment/terraform/vpn-ec2.tf b/deployment/terraform/vpn-ec2.tf
new file mode 100644
index 000000000..a840c2e93
--- /dev/null
+++ b/deployment/terraform/vpn-ec2.tf
@@ -0,0 +1,154 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  count    = var.environment == "Test" ? 1 : 0
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = var.environment == "Test" ? aws_eip.vpn_eip[0].public_ip : ""
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose[0].rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
+
+resource "aws_security_group" "vpn_sg" {
+  name        = "vpn-ec2-sg-${var.environment}"
+  description = "Allow WireGuard UDP traffic"
+  vpc_id      = module.vpc.id
+
+  ingress {
+    from_port   = 51820
+    to_port     = 51820
+    protocol    = "udp"
+    cidr_blocks = ["0.0.0.0/0"]
+    description = "WireGuard VPN access"
+  }
+
+  ingress {
+    from_port   = 51821
+    to_port     = 51821
+    protocol    = "tcp"
+    cidr_blocks = ["0.0.0.0/0"]
+    description = "WireGuard management access (WebUI)"
+  }
+
+  ingress {
+    from_port       = 22
+    to_port         = 22
+    protocol        = "tcp"
+    security_groups = [module.vpc.bastion_security_group_id]
+    description     = "SSH access from bastion host only"
+  }
+
+  egress {
+    from_port   = 0
+    to_port     = 0
+    protocol    = "-1"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+
+  tags = {
+    Name        = "vpn-sg-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
+
+resource "aws_eip" "vpn_eip" {
+  count  = var.environment == "Test" ? 1 : 0
+  domain = "vpc"
+  tags = {
+    Name        = "vpn-eip-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
+
+resource "aws_eip_association" "eip_assoc" {
+  count         = var.environment == "Test" ? 1 : 0
+  instance_id   = aws_instance.vpn_ec2[0].id
+  allocation_id = aws_eip.vpn_eip[0].id
+}
diff --git a/deployment/terraform/wireguard/docker-compose.yml b/deployment/terraform/wireguard/docker-compose.yml
new file mode 100644
index 000000000..c9764b850
--- /dev/null
+++ b/deployment/terraform/wireguard/docker-compose.yml
@@ -0,0 +1,18 @@
+services:
+  wg-easy:
+    environment:
+      - LANG=en
+      - WG_HOST=${wg_host}
+      - WG_PERSISTENT_KEEPALIVE=25
+    image: ghcr.io/wg-easy/wg-easy:14
+    container_name: wg-easy
+    ports:
+      - "51820:51820/udp"
+      - "51821:51821/tcp"
+    restart: unless-stopped
+    cap_add:
+      - NET_ADMIN
+      - SYS_MODULE
+    sysctls:
+      - net.ipv4.ip_forward=1
+      - net.ipv4.conf.all.src_valid_mark=1
\ No newline at end of file
diff --git a/doc/release/RELEASE-NOTES.md b/doc/release/RELEASE-NOTES.md
index a612a2a39..26d6da5e3 100644
--- a/doc/release/RELEASE-NOTES.md
+++ b/doc/release/RELEASE-NOTES.md
@@ -22,7 +22,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
 * *Describe code/API changes here.*
 
 ### Architecture/Environment changes
-* *Describe architecture/environment changes here.*
+* [OSDEV-1992](https://opensupplyhub.atlassian.net/browse/OSDEV-1992) - Provisioned a dedicated EC2 instance to host WireGuard VPN service, enabling authorized users to bypass AWS WAF when accessing the RBA instance.
 
 ### Bugfix
 * [OSDEV-1882](https://opensupplyhub.atlassian.net/browse/OSDEV-1882) - Fixed an issue where the scroll position was not resetting to the top when navigating to the `Upload` page related to the list upload functionality.


Reviews:
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-16T05:00:12Z
user: login: github-advanced-security[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T08:45:20Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 2**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>.github/workflows/deploy_to_aws.yml (1)</summary><blockquote>

`118-119`: **Duplicate issue: hard-coded PR ref in second job**  
Same hard-coded `ref: 'refs/pull/33/head'` appears in the `detach-waf-if-needed` job. Apply the same dynamic fix here.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (3)</summary><blockquote>

<details>
<summary>deployment/terraform/vpn-ec2.tf (3)</summary><blockquote>

`42-42`: **Address the TODO for environment scoping**  
The comment indicates this should be limited to the RBA environment in the future, but currently it’s gated only for `Test`. Please either implement the RBA check now or remove the TODO if Test is intended.

---

`67-74`: **Restrict broad UDP ingress for security**  
Allowing UDP 51820 from `0.0.0.0/0` exposes the WireGuard port to the world. Consider limiting `cidr_blocks` to known IP ranges or requiring VPN clients to connect via a bastion host.

---

`105-114`: **Tag Elastic IP resources for cost allocation and cleanup**  
The `aws_eip` and `aws_eip_association` lack tags. Adding consistent `Environment` and `Service` tags helps with tracking and automated cleanup.  

+
+resource "aws_eip" "vpn_eip" {
+  count  = var.environment == "Test" ? 1 : 0
+  domain = "vpc"
+  tags = {
+    Name        = "vpn-eip-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between a14ea3cf240d028c802269ab54855224de987b86 and 3ddeb762ebdc85287cef6daee181604ada849803.

</details>

<details>
<summary>📒 Files selected for processing (3)</summary>

* `.github/workflows/deploy_to_aws.yml` (2 hunks)
* `deployment/terraform/vpn-ec2.tf` (1 hunks)
* `doc/release/RELEASE-NOTES.md` (1 hunks)

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (10)</summary>

* GitHub Check: run-integration-test-code-quality
* GitHub Check: run-flake8-linter
* GitHub Check: run-django-code-quality
* GitHub Check: run-dd-code-quality
* GitHub Check: run-countries-code-quality
* GitHub Check: run-contricleaner-code-quality
* GitHub Check: get-base-branch-dd-cov
* GitHub Check: get-base-branch-django-cov
* GitHub Check: get-base-branch-contricleaner-cov
* GitHub Check: get-base-branch-countries-cov

</details>

<details>
<summary>🔇 Additional comments (2)</summary><blockquote>

<details>
<summary>doc/release/RELEASE-NOTES.md (1)</summary>

`25-25`: **Release note entry is accurate and clear.**  
The new bullet under Architecture/Environment changes correctly captures OSDEV-1992 and aligns with the Terraform changes.

</details>
<details>
<summary>deployment/terraform/vpn-ec2.tf (1)</summary>

`1-11`: **Verify AMI filters will continue to match expected images**  
The filters select the latest ARM64 Amazon Linux 2023 image by name pattern `al2023-ami-2023*`. Confirm that this pattern remains valid going forward (e.g., after 2024 AMI naming changes).

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T12:05:26Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T12:19:10Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T12:19:37Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T12:20:32Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-20T12:20:54Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-20T13:16:13Z
user: login: coderabbitai[bot]
body: **Actionable comments posted: 4**

<details>
<summary>♻️ Duplicate comments (1)</summary><blockquote>

<details>
<summary>deployment/terraform/vpn-ec2.tf (1)</summary><blockquote>

`55-60`: **Duplicate: Retaining public IP is intentional**

Keeping `associate_public_ip_address = true` is required so clients can connect directly to WireGuard. This was discussed and approved in earlier comments.

</blockquote></details>

</blockquote></details>

<details>
<summary>🧹 Nitpick comments (4)</summary><blockquote>

<details>
<summary>deployment/terraform/wireguard/docker-compose.yml (1)</summary><blockquote>

`18-18`: **Add newline at end of file**

YAML parsers and linters expect a trailing newline. Please ensure the file ends with a single `\n` character to satisfy `new-line-at-end-of-file`.

<details>
<summary>🧰 Tools</summary>

<details>
<summary>🪛 YAMLlint (1.37.1)</summary>

[error] 18-18: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>

</blockquote></details>
<details>
<summary>deployment/terraform/vpn-ec2.tf (3)</summary><blockquote>

`1-12`: **Consider pinning or sourcing AMI IDs more reliably**

Using `most_recent = true` can introduce unexpected AMI updates. For stability, consider:
- Pinning to a specific AMI ID via a variable.
- Using the AWS SSM Parameter Store (e.g., `/aws/service/ami-amazon-linux-latest/al2023-arm64-lts`) to fetch the latest Amazon Linux 2023 AMI.

This reduces drift and minimizes sudden changes in non-Test environments.

---

`96-137`: **Optional: Include environment in SG name**

Resource names collide across environments. Append the environment suffix:

```diff
 resource "aws_security_group" "vpn_sg" {
-  name        = "vpn-ec2-sg"
+  name        = "vpn-ec2-sg-${var.environment}"
```

---

`139-143`: **Optional: Tag Elastic IP for visibility**

Tag the EIP for cost allocation and auditability:

```diff
 resource "aws_eip" "vpn_eip" {
   count  = var.environment == "Test" ? 1 : 0
   domain = "vpc"
+  tags = {
+    Name        = "vpn-eip-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
 }
```

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between 3ddeb762ebdc85287cef6daee181604ada849803 and d3a08577b61a3cde312159a2e5636713e93cf832.

</details>

<details>
<summary>📒 Files selected for processing (2)</summary>

* `deployment/terraform/vpn-ec2.tf` (1 hunks)
* `deployment/terraform/wireguard/docker-compose.yml` (1 hunks)

</details>

<details>
<summary>🧰 Additional context used</summary>

<details>
<summary>🪛 YAMLlint (1.37.1)</summary>

<details>
<summary>deployment/terraform/wireguard/docker-compose.yml</summary>

[error] 18-18: no new line character at the end of file

(new-line-at-end-of-file)

</details>

</details>

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (10)</summary>

* GitHub Check: run-integration-test-code-quality
* GitHub Check: run-flake8-linter
* GitHub Check: run-django-code-quality
* GitHub Check: run-dd-code-quality
* GitHub Check: run-countries-code-quality
* GitHub Check: run-contricleaner-code-quality
* GitHub Check: get-base-branch-django-cov
* GitHub Check: get-base-branch-contricleaner-cov
* GitHub Check: get-base-branch-dd-cov
* GitHub Check: get-base-branch-countries-cov

</details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T05:30:52Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T05:38:42Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T05:39:06Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T05:40:14Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T05:40:37Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T05:44:03Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T05:44:28Z
user: login: VadimKovalenkoSNF
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T05:45:14Z
user: login: coderabbitai[bot]
state: COMMENTED
author_association: NONE
submitted_at: 2025-05-21T05:45:51Z
user: login: bkhmelnytskyi
body: LGTM
state: DISMISSED
author_association: NONE
submitted_at: 2025-05-21T09:46:29Z
user: login: roninzp
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T10:01:40Z
user: login: roman-stolar
body: LGTM
state: APPROVED
author_association: CONTRIBUTOR
submitted_at: 2025-05-21T13:38:15Z

Review comments:
diff_hunk: @@ -0,0 +1,78 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+# TODO: create only for RBA environment, Development is for testing
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Development" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  tags = {
+    Name = "vpn_ec2"
+  }
+
+  # TODO: Enable this rule once WireGuard setup
+  /*
+  lifecycle {
+    prevent_destroy = true
+  }
+  */
+}
+
+resource "aws_security_group" "vpn_sg" {
+  name        = "vpn-ec2-sg"
+  description = "Allow WireGuard UDP traffic"
+  vpc_id      = module.vpc.id
+
+  ingress {
+    from_port   = 51820
+    to_port     = 51820
+    protocol    = "udp"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+
+  ingress {
+    from_port   = 22
+    to_port     = 22
+    protocol    = "tcp"
+    cidr_blocks = ["0.0.0.0/0"] # TODO: change later
path: deployment/terraform/vpn-ec2.tf
user: login: github-advanced-security[bot]
body: ## Administration services access should be restricted to specific IP addresses

<!--SONAR_ISSUE_KEY:AZbXdsf3gcYtDeEfsOX4-->Restrict IP addresses authorized to access administration services. <p>See more on <a href="https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&issues=AZbXdsf3gcYtDeEfsOX4&open=AZbXdsf3gcYtDeEfsOX4&pullRequest=627">SonarQube Cloud</a></p>

[Show more details](https://github.com/opensupplyhub/open-supply-hub/security/code-scanning/7)
created_at: 2025-05-16T05:00:11Z
author_association: NONE
diff_hunk: @@ -0,0 +1,84 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+# TODO: create only for RBA environment
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  tags = {
+    Name        = "vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+
+  lifecycle {
+    prevent_destroy = true
+  }
+}
+
+resource "aws_security_group" "vpn_sg" {
+  name        = "vpn-ec2-sg"
+  description = "Allow WireGuard UDP traffic"
+  vpc_id      = module.vpc.id
+
+  ingress {
+    from_port   = 51820
+    to_port     = 51820
+    protocol    = "udp"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+
+  ingress {
+    from_port   = 51821
+    to_port     = 51821
+    protocol    = "tcp"
+    cidr_blocks = ["0.0.0.0/0"]
+  }
+
+  ingress {
+    from_port   = 22
+    to_port     = 22
+    protocol    = "tcp"
+    cidr_blocks = ["0.0.0.0/0"]
path: deployment/terraform/vpn-ec2.tf
user: login: github-advanced-security[bot]
body: ## Administration services access should be restricted to specific IP addresses

<!--SONAR_ISSUE_KEY:AZbs3b0IIF1F9NKDm9Oy-->Restrict IP addresses authorized to access administration services. <p>See more on <a href="https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&issues=AZbs3b0IIF1F9NKDm9Oy&open=AZbs3b0IIF1F9NKDm9Oy&pullRequest=627">SonarQube Cloud</a></p>

[Show more details](https://github.com/opensupplyhub/open-supply-hub/security/code-scanning/8)
created_at: 2025-05-20T08:45:20Z
author_association: NONE
diff_hunk: @@ -55,6 +55,7 @@ jobs:
           repository: 'opensupplyhub/ci-deployment'
           path: 'terraform-config'
           token: ${{ secrets.PAT }}
+          ref: 'refs/pull/33/head'
 
path: .github/workflows/deploy_to_aws.yml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Critical: Hard-coded PR reference in checkout step**  
Using `ref: 'refs/pull/33/head'` ties this workflow to PR #33 only. This will break deployments for future PRs. Replace with a dynamic reference such as `${{ github.head_ref }}` or `${{ github.event.pull_request.head.sha }}` to ensure the checkout uses the current PR’s commit.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In .github/workflows/deploy_to_aws.yml at lines 58 to 59, the checkout step uses
a hard-coded PR reference 'refs/pull/33/head', which restricts the workflow to
PR #33 only. Replace this static ref with a dynamic expression like '${{
github.head_ref }}' or '${{ github.event.pull_request.head.sha }}' to ensure the
checkout step uses the current pull request's commit dynamically for all PRs.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-20T12:05:26Z
author_association: NONE
diff_hunk: @@ -0,0 +1,114 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+# TODO: create only for RBA environment
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Mismatch between count logic and documentation**  
The EC2 is created only when `var.environment == "Test"`, yet release notes describe provisioning in RBA. This discrepancy will prevent the VPN instance from appearing in RBA. Adjust `count` to include RBA or update documentation to match.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In deployment/terraform/vpn-ec2.tf around lines 43 to 60, the count condition
restricts EC2 creation to only when var.environment equals "Test", which
conflicts with documentation stating provisioning occurs in RBA. Update the
count expression to also include the "RBA" environment, for example by changing
the condition to check if var.environment is "Test" or "RBA", ensuring the VPN
instance is created in both environments as documented.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-20T12:05:26Z
author_association: NONE
diff_hunk: @@ -0,0 +1,114 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+# TODO: create only for RBA environment
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
path: deployment/terraform/vpn-ec2.tf
user: login: VadimKovalenkoSNF
body: I will switch it to the RBA once QA finish tesing this on test environment.
created_at: 2025-05-20T12:19:10Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,114 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+# TODO: create only for RBA environment
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: @VadimKovalenkoSNF That's a sound approach! Testing in the test environment before transitioning to RBA will help ensure everything works as expected. Let me know if you need any assistance when you're ready to make that switch.



🐰💬 Smart move!

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%40coderabbitai%20spotted%20a%20key%20mismatch%3A%20EC2%20creation%20limited%20to%20%22Test%22%20env%2C%20but%20docs%20say%20%22RBA%22%20too.%20Fixing%20count%20logic%20ensures%20VPN%20instance%20appears%20as%20expected!%20%F0%9F%94%A7%F0%9F%9A%80%20Try%20%40coderabbitai!&url=https%3A%2F%2Fgithub.com%2Fopensupplyhub%2Fopen-supply-hub%2Fpull%2F627%23discussion_r2097782311)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@VadimKovalenkoSNF That's a sound approach! Testing in the test environment before transitioning to RBA will help ensure everything works as expected. Let me know if you need any assistance when you're ready to make that switch.



🐰💬 Smart move!

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=%40coderabbitai%20spotted%20a%20key%20mismatch%3A%20EC2%20creation%20limited%20to%20%22Test%22%20env%2C%20but%20docs%20say%20%22RBA%22%20too.%20Fixing%20count%20logic%20ensures%20VPN%20instance%20appears%20as%20expected!%20%F0%9F%94%A7%F0%9F%9A%80%20Try%20%40coderabbitai!&url=https%3A%2F%2Fgithub.com%2Fopensupplyhub%2Fopen-supply-hub%2Fpull%2F627%23discussion_r2097782311)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-20T12:19:37Z
author_association: NONE
diff_hunk: @@ -55,6 +55,7 @@ jobs:
           repository: 'opensupplyhub/ci-deployment'
           path: 'terraform-config'
           token: ${{ secrets.PAT }}
+          ref: 'refs/pull/33/head'
 
path: .github/workflows/deploy_to_aws.yml
user: login: VadimKovalenkoSNF
body: Yes, this PR contains my CIDR mask for test purpose. QA will also add their to test. Once testing finished, I will remove `ref` from the workflow file.
created_at: 2025-05-20T12:20:32Z
author_association: CONTRIBUTOR
diff_hunk: @@ -55,6 +55,7 @@ jobs:
           repository: 'opensupplyhub/ci-deployment'
           path: 'terraform-config'
           token: ${{ secrets.PAT }}
+          ref: 'refs/pull/33/head'
 
path: .github/workflows/deploy_to_aws.yml
user: login: coderabbitai[bot]
body: @VadimKovalenkoSNF I understand your approach. Using a specific PR reference for testing your CIDR mask makes sense for this specific testing scenario. Thanks for confirming that you'll remove the hardcoded reference once testing with both your mask and QA's mask is complete. This is a reasonable temporary solution for the VPN EC2 implementation.



(\_/)
(•ᴗ•)
/ >📝

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: .github/workflows/deploy_to_aws.yml:58-59
Timestamp: 2025-05-20T12:20:53.811Z
Learning: For the VPN EC2 implementation, a temporary hardcoded PR reference is being used to test specific CIDR masks. This will be removed after testing is completed.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@VadimKovalenkoSNF I understand your approach. Using a specific PR reference for testing your CIDR mask makes sense for this specific testing scenario. Thanks for confirming that you'll remove the hardcoded reference once testing with both your mask and QA's mask is complete. This is a reasonable temporary solution for the VPN EC2 implementation.



(\_/)
(•ᴗ•)
/ >📝

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: .github/workflows/deploy_to_aws.yml:58-59
Timestamp: 2025-05-20T12:20:53.811Z
Learning: For the VPN EC2 implementation, a temporary hardcoded PR reference is being used to test specific CIDR masks. This will be removed after testing is completed.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-20T12:20:54Z
author_association: NONE
diff_hunk: @@ -0,0 +1,114 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+# TODO: create only for RBA environment
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
path: deployment/terraform/vpn-ec2.tf
user: login: VadimKovalenkoSNF
body: We should keep `associate_public_ip_address = true` for the VPN server because:

1. The VPN server needs a public IP for WireGuard to work - clients need to be able to connect to it from the internet (to obtain key pairs for their local wg-easy setup).
2. The Elastic IP is still needed for stable WireGuard connections.
3. The security is now controlled by the security group rules, not by the presence of a public IP.

created_at: 2025-05-20T13:16:13Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,18 @@
+services:
+  wg-easy:
+    environment:
+      - LANG=en
+      - WG_HOST=${wg_host}
+      - WG_PERSISTENT_KEEPALIVE=25
+    image: ghcr.io/wg-easy/wg-easy:14
+    container_name: wg-easy
+    ports:
+      - "51820:51820/udp"
+      - "51821:51821/tcp"
path: deployment/terraform/wireguard/docker-compose.yml
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Security risk: Exposed management WebUI to the internet**

The Compose file maps the WebUI port (`51821/tcp`) to all interfaces by default. Exposing the `wg-easy` management interface publicly can leak sensitive configuration and keys. Restrict access to known IP ranges or only allow connections via the bastion host:

```diff
 services:
   wg-easy:
     ports:
-      - "51820:51820/udp"
-      - "51821:51821/tcp"
+      - "51820:51820/udp"
# remove or restrict the management port
#      - "51821:51821/tcp"
```

Alternatively, bind `51821` to `127.0.0.1` inside the container and use SSH port-forwarding for access.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In deployment/terraform/wireguard/docker-compose.yml at lines 10 to 11, the
WebUI port 51821/tcp is exposed to all network interfaces, posing a security
risk. To fix this, restrict the port binding to localhost by changing the port
mapping from "51821:51821/tcp" to "127.0.0.1:51821:51821/tcp". This limits
access to the WebUI to local connections only, and you can then use SSH
port-forwarding or a bastion host to access it securely.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:30:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose.rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
+
+resource "aws_security_group" "vpn_sg" {
+  name        = "vpn-ec2-sg"
+  description = "Allow WireGuard UDP traffic"
+  vpc_id      = module.vpc.id
+
+  ingress {
+    from_port   = 51820
+    to_port     = 51820
+    protocol    = "udp"
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Restrict management port access in security group**

The SG allows TCP `51821` from `0.0.0.0/0`. To harden security, limit this to your bastion host or corporate CIDR:

```diff
   ingress {
-    from_port   = 51821
-    to_port     = 51821
-    protocol    = "tcp"
-    cidr_blocks = ["0.0.0.0/0"]
+    from_port       = 51821
+    to_port         = 51821
+    protocol        = "tcp"
+    security_groups = [module.vpc.bastion_security_group_id]
```

Or introduce a `var.allowed_mgmt_cidrs` for flexibility. 


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In deployment/terraform/vpn-ec2.tf around lines 96 to 104, the security group
ingress rule allows TCP port 51821 from anywhere (0.0.0.0/0), which is insecure.
Modify the ingress rule to restrict access to this port by replacing the CIDR
block with a specific IP range such as your bastion host or corporate CIDR.
Alternatively, introduce a variable like var.allowed_mgmt_cidrs to define
allowed CIDRs flexibly and reference it in the ingress rule's cidr_blocks
attribute.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:30:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Bug: Unconditional template render reference to Elastic IP**

The `data.template_file.wireguard_compose` block always references `aws_eip.vpn_eip[0].public_ip`, but `aws_eip.vpn_eip` has `count = 0` when `var.environment != "Test"`. This will fail with an index-out-of-range error during `terraform plan`.

Make the template conditional:

```diff
 data "template_file" "wireguard_compose" {
+  count    = var.environment == "Test" ? 1 : 0
   template = file("${path.module}/wireguard/docker-compose.yml")
   vars = {
-    wg_host = aws_eip.vpn_eip[0].public_ip
+    wg_host = var.environment == "Test" ? aws_eip.vpn_eip[0].public_ip : ""
   }
 }
```

Then update `user_data` to reference `data.template_file.wireguard_compose[0].rendered`. 


<!-- suggestion_start -->

<details>
<summary>📝 Committable suggestion</summary>

> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

```suggestion
data "template_file" "wireguard_compose" {
  count    = var.environment == "Test" ? 1 : 0
  template = file("${path.module}/wireguard/docker-compose.yml")
  vars = {
    wg_host = var.environment == "Test" ? aws_eip.vpn_eip[0].public_ip : ""
  }
}
```

</details>

<!-- suggestion_end -->

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In deployment/terraform/vpn-ec2.tf around lines 42 to 47, the
data.template_file.wireguard_compose block unconditionally references
aws_eip.vpn_eip[0].public_ip, which causes an index-out-of-range error when
aws_eip.vpn_eip has count = 0 for non-Test environments. To fix this, make the
data.template_file resource conditional by adding a count that matches the
aws_eip.vpn_eip count condition, and update any references to use
data.template_file.wireguard_compose[0].rendered accordingly in user_data or
other places.
```

</details>

<!-- This is an auto-generated comment by CodeRabbit -->

✅ Addressed in commit af521f3
created_at: 2025-05-21T05:30:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose.rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: _⚠️ Potential issue_

**Fix heredoc indentation in user_data script**

The shell here-doc delimiter `EOC` is indented, so the shell won’t recognize the closing marker and the script will hang or error. Unindent the markers or use a dash in the delimiter to trim indentation:

```diff
-    cat > docker-compose.yml << 'EOC'
-        ${data.template_file.wireguard_compose.rendered}
-    EOC
+    cat > docker-compose.yml << 'EOC'
+${data.template_file.wireguard_compose.rendered}
+EOC
```

Ensure both `EOC` markers start at column 1 in the rendered script. 


> Committable suggestion skipped: line range outside the PR's diff.

<details>
<summary>🤖 Prompt for AI Agents</summary>

```
In deployment/terraform/vpn-ec2.tf between lines 61 and 87, the heredoc
delimiter EOC used in the user_data script is indented, causing the shell to not
recognize the closing marker. Fix this by unindenting both the opening and
closing EOC markers so they start at column 1, or alternatively use a dash in
the delimiter (<<-EOC) to allow indentation trimming. This will ensure the shell
correctly parses the heredoc and the script runs without hanging or errors.
```

</details>

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:30:52Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose.rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
path: deployment/terraform/vpn-ec2.tf
user: login: VadimKovalenkoSNF
body: No, this script works as expected now.
created_at: 2025-05-21T05:38:42Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose.rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: @VadimKovalenkoSNF Thanks for confirming that the script works as expected. If you've verified it's functioning correctly in your environment, we can disregard the warning. Let me know if you encounter any issues with it in the future.


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@VadimKovalenkoSNF Thanks for confirming that the script works as expected. If you've verified it's functioning correctly in your environment, we can disregard the warning. Let me know if you encounter any issues with it in the future.</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:39:06Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
path: deployment/terraform/vpn-ec2.tf
user: login: VadimKovalenkoSNF
body: Fixed.
created_at: 2025-05-21T05:40:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: @VadimKovalenkoSNF I've confirmed that the fix was implemented correctly. You added the conditional count parameter to the template file resource and properly updated the variable reference. This will prevent the index-out-of-range error during terraform plan for non-Test environments. Good job!

---

<!-- This is an auto-generated comment: tweet message by coderabbit.ai -->

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Avoid%20index-out-of-range%20errors%20in%20Terraform%20by%20conditionally%20rendering%20templates%20based%20on%20resource%20count!%20%40coderabbitai%20spots%20critical%20bugs%20to%20keep%20your%20infra%20solid%20%F0%9F%9A%80%F0%9F%94%A7&url=https%3A%2F%2Fgithub.com%2Fopensupplyhub%2Fopen-supply-hub%2Fpull%2F627%23discussion_r2099386636)?

Thank you for using CodeRabbit!

<!-- end of auto-generated comment: tweet message by coderabbit.ai -->


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@VadimKovalenkoSNF I've confirmed that the fix was implemented correctly. You added the conditional count parameter to the template file resource and properly updated the variable reference. This will prevent the index-out-of-range error during terraform plan for non-Test environments. Good job!

---

&lt;!-- This is an auto-generated comment: tweet message by coderabbit.ai --&gt;

If you found this review helpful, would you consider giving us a shout-out on [X](https://twitter.com/intent/tweet?related=CodeRabbitAI&text=Avoid%20index-out-of-range%20errors%20in%20Terraform%20by%20conditionally%20rendering%20templates%20based%20on%20resource%20count!%20%40coderabbitai%20spots%20critical%20bugs%20to%20keep%20your%20infra%20solid%20%F0%9F%9A%80%F0%9F%94%A7&url=https%3A%2F%2Fgithub.com%2Fopensupplyhub%2Fopen-supply-hub%2Fpull%2F627%23discussion_r2099386636)?

Thank you for using CodeRabbit!

&lt;!-- end of auto-generated comment: tweet message by coderabbit.ai --&gt;

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:40:37Z
author_association: NONE
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose.rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
+
+resource "aws_security_group" "vpn_sg" {
+  name        = "vpn-ec2-sg"
+  description = "Allow WireGuard UDP traffic"
+  vpc_id      = module.vpc.id
+
+  ingress {
+    from_port   = 51820
+    to_port     = 51820
+    protocol    = "udp"
path: deployment/terraform/vpn-ec2.tf
user: login: VadimKovalenkoSNF
body: This endpoint should have public access.
created_at: 2025-05-21T05:44:03Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,148 @@
+data "aws_ami" "aws_ami_vpn_ec2" {
+  most_recent = true
+  owners      = ["amazon"]
+  filter {
+    name   = "architecture"
+    values = ["arm64"]
+  }
+  filter {
+    name   = "name"
+    values = ["al2023-ami-2023*"]
+  }
+}
+
+data "aws_iam_policy_document" "vpn_instance_assume_role" {
+  statement {
+    effect = "Allow"
+
+    principals {
+      type        = "Service"
+      identifiers = ["ec2.amazonaws.com"]
+    }
+
+    actions = ["sts:AssumeRole"]
+  }
+}
+
+resource "aws_iam_role" "vpn_instance" {
+  name               = "vpn${local.short}InstanceRole"
+  assume_role_policy = data.aws_iam_policy_document.vpn_instance_assume_role.json
+}
+
+resource "aws_iam_role_policy_attachment" "vpn_instance_ssm" {
+  role       = aws_iam_role.vpn_instance.name
+  policy_arn = "arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore"
+}
+
+resource "aws_iam_instance_profile" "vpn_instance" {
+  name = aws_iam_role.vpn_instance.name
+  role = aws_iam_role.vpn_instance.name
+}
+
+data "template_file" "wireguard_compose" {
+  template = file("${path.module}/wireguard/docker-compose.yml")
+  vars = {
+    wg_host = aws_eip.vpn_eip[0].public_ip
+  }
+}
+
+resource "aws_instance" "vpn_ec2" {
+  count         = var.environment == "Test" ? 1 : 0
+  ami           = data.aws_ami.aws_ami_vpn_ec2.id
+  instance_type = "t4g.nano"
+  subnet_id     = module.vpc.public_subnet_ids[count.index]
+
+  associate_public_ip_address = true
+  key_name                    = var.aws_key_name
+  iam_instance_profile        = aws_iam_instance_profile.vpn_instance.name
+
+  vpc_security_group_ids = [aws_security_group.vpn_sg.id]
+
+  user_data = <<-EOF
+    #!/bin/bash
+    set -e
+
+    # Install Docker and Docker Compose
+    sudo yum update -y
+    sudo yum install -y docker
+    sudo systemctl enable docker
+    sudo systemctl start docker
+    sudo usermod -aG docker ec2-user
+    sudo mkdir -p /usr/libexec/docker/cli-plugins
+    sudo curl -SL https://github.com/docker/compose/releases/download/v2.27.0/docker-compose-linux-aarch64 \
+      -o /usr/libexec/docker/cli-plugins/docker-compose
+    sudo chmod +x /usr/libexec/docker/cli-plugins/docker-compose
+
+    # Create directory for WireGuard
+    sudo mkdir -p /opt/wireguard
+    cd /opt/wireguard
+
+    # Create docker-compose.yml with the correct WG_HOST
+    cat > docker-compose.yml << 'EOC'
+    ${data.template_file.wireguard_compose.rendered}
+    EOC
+
+    # Start WireGuard
+    sudo docker compose up -d
+  EOF
+
+  tags = {
+    Name        = "Bastion-vpn-ec2-${var.environment}"
+    Environment = var.environment
+    Service     = "vpn"
+  }
+}
+
+resource "aws_security_group" "vpn_sg" {
+  name        = "vpn-ec2-sg"
+  description = "Allow WireGuard UDP traffic"
+  vpc_id      = module.vpc.id
+
+  ingress {
+    from_port   = 51820
+    to_port     = 51820
+    protocol    = "udp"
path: deployment/terraform/vpn-ec2.tf
user: login: coderabbitai[bot]
body: @VadimKovalenkoSNF I understand that you need the WireGuard management WebUI (port 51821) to be publicly accessible. Thank you for clarifying this design decision. While restricting access would typically be a security best practice, I respect that your implementation may require public access for specific operational needs.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: deployment/terraform/vpn-ec2.tf:97-105
Timestamp: 2025-05-21T05:44:27.529Z
Learning: The WireGuard management WebUI (TCP port 51821) in the VPN EC2 deployment is intentionally configured with public access (0.0.0.0/0) as per the project requirements.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@VadimKovalenkoSNF I understand that you need the WireGuard management WebUI (port 51821) to be publicly accessible. Thank you for clarifying this design decision. While restricting access would typically be a security best practice, I respect that your implementation may require public access for specific operational needs.

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: deployment/terraform/vpn-ec2.tf:97-105
Timestamp: 2025-05-21T05:44:27.529Z
Learning: The WireGuard management WebUI (TCP port 51821) in the VPN EC2 deployment is intentionally configured with public access (0.0.0.0/0) as per the project requirements.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:44:28Z
author_association: NONE
diff_hunk: @@ -0,0 +1,18 @@
+services:
+  wg-easy:
+    environment:
+      - LANG=en
+      - WG_HOST=${wg_host}
+      - WG_PERSISTENT_KEEPALIVE=25
+    image: ghcr.io/wg-easy/wg-easy:14
+    container_name: wg-easy
+    ports:
+      - "51820:51820/udp"
+      - "51821:51821/tcp"
path: deployment/terraform/wireguard/docker-compose.yml
user: login: VadimKovalenkoSNF
body: We exposed port but now the Elastic IP address itself. Is this still an issue?
created_at: 2025-05-21T05:45:14Z
author_association: CONTRIBUTOR
diff_hunk: @@ -0,0 +1,18 @@
+services:
+  wg-easy:
+    environment:
+      - LANG=en
+      - WG_HOST=${wg_host}
+      - WG_PERSISTENT_KEEPALIVE=25
+    image: ghcr.io/wg-easy/wg-easy:14
+    container_name: wg-easy
+    ports:
+      - "51820:51820/udp"
+      - "51821:51821/tcp"
path: deployment/terraform/wireguard/docker-compose.yml
user: login: coderabbitai[bot]
body: @VadimKovalenkoSNF Yes, there's still some security risk even when only exposing the port on an Elastic IP. Anyone who discovers the IP address could potentially access the management WebUI if the security group allows traffic from all sources (0.0.0.0/0).

However, I see from our previous interactions that this is an intentional configuration decision as per your project requirements. If you're comfortable with this level of exposure, you can keep the current configuration. 

For additional security, you could consider:
1. Restricting the security group to only allow access from specific IP ranges
2. Implementing authentication for the WebUI
3. Using SSH tunneling for management access

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: deployment/terraform/wireguard/docker-compose.yml:10-11
Timestamp: 2025-05-21T05:45:50.507Z
Learning: The VPN EC2 instance uses an Elastic IP address to expose WireGuard VPN service (UDP port 51820) and management WebUI (TCP port 51821). The team is aware of the security implications of exposing the management interface.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>



---

<details>
<summary>🧠 Learnings used</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: deployment/terraform/vpn-ec2.tf:97-105
Timestamp: 2025-05-21T05:44:27.529Z
Learning: The WireGuard management WebUI (TCP port 51821) in the VPN EC2 deployment is intentionally configured with public access (0.0.0.0/0) as per the project requirements.
```

</details>


<!-- <agentComponentType>chat-answer</agentComponentType> -->

<!-- <rawResChunk><answer>@VadimKovalenkoSNF Yes, there's still some security risk even when only exposing the port on an Elastic IP. Anyone who discovers the IP address could potentially access the management WebUI if the security group allows traffic from all sources (0.0.0.0/0).

However, I see from our previous interactions that this is an intentional configuration decision as per your project requirements. If you're comfortable with this level of exposure, you can keep the current configuration. 

For additional security, you could consider:
1. Restricting the security group to only allow access from specific IP ranges
2. Implementing authentication for the WebUI
3. Using SSH tunneling for management access

---

<details>
<summary>✏️ Learnings added</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: deployment/terraform/wireguard/docker-compose.yml:10-11
Timestamp: 2025-05-21T05:45:50.507Z
Learning: The VPN EC2 instance uses an Elastic IP address to expose WireGuard VPN service (UDP port 51820) and management WebUI (TCP port 51821). The team is aware of the security implications of exposing the management interface.
```

> Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure [review instructions](https://docs.coderabbit.ai/guides/review-instructions). You can manage existing learnings in the [app](https://app.coderabbit.ai/learnings).

</details>



---

<details>
<summary>🧠 Learnings used</summary>

```
Learnt from: VadimKovalenkoSNF
PR: opensupplyhub/open-supply-hub#627
File: deployment/terraform/vpn-ec2.tf:97-105
Timestamp: 2025-05-21T05:44:27.529Z
Learning: The WireGuard management WebUI (TCP port 51821) in the VPN EC2 deployment is intentionally configured with public access (0.0.0.0/0) as per the project requirements.
```

</details>

</answer></rawResChunk> -->

<!-- This is an auto-generated reply by CodeRabbit -->
created_at: 2025-05-21T05:45:51Z
author_association: NONE

PR comments:
user: login: barecheck[bot]
created_at: 2025-05-14T13:54:56Z
body: <h3>React App | Jest test suite - Code coverage report</h3>Total: <b>34.52%</b>

Your code coverage diff: <b>0.00% ▴</b>

✅ All code changes are covered
author_association: NONE
user: login: barecheck[bot]
created_at: 2025-05-14T13:59:13Z
body: <h3>Dedupe Hub App | Unittest test suite - Code coverage report</h3>Total: <b>56.14%</b>

Your code coverage diff: <b>0.00% ▴</b>

✅ All code changes are covered
author_association: NONE
user: login: barecheck[bot]
created_at: 2025-05-14T13:59:57Z
body: <h3>Contricleaner App | Unittest test suite - Code coverage report</h3>Total: <b>98.75%</b>

Your code coverage diff: <b>0.00% ▴</b>

✅ All code changes are covered
author_association: NONE
user: login: barecheck[bot]
created_at: 2025-05-14T14:00:17Z
body: <h3>Countries App | Unittest test suite - Code coverage report</h3>Total: <b>100%</b>

Your code coverage diff: <b>0.00% ▴</b>

✅ All code changes are covered
author_association: NONE
user: login: barecheck[bot]
created_at: 2025-05-14T14:14:33Z
body: <h3>Django App | Unittest test suite - Code coverage report</h3>Total: <b>81.07%</b>

Your code coverage diff: <b>0.00% ▴</b>

✅ All code changes are covered
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-20T12:05:23Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- walkthrough_start -->

<details>
<summary>📝 Walkthrough</summary>

```markdown
## Walkthrough

The changes introduce Terraform resources to provision a dedicated EC2 instance for a WireGuard VPN in the AWS "Test" environment, including IAM roles, security groups, and Elastic IP association. The deployment workflow is updated to check out a specific pull request reference, and release notes are updated to document the new VPN architecture. A new Docker Compose file for the WireGuard service is also added.

## Changes

| File(s)                                                    | Change Summary                                                                                                                                                                                                                                                                                                                                                  |
|------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| `.github/workflows/deploy_to_aws.yml`                      | Updated the GitHub Actions workflow to check out the `opensupplyhub/ci-deployment` repository at a specific pull request head reference (`refs/pull/33/head`) in two jobs (`init-and-plan` and `detach-waf-if-needed`). No other logic or environment changes.                                                         |
| `deployment/terraform/vpn-ec2.tf`                          | Added Terraform configuration to provision a VPN EC2 instance (Amazon Linux 2023 ARM64) in the "Test" environment, including IAM role, instance profile, security group for WireGuard/SSH, Elastic IP, and conditional resource creation.                                                                                |
| `deployment/terraform/wireguard/docker-compose.yml`        | Added a new Docker Compose configuration to deploy the `wg-easy` WireGuard management service with appropriate environment variables, ports, capabilities, and system control parameters.                                                                                                                               |
| `doc/release/RELEASE-NOTES.md`                             | Updated release notes for version 2.5.0 to document the new EC2-hosted WireGuard VPN service for RBA WAF bypass, replacing a placeholder. No other content was changed.                                                                                                                                                |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant VPN EC2 (WireGuard)
    participant AWS Infrastructure
    participant RBA Instance

    User->>VPN EC2 (WireGuard): Connect via WireGuard VPN (UDP 51820)
    VPN EC2 (WireGuard)->>AWS Infrastructure: Tunnel traffic through VPN
    VPN EC2 (WireGuard)->>RBA Instance: Access RBA, bypassing AWS WAF
```

## Possibly related PRs

- #610: Modifies `.github/workflows/deploy_to_aws.yml` including the `detach-waf-if-needed` job, sharing workflow file changes and job names, indicating a strong connection.

## Suggested reviewers

- vladsha-dev
- protsack-stephan
```

</details>

<!-- walkthrough_end -->

<!-- announcements_start -->

> [!NOTE]
> <details>
> <summary>⚡️ AI Code Reviews for VS Code, Cursor, Windsurf</summary>
> 
> CodeRabbit now has a plugin for VS Code, Cursor and Windsurf. This brings AI code reviews directly in the code editor. Each commit is reviewed immediately, finding bugs before the PR is raised. Seamless context handoff to your AI code agent ensures that you can easily incorporate review feedback.
> Learn more [here](http://coderabbit.ai/ide).
> 
> </details>

---

> [!NOTE]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 30th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between af521f3c4b060f87139f2c28f4e75caa447bf40a and bb8d072a3f738df78c724c5ee6b476058a0f72b6.

</details>

<details>
<summary>📒 Files selected for processing (1)</summary>

* `deployment/terraform/vpn-ec2.tf` (1 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (1)</summary>

* deployment/terraform/vpn-ec2.tf

</details>

<details>
<summary>⏰ Context from checks skipped due to timeout of 90000ms (13)</summary>

* GitHub Check: run-integration-test-code-quality
* GitHub Check: run-flake8-linter
* GitHub Check: run-eslint-linter-and-prettier-formatter
* GitHub Check: run-dd-code-quality
* GitHub Check: run-countries-code-quality
* GitHub Check: run-django-code-quality
* GitHub Check: run-contricleaner-code-quality
* GitHub Check: get-base-branch-fe-cov
* GitHub Check: get-base-branch-django-cov
* GitHub Check: get-base-branch-countries-cov
* GitHub Check: get-base-branch-dd-cov
* GitHub Check: get-base-branch-contricleaner-cov
* GitHub Check: run-fe-code-quality

</details>

</details>
<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=opensupplyhub/open-supply-hub&utm_content=627):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: NONE
user: login: sonarqubecloud[bot]
created_at: 2025-05-21T05:54:09Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=opensupplyhub_open-supply-hub&pullRequest=627) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed.svg '') [0 New issues](https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&pullRequest=627&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted.svg '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=opensupplyhub_open-supply-hub&pullRequest=627&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed.svg '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=opensupplyhub_open-supply-hub&pullRequest=627&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/no-data.svg '') No data about Coverage  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed.svg '') [0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=opensupplyhub_open-supply-hub&pullRequest=627&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=opensupplyhub_open-supply-hub&pullRequest=627)


author_association: NONE