Skip to content

Commit

Permalink
Use Entra for SSH into jumpbox
Browse files Browse the repository at this point in the history
  • Loading branch information
twsouthwick committed May 24, 2024
1 parent 8cc0972 commit 043efa7
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 91 deletions.
88 changes: 47 additions & 41 deletions infra/core/compute/ubuntu-jumpbox.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -46,19 +46,13 @@ param name string
@description('The name of the linux PC. By default, this will be automatically constructed by the resource name.')
param computerLinuxName string?

@description('Username for the Virtual Machine.')
param adminUsername string
@description('Username for the Virtual Machine. NOTE: this is not saved anywhere as Entra is used to manage SSH logins once created.')
param adminUsername string = newGuid()

@description('Type of authentication to use on the Virtual Machine. SSH key is recommended.')
@allowed([
'sshPublicKey'
'password'
])
param authenticationType string = 'password'

@description('SSH Key or password for the Virtual Machine. SSH key is recommended.')
@description('Password for admin. NOTE: this is not saved anywhere as Entra is used to manage SSH logins once created.')
@secure()
param adminPasswordOrKey string
@minLength(8)
param adminPassword string = newGuid()

@description('The Ubuntu version for the VM. This will pick a fully patched image of this given Ubuntu version.')
@allowed([
Expand All @@ -77,6 +71,9 @@ param vmSize string = 'Standard_B2ms'
@description('The tags to associate with this resource.')
param tags object = {}

@description('Users to add to VM management role for jumpbox')
param users string[]

/*
** Dependencies
*/
Expand Down Expand Up @@ -129,31 +126,14 @@ var imageReference = {
version: 'latest'
}
}
var osDiskType = 'Standard_LRS'
var linuxConfiguration = (authenticationType == 'password') ? {} : {
disablePasswordAuthentication: true
ssh: {
publicKeys: [
{
path: '/home/${adminUsername}/.ssh/authorized_keys'
keyData: adminPasswordOrKey
}
]
}
}

var securityProfileJson = {
uefiSettings: {
secureBootEnabled: true
vTpmEnabled: true
}
securityType: securityType
}
var extensionName = 'GuestAttestation'
var extensionPublisher = 'Microsoft.Azure.Security.LinuxAttestation'
var extensionVersion = '1.0'
var maaTenantName = 'GuestAttestation'
var maaEndpoint = substring('emptystring', 0, 0)


resource networkInterface 'Microsoft.Network/networkInterfaces@2022-11-01' = {
name: 'nic-${name}'
Expand All @@ -177,10 +157,12 @@ resource networkInterface 'Microsoft.Network/networkInterfaces@2022-11-01' = {
}
}


resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = {
name: name
location: location
identity: {
type: 'SystemAssigned' // Needed by the Entra extension
}
properties: {
hardwareProfile: {
vmSize: vmSize
Expand All @@ -189,7 +171,7 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = {
osDisk: {
createOption: 'FromImage'
managedDisk: {
storageAccountType: osDiskType
storageAccountType: 'Standard_LRS'
}
}
imageReference: imageReference[ubuntuOSVersion]
Expand All @@ -204,29 +186,28 @@ resource virtualMachine 'Microsoft.Compute/virtualMachines@2021-11-01' = {
osProfile: {
computerName: computerName
adminUsername: adminUsername
adminPassword: adminPasswordOrKey
linuxConfiguration: ((authenticationType == 'password') ? null : linuxConfiguration)
adminPassword: adminPassword
}
securityProfile: ((securityType == 'TrustedLaunch') ? securityProfileJson : null)
}
tags: tags
}

resource vmExtension 'Microsoft.Compute/virtualMachines/extensions@2022-03-01' = if ((securityType == 'TrustedLaunch') && ((securityProfileJson.uefiSettings.secureBootEnabled == true) && (securityProfileJson.uefiSettings.vTpmEnabled == true))) {
resource vmExtension 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = if ((securityType == 'TrustedLaunch') && ((securityProfileJson.uefiSettings.secureBootEnabled == true) && (securityProfileJson.uefiSettings.vTpmEnabled == true))) {
parent: virtualMachine
name: extensionName
name: 'GuestAttestation'
location: location
properties: {
publisher: extensionPublisher
type: extensionName
typeHandlerVersion: extensionVersion
publisher: 'Microsoft.Azure.Security.LinuxAttestation'
type: 'GuestAttestation'
typeHandlerVersion: '1.0'
autoUpgradeMinorVersion: true
enableAutomaticUpgrade: true
settings: {
AttestationConfig: {
MaaSettings: {
maaEndpoint: maaEndpoint
maaTenantName: maaTenantName
maaEndpoint: substring('emptystring', 0, 0)
maaTenantName: 'GuestAttestation'
}
}
}
Expand All @@ -243,7 +224,7 @@ resource postDeploymentScript 'Microsoft.Compute/virtualMachines/extensions@2023
typeHandlerVersion: '2.1'
autoUpgradeMinorVersion: true
settings: {
skipDos2Unix:false
skipDos2Unix: false
}
protectedSettings: {
commandToExecute: 'chmod +x post-deployment.sh && bash post-deployment.sh'
Expand All @@ -254,6 +235,31 @@ resource postDeploymentScript 'Microsoft.Compute/virtualMachines/extensions@2023
}
}

resource aadsshlogin 'Microsoft.Compute/virtualMachines/extensions@2023-09-01' = {
name: 'aadsshlogin'
location: location
parent: virtualMachine
properties: {
publisher: 'Microsoft.Azure.ActiveDirectory'
type: 'AADSSHLoginForLinux'
typeHandlerVersion: '1.0'
autoUpgradeMinorVersion: true
}
}

// Role id for Virtual Machine Administrator Login
var vmAdminLoginId = '1c0163c0-47e6-4577-8991-ea5c82e286e4'

resource vmAdminRoles 'Microsoft.Authorization/roleAssignments@2022-04-01' = [for user in users: {
name: guid(virtualMachine.name, resourceGroup().name, user)
scope: virtualMachine
properties: {
principalType: 'User'
roleDefinitionId: resourceId('Microsoft.Authorization/roleDefinitions', vmAdminLoginId)
principalId: user
}
}]

resource diagnostics 'Microsoft.Insights/diagnosticSettings@2021-05-01-preview' = if (diagnosticSettings != null && !empty(logAnalyticsWorkspaceId)) {
name: '${name}-diagnostics'
scope: virtualMachine
Expand Down
5 changes: 0 additions & 5 deletions infra/main.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -314,8 +314,6 @@ module hubNetwork './modules/hub-network.bicep' = if (willDeployHubNetwork) {
logAnalyticsWorkspaceId: azureMonitor.outputs.log_analytics_workspace_id

// Settings
administratorPassword: jumpboxAdministratorPassword
administratorUsername: administratorUsername
createDevopsSubnet: true
enableBastionHost: true
// DDoS protection is recommended for Production deployments
Expand Down Expand Up @@ -485,14 +483,11 @@ module application2 './modules/application-resources.bicep' = if (isMultiLocati
module applicationPostConfiguration './modules/application-post-config.bicep' = {
name: '${prefix}-application-postconfig'
params: {
deploymentSettings: primaryDeploymentSettings
administratorPassword: jumpboxAdministratorPassword
administratorUsername: administratorUsername
databasePassword: databasePassword
keyVaultName: isNetworkIsolated? hubNetwork.outputs.key_vault_name : application.outputs.key_vault_name
kvResourceGroupName: isNetworkIsolated? resourceGroups.outputs.hub_resource_group_name : resourceGroups.outputs.application_resource_group_name
readerIdentities: union(application.outputs.service_managed_identities, defaultDeploymentSettings.isMultiLocationDeployment ? application2.outputs.service_managed_identities : [])
resourceNames: naming.outputs.resourceNames
}
}

Expand Down
27 changes: 0 additions & 27 deletions infra/modules/application-post-config.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -61,17 +61,6 @@ type DeploymentSettings = {
// PARAMETERS
// ========================================================================

@description('The deployment settings to use for this deployment.')
param deploymentSettings DeploymentSettings

/*
** Passwords - specify these!
*/
@secure()
@minLength(12)
@description('The password for the administrator account. This will be used for the jump box, SQL server, and anywhere else a password is needed for creating a resource.')
param administratorPassword string = newGuid()

@minLength(8)
@description('The username for the administrator account on the jump box.')
param administratorUsername string = 'adminuser'
Expand All @@ -81,9 +70,6 @@ param administratorUsername string = 'adminuser'
@description('The password for the administrator account on the SQL Server.')
param databasePassword string

@description('The resource names for the resources to be created.')
param resourceNames object

/*
** Dependencies
*/
Expand Down Expand Up @@ -141,19 +127,6 @@ resource existingKeyVault 'Microsoft.KeyVault/vaults@2023-02-01' existing = {
// AZURE MODULES
// ========================================================================

module writeJumpBoxCredentialsToKeyVault '../core/security/key-vault-secrets.bicep' = if (deploymentSettings.isNetworkIsolated) {
name: 'hub-write-jumpbox-credentials-${deploymentSettings.resourceToken}'
scope: existingKvResourceGroup
params: {
name: existingKeyVault.name
secrets: [
{ key: 'Jumpbox--AdministratorPassword', value: administratorPassword }
{ key: 'Jumpbox--AdministratorUsername', value: administratorUsername }
{ key: 'Jumpbox--ComputerName', value: resourceNames.hubJumpbox }
]
}
}

module writeSqlAdminInfoToKeyVault '../core/security/key-vault-secrets.bicep' = {
name: 'write-sql-admin-info-to-keyvault'
scope: existingKvResourceGroup
Expand Down
13 changes: 3 additions & 10 deletions infra/modules/hub-network.bicep
Original file line number Diff line number Diff line change
Expand Up @@ -93,14 +93,6 @@ param logAnalyticsWorkspaceId string = ''
/*
** Settings
*/
@secure()
@minLength(8)
@description('The password for the administrator account on the jump box.')
param administratorPassword string = newGuid()

@minLength(8)
@description('The username for the administrator account on the jump box.')
param administratorUsername string = 'adminuser'

@description('If enabled, an Ubuntu jump box will be deployed. Ensure you enable the bastion host as well.')
param enableJumpBox bool = false
Expand Down Expand Up @@ -377,9 +369,10 @@ module jumpbox '../core/compute/ubuntu-jumpbox.bicep' = if (enableJumpBox) {
logAnalyticsWorkspaceId: logAnalyticsWorkspaceId
subnetId: virtualNetwork.outputs.subnets[resourceNames.spokeDevopsSubnet].id

// users
users: [ deploymentSettings.principalId ]

// Settings
adminPasswordOrKey: administratorPassword
adminUsername: administratorUsername
diagnosticSettings: diagnosticSettings
}
}
Expand Down
23 changes: 15 additions & 8 deletions prod-deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,22 +166,29 @@ To retrieve the generated password:
azd package
```
1. From PowerShell use the following SCP command to upload the code to the jump box (use the password you retrieved from Key Vault to authenticate the SCP command):
```shell
scp -r -P 50022 * azureadmin@127.0.0.1:/home/azureadmin/web-app-pattern
1. Install the SSH extension for Azure CLI
```pwsh
az extension add --name ssh
```
> If you were unable to connect due to [Remote host identification has changed](troubleshooting.md#remote-host-identification-has-changed)
1. Obtain an SSH key from entra:
```pwsh
az ssh config --ip 127.0.0.1 -f ./ssh-config
```
1. From PowerShell use the SCP command to upload the AZD environment to the jump box:
1. From PowerShell use the following SCP command to upload the code to the jump box (use the password you retrieved from Key Vault to authenticate the SCP command):
```shell
scp -r -P 50022 ./.azure azureadmin@127.0.0.1:/home/azureadmin/web-app-pattern
rsync -av -e "ssh -F ./ssh-config -p 50022" . 127.0.0.1:~/web-app-pattern
```
> If you were unable to connect due to [Remote host identification has changed](troubleshooting.md#remote-host-identification-has-changed)
1. Run the following command to start a shell session on the jump box:
```shell
ssh azureadmin@127.0.0.1 -p 50022
ssh -F ./ssh-config 127.0.0.1 -p 50022
```
### 4. Deploy code from the jump box
Expand Down Expand Up @@ -249,7 +256,7 @@ To retrieve the generated password:
1. Use the URL displayed in the console output to launch the Relecloud application that you have deployed:
![screenshot of Relecloud app home page](assets/images/WebAppHomePage.png)
### 5. Teardown
1. Close the PowerShell session on the jump box:
Expand Down

0 comments on commit 043efa7

Please sign in to comment.