Image Packer et Ansible¶
Construction de l'image Guacamole avec Packer et trois rôles Ansible : Tomcat, guacd et la webapp Guacamole.
Template Packer¶
Variables¶
Créez packer/variables.pkr.hcl :
variable "project_id" {
type = string
description = "ID du projet GCP"
}
variable "zone" {
type = string
default = "europe-west1-b"
description = "Zone GCP pour le build"
}
variable "universe_domain" {
type = string
default = "googleapis.com"
description = "Universe domain GCP (souverain ou standard)"
}
variable "cloudsql_host" {
type = string
description = "Adresse Private IP de l'instance Cloud SQL"
}
variable "guacamole_version" {
type = string
default = "1.5.5"
description = "Version d'Apache Guacamole"
}
Source et build¶
Créez packer/guacamole.pkr.hcl :
packer {
required_plugins {
googlecompute = {
version = ">= 1.1.0"
source = "github.com/hashicorp/googlecompute"
}
ansible = {
version = ">= 1.1.0"
source = "github.com/hashicorp/ansible"
}
}
}
source "googlecompute" "guacamole" {
project_id = var.project_id
zone = var.zone
source_image_family = "debian-12"
source_image_project_id = "debian-cloud"
machine_type = "e2-standard-2"
ssh_username = "packer"
image_name = "guacamole-{{timestamp}}"
image_family = "guacamole"
image_description = "Apache Guacamole - Tomcat + guacd + extensions JDBC/SAML"
disk_size = 20
universe_domain = var.universe_domain
}
build {
sources = ["source.googlecompute.guacamole"]
provisioner "ansible" {
playbook_file = "../ansible/playbook.yml"
user = "packer"
extra_arguments = [
"--extra-vars", "cloudsql_host=${var.cloudsql_host}",
"--extra-vars", "guacamole_version=${var.guacamole_version}"
]
}
}
Fichier de variables¶
Créez packer/guacamole.auto.pkrvars.hcl :
project_id = "MON-PROJET-GCP"
zone = "europe-west1-b"
cloudsql_host = "10.0.0.X" # Adresse Private IP Cloud SQL
Adresse Cloud SQL
L'adresse cloudsql_host sera connue après le provisionnement Cloud SQL (chapitre suivant). Vous pourrez revenir mettre à jour cette valeur.
Playbook Ansible¶
Créez ansible/playbook.yml :
---
- name: Provisionner Apache Guacamole
hosts: all
become: true
vars:
guacamole_version: "1.5.5"
cloudsql_host: "10.0.0.X"
roles:
- tomcat
- guacd
- guacamole-webapp
Rôle tomcat¶
Variables par défaut¶
Créez roles/tomcat/defaults/main.yml :
Tâches principales¶
Créez roles/tomcat/tasks/main.yml :
---
- name: Installer OpenJDK
ansible.builtin.apt:
name: "openjdk-{{ tomcat_java_version }}-jdk-headless"
state: present
update_cache: true
- name: Installer Tomcat
ansible.builtin.apt:
name: "tomcat{{ tomcat_version }}"
state: present
- name: Supprimer les webapps par defaut
ansible.builtin.file:
path: "/var/lib/tomcat{{ tomcat_version }}/webapps/{{ item }}"
state: absent
loop:
- ROOT
- docs
- examples
- host-manager
- manager
- name: Activer Tomcat au demarrage
ansible.builtin.systemd:
name: "tomcat{{ tomcat_version }}"
enabled: true
state: started
- name: Validation du role
ansible.builtin.include_tasks: validate.yml
tags: [validate]
Validation¶
Créez roles/tomcat/tasks/validate.yml :
---
- name: "Assert : Java installe"
ansible.builtin.command: java -version
changed_when: false
- name: "Assert : Tomcat actif"
ansible.builtin.systemd:
name: "tomcat{{ tomcat_version }}"
register: tomcat_status
failed_when: tomcat_status.status.ActiveState != "active"
- name: "Assert : Tomcat ecoute sur le port configure"
ansible.builtin.wait_for:
port: "{{ tomcat_http_port }}"
timeout: 10
Handlers¶
Créez roles/tomcat/handlers/main.yml :
---
- name: Restart Tomcat
ansible.builtin.systemd:
name: "tomcat{{ tomcat_version }}"
state: restarted
Rôle guacd¶
Variables par défaut¶
Créez roles/guacd/defaults/main.yml :
Tâches principales¶
Créez roles/guacd/tasks/main.yml :
---
- name: Installer les dependances de build
ansible.builtin.apt:
name:
- build-essential
- libcairo2-dev
- libjpeg62-turbo-dev
- libpng-dev
- libtool-bin
- uuid-dev
# RDP
- libfreerdp-dev
- freerdp2-dev
# SSH
- libssh2-1-dev
- libpango1.0-dev
# WebSocket
- libwebsockets-dev
- libwebp-dev
# Aucune dependance VNC (libvncserver-dev NON installe)
state: present
update_cache: true
- name: Telecharger guacamole-server
ansible.builtin.get_url:
url: "https://apache.org/dyn/closer.lua/guacamole/{{ guacamole_version }}/source/guacamole-server-{{ guacamole_version }}.tar.gz?action=download"
dest: "/tmp/guacamole-server-{{ guacamole_version }}.tar.gz"
mode: '0644'
- name: Extraire guacamole-server
ansible.builtin.unarchive:
src: "/tmp/guacamole-server-{{ guacamole_version }}.tar.gz"
dest: /tmp
remote_src: true
- name: Compiler et installer guacd
ansible.builtin.shell: |
cd /tmp/guacamole-server-{{ guacamole_version }}
./configure --with-init-dir=/etc/init.d \
--disable-guacenc \
--without-vnc
make -j$(nproc)
make install
ldconfig
args:
creates: /usr/local/sbin/guacd
- name: Creer le service systemd guacd
ansible.builtin.copy:
dest: /etc/systemd/system/guacd.service
content: |
[Unit]
Description=Guacamole proxy daemon
After=network.target
[Service]
ExecStart=/usr/local/sbin/guacd -f -b {{ guacd_bind_host }} -l {{ guacd_bind_port }}
Restart=on-failure
User=daemon
Group=daemon
[Install]
WantedBy=multi-user.target
mode: '0644'
notify: Restart guacd
- name: Activer guacd au demarrage
ansible.builtin.systemd:
name: guacd
enabled: true
state: started
daemon_reload: true
- name: Validation du role
ansible.builtin.include_tasks: validate.yml
tags: [validate]
Validation¶
Créez roles/guacd/tasks/validate.yml :
---
- name: "Assert : guacd actif"
ansible.builtin.systemd:
name: guacd
register: guacd_status
failed_when: guacd_status.status.ActiveState != "active"
- name: "Assert : guacd ecoute sur le port configure"
ansible.builtin.wait_for:
host: "{{ guacd_bind_host }}"
port: "{{ guacd_bind_port }}"
timeout: 10
- name: "Assert : VNC non compile"
ansible.builtin.shell: "! ldd /usr/local/sbin/guacd | grep -q vnc"
changed_when: false
Handlers¶
Créez roles/guacd/handlers/main.yml :
Rôle guacamole-webapp¶
Variables par défaut¶
Créez roles/guacamole-webapp/defaults/main.yml :
guacamole_home: "/etc/guacamole"
guacamole_db_name: "guacamole"
guacamole_db_user: "guacamole"
guacamole_db_password: "changeme"
postgresql_driver_version: "42.7.4"
Tâches principales¶
Créez roles/guacamole-webapp/tasks/main.yml :
---
- name: Creer le repertoire GUACAMOLE_HOME
ansible.builtin.file:
path: "{{ item }}"
state: directory
owner: "tomcat"
group: "tomcat"
mode: '0755'
loop:
- "{{ guacamole_home }}"
- "{{ guacamole_home }}/extensions"
- "{{ guacamole_home }}/lib"
- name: Telecharger le WAR Guacamole
ansible.builtin.get_url:
url: "https://apache.org/dyn/closer.lua/guacamole/{{ guacamole_version }}/binary/guacamole-{{ guacamole_version }}.war?action=download"
dest: "/var/lib/tomcat{{ tomcat_version }}/webapps/guacamole.war"
mode: '0644'
notify: Restart Tomcat
- name: Telecharger l'extension JDBC PostgreSQL
ansible.builtin.get_url:
url: "https://apache.org/dyn/closer.lua/guacamole/{{ guacamole_version }}/binary/guacamole-auth-jdbc-{{ guacamole_version }}.tar.gz?action=download"
dest: "/tmp/guacamole-auth-jdbc-{{ guacamole_version }}.tar.gz"
mode: '0644'
- name: Extraire l'extension JDBC
ansible.builtin.unarchive:
src: "/tmp/guacamole-auth-jdbc-{{ guacamole_version }}.tar.gz"
dest: /tmp
remote_src: true
- name: Installer l'extension JDBC PostgreSQL
ansible.builtin.copy:
src: "/tmp/guacamole-auth-jdbc-{{ guacamole_version }}/postgresql/guacamole-auth-jdbc-postgresql-{{ guacamole_version }}.jar"
dest: "{{ guacamole_home }}/extensions/"
remote_src: true
mode: '0644'
- name: Telecharger le driver JDBC PostgreSQL
ansible.builtin.get_url:
url: "https://jdbc.postgresql.org/download/postgresql-{{ postgresql_driver_version }}.jar"
dest: "{{ guacamole_home }}/lib/"
mode: '0644'
- name: Telecharger l'extension SAML
ansible.builtin.get_url:
url: "https://apache.org/dyn/closer.lua/guacamole/{{ guacamole_version }}/binary/guacamole-auth-sso-{{ guacamole_version }}.tar.gz?action=download"
dest: "/tmp/guacamole-auth-sso-{{ guacamole_version }}.tar.gz"
mode: '0644'
- name: Extraire l'extension SSO
ansible.builtin.unarchive:
src: "/tmp/guacamole-auth-sso-{{ guacamole_version }}.tar.gz"
dest: /tmp
remote_src: true
- name: Installer l'extension SAML
ansible.builtin.copy:
src: "/tmp/guacamole-auth-sso-{{ guacamole_version }}/saml/guacamole-auth-sso-saml-{{ guacamole_version }}.jar"
dest: "{{ guacamole_home }}/extensions/"
remote_src: true
mode: '0644'
- name: Deployer guacamole.properties
ansible.builtin.template:
src: guacamole.properties.j2
dest: "{{ guacamole_home }}/guacamole.properties"
owner: tomcat
group: tomcat
mode: '0640'
notify: Restart Tomcat
- name: Configurer GUACAMOLE_HOME pour Tomcat
ansible.builtin.lineinfile:
path: "/etc/default/tomcat{{ tomcat_version }}"
line: "GUACAMOLE_HOME={{ guacamole_home }}"
notify: Restart Tomcat
- name: Validation du role
ansible.builtin.include_tasks: validate.yml
tags: [validate]
Template guacamole.properties¶
Créez roles/guacamole-webapp/templates/guacamole.properties.j2 :
# Guacd
guacd-hostname: {{ guacd_bind_host | default('127.0.0.1') }}
guacd-port: {{ guacd_bind_port | default('4822') }}
# PostgreSQL (Cloud SQL)
postgresql-hostname: {{ cloudsql_host }}
postgresql-port: 5432
postgresql-database: {{ guacamole_db_name }}
postgresql-username: {{ guacamole_db_user }}
postgresql-password: {{ guacamole_db_password }}
postgresql-auto-create-accounts: true
# SAML (configure au chapitre 05)
# saml-idp-metadata-url:
# saml-idp-url:
# saml-entity-id:
# saml-callback-url:
Validation¶
Créez roles/guacamole-webapp/tasks/validate.yml :
---
- name: "Assert : WAR deploye"
ansible.builtin.stat:
path: "/var/lib/tomcat{{ tomcat_version }}/webapps/guacamole.war"
register: war_file
failed_when: not war_file.stat.exists
- name: "Assert : Extension JDBC presente"
ansible.builtin.find:
paths: "{{ guacamole_home }}/extensions"
patterns: "guacamole-auth-jdbc-postgresql-*.jar"
register: jdbc_ext
failed_when: jdbc_ext.matched == 0
- name: "Assert : Extension SAML presente"
ansible.builtin.find:
paths: "{{ guacamole_home }}/extensions"
patterns: "guacamole-auth-sso-saml-*.jar"
register: saml_ext
failed_when: saml_ext.matched == 0
- name: "Assert : Driver PostgreSQL present"
ansible.builtin.find:
paths: "{{ guacamole_home }}/lib"
patterns: "postgresql-*.jar"
register: pg_driver
failed_when: pg_driver.matched == 0
- name: "Assert : guacamole.properties existe"
ansible.builtin.stat:
path: "{{ guacamole_home }}/guacamole.properties"
register: props_file
failed_when: not props_file.stat.exists
Ordre des rôles
Le rôle guacamole-webapp dépend des variables de tomcat et guacd. Ansible les exécuté dans l'ordre du playbook, donc tomcat → guacd → guacamole-webapp.