Content de te revoir! Cet article s’appuiera sur les articles précédents de cette série en déployant un hôte bastion Windows Server 2019 pour gérer notre SDDC Google Cloud VMware Engine (GCVE). L’accès à l’hôte bastion sera fourni avec Identity-Aware Proxy (IAP). Tout sera déployé et configuré avec Terraform, avec tout le code référencé dans cet article est disponible sur https://github.com/shamsway/gcp-terraform-examples dans le gcve-bastion-iap
sous-répertoire.
Présentation de Identity Aware Proxy (IAP)
La mise en place de la connectivité cloud initiale est un défi. J’ai parcouru les étapes de déploiement d’un VPN client dans Établissement d’un SDDC dans Google Cloud, mais cet article montrera comment utiliser IAP comme méthode d’accès à un nouvel hôte bastion. L’utilisation d’IAP signifie que l’hôte bastion sera accessible sans avoir à configurer un VPN ou à l’exposer à Internet. Je suis un grand fan de cette approche, et bien qu’il y ait certains compromis à discuter, il s’agit d’une approche plus simple et plus sécurisée que les méthodes d’accès traditionnelles.
IAP peut être utilisé pour accéder à diverses ressources, y compris App Engine et GKE. L’accès à l’hôte bastion via RDP (port TCP 3389) sera effectué à l’aide d’IAP pour le transfert TCP. Une fois configuré, IAP nous permettra d’établir une connexion à notre hôte bastion via un tunnel crypté à la demande. La configuration de cette fonctionnalité nécessitera certains rôles IAM spécifiques, ainsi que certaines règles de pare-feu dans votre VPC. Si tu as Owner
autorisations dans votre projet GCP, alors vous êtes prêt à partir. Sinon, vous aurez besoin des rôles suivants attribués pour effectuer les tâches décrites dans le reste de cet article :
- Administrateur de calcul (
roles/compute.admin
) - Administrateur de compte de service (
roles/iam.serviceAccountAdmin
) - Utilisateur du compte de service (
roles/iam.serviceAccountUser
) - Administrateur des stratégies IAP (
roles/iap.admin
) - Administrateur des paramètres IAP (
roles/iap.settingsAdmin
) - Utilisateur de tunnel sécurisé par IAP (
roles/iap.tunnelResourceAccessor
) - Administrateur de réseau de services (
roles/servicenetworking.networksAdmin
) - Administrateur IAM du projet (
roles/resourcemanager.projectIamAdmin
)
Le pare-feu VPC devra autoriser le trafic provenant de 35.235.240.0/20
, qui est la plage qu’IAP utilise pour le transfert TCP. Cette règle peut être encore limitée à des ports TCP spécifiques, comme 3389 pour RDP ou 22 pour SSH.
Déploiement d’un hôte Bastion avec Terraform
L’exemple de code Terraform lié au début de la publication effectuera les opérations suivantes :
- Créez un compte de service, qui sera associé à l’hôte bastion
- Créez une instance Windows 2019 Server, qui sera utilisée comme hôte bastion
- Créez des règles de pare-feu pour accéder à l’hôte bastion via IAP et accéder aux ressources à partir de l’hôte bastion
- Attribuer les rôles IAM nécessaires pour IAP
- Définissez un mot de passe sur l’hôte bastion à l’aide du
gcloud
outil
Une fois que Terraform a terminé la configuration, vous pourrez utiliser le gcloud
outil pour activer le transfert TCP pour RDP. Une fois connecté à l’hôte bastion, vous pourrez vous connecter à votre portail vSphere basé sur GCVE. Pour commencer, clonez l’exemple de référentiel avec git clone https://github.com/shamsway/gcp-terraform-examples.git
, puis passez au gcve-bastion-iap
sous-répertoire. Vous trouverez ces fichiers :
main.tf
– Contient le code Terraform principal pour effectuer les étapes mentionnées ci-dessusvariables.tf
– Définit les variables d’entrée qui seront utilisées dansmain.tf
terraform.tfvars
– Fournit des valeurs pour les variables d’entrée définies dansvariables.tf
outputs.tf
– Définit les variables de sortie à renvoyer demain.tf
Regardons de plus près ce qui se passe dans chacun de ces fichiers.
Contenu de main.tf
provider "google" {
project = var.project
region = var.region
zone = var.zone
}
data "google_compute_network" "network" {
name = var.network_name
}
data "google_compute_subnetwork" "subnet" {
name = var.subnet_name
region = var.region
}
Tout comme l’exemple de mon dernier post, main.tf
commence par un provider
block pour définir le projet, la région et la zone Google Cloud dans lesquels Terraform créera des ressources. Les blocs de données suivants, google_compute_network.network
et google_compute_network.subnet
, référence un réseau et un sous-réseau VPC existants. Ces blocs de données fourniront les paramètres nécessaires à la création d’un hôte bastion et de règles de pare-feu.
resource "google_service_account" "bastion_host" {
project = var.project
account_id = var.service_account_name
display_name = "Service Account for Bastion"
}
Le premier bloc de ressources crée un nouveau compte de service qui sera associé à notre instance d’hôte bastion.
resource "google_compute_instance" "bastion_host" {
name = var.name
machine_type = var.machine_type
boot_disk {
initialize_params {
image = var.image
}
}
network_interface {
subnetwork = data.google_compute_subnetwork.subnet.self_link
access_config {}
}
service_account {
email = google_service_account.bastion_host.email
scopes = var.scopes
}
tags = [var.tag]
labels = var.labels
metadata = var.metadata
}
Les google_compute_instance.bastion_host
block crée l’hôte bastion. Il y a quelques points à noter dans ce bloc. subnetwork
est défini sur la base de l’un des blocs de données au début de main.tf
, data.google_compute_subnetwork.subnet.self_link
. Les self_link
La propriété fournit une référence unique au sous-réseau que Terraform utilisera lors de la soumission de l’appel d’API pour créer l’hôte bastion. De même, le compte de service créé par google_service_account.bastion_host
est attribué à l’hôte bastion.
tags
, labels
, et metadata
tous servent des objectifs similaires, mais distincts. tags
sont des balises réseau, qui seront utilisées dans les règles de pare-feu. labels
sont des données d’information qui peuvent être utilisées à des fins d’organisation ou de facturation. metadata
a de nombreuses utilisations, dont la plus courante consiste à fournir un script de démarrage que l’instance exécutera au premier démarrage.
resource "google_compute_firewall" "allow_from_iap_to_bastion" {
project = var.project
name = var.fw_name_allow_iap_to_bastion
network = data.google_compute_network.network.self_link
allow {
protocol = "tcp"
ports = ["3389"]
}
# https://cloud.google.com/iap/docs/using-tcp-forwarding#before_you_begin
# This range is needed to allow IAP to access the bastion host
source_ranges = ["35.235.240.0/20"]
target_tags = [var.tag]
}
resource "google_compute_firewall" "allow_access_from_bastion" {
project = var.project
name = var.fw_name_allow_mgmt_from_bastion
network = data.google_compute_network.network.self_link
allow {
protocol = "icmp"
}
allow {
protocol = "tcp"
ports = ["22", "80", "443", "3389"]
}
# Allow management traffic from bastion
source_tags = [var.tag]
}
Les deux blocs suivants créent des règles de pare-feu : une pour accéder à l’hôte bastion via IAP et l’autre pour accéder aux ressources à partir de l’hôte bastion. google_compute_firewall.allow_from_iap_to_bastion
autorise le trafic de 35.235.240.0/24
au tcp/3389
aux instances qui ont la même balise réseau que celle qui a été attribuée à l’hôte bastion. google_compute_firewall.allow_access_from_bastion
autorise le trafic depuis l’hôte bastion en référençant la même balise réseau à tout autre élément de notre projet, en utilisant des ports/protocoles de gestion communs.
resource "google_iap_tunnel_instance_iam_binding" "enable_iap" {
project = var.project
zone = var.zone
instance = var.name
role = "roles/iap.tunnelResourceAccessor"
members = var.members
depends_on = [google_compute_instance.bastion_host]
}
Les google_iap_tunnel_instance_iam_binding.enable_iap
le bloc attribue le roles/iap.tunnelResourceAccessor
Rôle IAM aux comptes définis dans le members
variable. Cette valeur peut être n’importe quelle ressource IAM valide, comme un compte ou un groupe spécifique. Ce rôle est requis pour pouvoir accéder à l’hôte bastion via IAP.
resource "google_service_account_iam_binding" "bastion_sa_user" {
service_account_id = google_service_account.bastion_host.id
role = "roles/iam.serviceAccountUser"
members = var.members
}
Les google_project_iam_member.bastion_sa_user
bloc autorise les comptes spécifiés dans le members
variable pour utiliser le compte de service nouvellement créé via le Service Account User
rôle (roles/iam.serviceAccountUser
). Cela permet aux utilisateurs ou groupes définis dans le members
variable pour accéder à toutes les ressources auxquelles le compte de service a le droit d’accéder. Plus d’informations à ce sujet peuvent être trouvées ici.
resource "google_project_iam_member" "bastion_sa_bindings" {
for_each = toset(var.service_account_roles)
project = var.project
role = each.key
member = "serviceAccount:${google_service_account.bastion_host.email}"
}
google_project_iam_member.bastion_sa_bindings
termine la configuration liée à IAM en accordant les rôles définis dans le service_account_roles
variable au compte de service. Ce compte de service est attribué à l’hôte bastion, qui définit ce que l’hôte bastion peut faire. Les rôles attribués par défaut sont répertoriés ci-dessous, mais ils peuvent être modifiés dans variables.tf
.
- Rédacteur de journal (
roles/logging.logWriter
) - Rédacteur de métrique de surveillance (
roles/monitoring.metricWriter
) - Visionneuse de surveillance (
roles/monitoring.viewer
) - Connexion au système d’exploitation de calcul (
roles/compute.osLogin
)
resource "time_sleep" "wait_60_seconds" {
create_duration = "60s"
depends_on = [google_compute_instance.bastion_host]
}
data "external" "gcloud_set_bastion_password" {
program = ["bash", "-c", "gcloud compute reset-windows-password ${var.name} --user=${var.username} --format=json --quiet"]
depends_on = [time_sleep.wait_60_seconds]
}
Ces deux derniers blocs sont ce que j’appelle des « trucs sympas de Terraform ». Le but de ces blocs est de définir le mot de passe sur l’hôte bastion. Il existe plusieurs façons de procéder, mais malheureusement, il n’existe aucun moyen de définir un mot de passe d’instance Windows avec une ressource Terraform native. Au lieu de cela, un external
la source de données est utilisée pour exécuter le gcloud
commande, avec les résultats au format JSON renvoyés (il s’agit d’une exigence de la external
source de données.) Le mot de passe ne peut pas être défini tant que l’hôte bastion n’est pas complètement démarré. external.gcloud_set_bastion_pasword
dépend de time_sleep.wait_60_seconds
, qui est une simple minuterie de 60 secondes qui donne à l’hôte bastion le temps de démarrer avant le gcloud
la commande est exécutée.
Il est possible que 60 secondes ne soient pas assez longues pour que l’hôte bastion démarre. Si vous recevez une erreur indiquant que l’instance n’est pas prête à l’emploi, vous avez deux options :
- Courir
terraform destroy
pour supprimer l’hôte bastion. Éditermain.tf
et augmenter lecreate_duration
à une valeur plus élevée, puis exécutezterraform apply
de nouveau. - Exécutez le
gcloud compute reset-windows-password
commande manuellement
Idéalement, la fonctionnalité de réinitialisation du mot de passe serait intégrée au fournisseur Google Cloud Terraform, et je ne serais pas surpris de la voir ajoutée à l’avenir. Si vous lisez cet article en 2022 ou au-delà, cela vaut probablement la peine d’une enquête rapide pour voir si cela s’est produit.
output.tf Contenu
output "bastion_username" {
value = data.external.gcloud_set_bastion_password.result.username
}
output "bastion_password" {
value = data.external.gcloud_set_bastion_password.result.password
}
Ces deux sorties sont les résultats de l’exécution de la commande gcloud. Une fois que Terraform a terminé son exécution, il affichera le nom d’utilisateur et le mot de passe définis sur l’hôte bastion. Un mot de passe est une donnée sensible, donc si vous voulez empêcher son affichage, ajoutez sensitive = true
à la bastion_password
bloc de sortie. Les valeurs de sortie sont stockées dans le fichier d’état Terraform, vous devez donc prendre…