Skip to content

Added jumpbox for Azure Prod DB - #966

Open
SanderBuSu wants to merge 1 commit into
devfrom
feat(infra)/jumpbox-setup
Open

Added jumpbox for Azure Prod DB#966
SanderBuSu wants to merge 1 commit into
devfrom
feat(infra)/jumpbox-setup

Conversation

@SanderBuSu

Copy link
Copy Markdown

##Proposed changes
This PR introduces a secure VM jumpbox infrastructure for administering production database resources without exposing them to the public internet.

What's being added:

  • Dedicated jumpbox VM in Azure with secure SSH access
    -Private network integration with existing production VNet and MySQL Flexible Server
  • Isolated Terraform state (jumpbox.tfstate) separate from main Lepton infrastructure
  • Cloud-init automation for initial VM setup and configuration

Why this is needed:
The team creating the new backend for TIHLDE need real data from the production database to test there application.
The jumpbox provides a secure, auditable, and convenient way to access production database and other internal resources through a hardened VM within the private network.

Key features:
Network isolation: Deployed in its own subnet within the prod VNet with NSG rules
SSH-only access to the VM: No RDP or unnecessary services exposed
Managed identity support: Uses Azure AD authentication for state management (use_azuread_auth = true)
Infrastructure as Code: Fully versioned and reproducible with Terraform
Separate lifecycle: Can be destroyed/recreated without affecting main Lepton infrastructure
Nginx config to forward traffic to the database

##Further comments
Architecture decisions:

  1. Why a separate Terraform workspace?
    Isolated state file prevents accidental changes to production infrastructure
    Independent lifecycle - can spin up/down the jumpbox without touching main app
    Different update cadence - jumpbox updates don't require app deployments

  2. Why a VM instead of Azure Bastion?
    Cost-effective: Azure Bastion charges ~$140/month; a small VM is ~$15-30/month
    More flexible: Can install database clients, run scripts, use as admin workstation
    Familiar tooling: Standard SSH access that dev team already uses

  3. Security considerations:
    Jumpbox placed in private subnet with restricted NSG rules
    Only SSH traffic allowed (port 22)
    SSH key-based authentication (no passwords) to VM
    Cloud-init script handles initial configuration
    The script opens for direct connection to the DB, no SSH key is needed for DB access
    Can be stopped when not in use to reduce costs and attack surface

  4. Alternatives considered:
    Azure Bastion: Too expensive for occasional admin tasks
    VPN Gateway: Overkill for database admin; requires client config on all machines
    Public database endpoint: Requiers rebuild of the database

Comment thread jumpbox/main.tf
Comment on lines +47 to +52
resource "azurerm_subnet" "jumpbox" {
name = "${var.name}-subnet"
resource_group_name = data.azurerm_resource_group.prod.name
virtual_network_name = data.azurerm_virtual_network.prod_vnet.name
address_prefixes = ["10.0.32.0/24"] # sørg for at dette er ledig i VNet'et
}

Check failure

Code scanning / checkov

Ensure VNET subnet is configured with a Network Security Group (NSG) Error

Ensure VNET subnet is configured with a Network Security Group (NSG)
Comment thread jumpbox/main.tf
Comment on lines +71 to +83
resource "azurerm_network_security_rule" "allow_ssh" {
name = "Allow-SSH"
priority = 100
direction = "Inbound"
access = "Allow"
protocol = "Tcp"
source_port_range = "*"
destination_port_range = "22"
source_address_prefix = "*"
destination_address_prefix = "*"
resource_group_name = azurerm_resource_group.rg.name
network_security_group_name = azurerm_network_security_group.nsg.name
}

Check failure

Code scanning / checkov

Ensure that SSH access is restricted from the internet Error

Ensure that SSH access is restricted from the internet
Comment thread jumpbox/main.tf
Comment on lines +101 to +114
resource "azurerm_network_interface" "nic" {
name = "${var.name}-nic"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name

ip_configuration {
name = "ipcfg"
subnet_id = azurerm_subnet.jumpbox.id
private_ip_address_allocation = "Dynamic"
public_ip_address_id = azurerm_public_ip.pip.id
}

tags = local.common_tags
}

Check failure

Code scanning / checkov

Ensure that Network Interfaces don't use public IPs Error

Ensure that Network Interfaces don't use public IPs
Comment thread jumpbox/main.tf
Comment on lines +124 to +161
resource "azurerm_linux_virtual_machine" "vm" {
name = "${var.name}-vm"
location = azurerm_resource_group.rg.location
resource_group_name = azurerm_resource_group.rg.name
network_interface_ids = [azurerm_network_interface.nic.id]
size = var.jumpbox_vm_size
admin_username = var.admin_username
disable_password_authentication = true

dynamic "admin_ssh_key" {
for_each = toset(var.ssh_public_keys)
content {
username = var.admin_username
public_key = admin_ssh_key.value
}
}

os_disk {
caching = "ReadWrite"
storage_account_type = "Standard_LRS"
}

source_image_reference {
publisher = "Canonical"
offer = "0001-com-ubuntu-server-jammy"
sku = "22_04-lts"
version = "latest"
}

custom_data = base64encode(
templatefile("${path.module}/cloud-init.yaml.tmpl", {
timezone = "Europe/Oslo"
db_host = local.db_fqdn
})
)

tags = local.common_tags
}

Check failure

Code scanning / checkov

Ensure Virtual Machine Extensions are not Installed Error

Ensure Virtual Machine Extensions are not Installed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants