Bonjour à tous, cela fait un long moment que je n’ai pas eu l’opportunité d’écrire un article. Il y a quelques jours, j’ai eu besoin de relier un object storage OpenStack à un Nextcloud. J’ai décidé ensuite de l’automatiser avec terraform et l’inclure dans un module. Pour ceux qui ne connaissent pas les pratiques Objects Storage, je renvoie au site OpenStack. Comme d’hab, comme je suis chauvin, je vais utiliser un service cloud français : OVH. Bientôt et si le temps me le permet, j’utiliserai ma propre infra OpenStack sur plusieurs nodes : pour ça, j’ai enfin reçu mes cartes mères bi-xeon supermicro, mais tout est à construire, même le hardware donc à suivre…
Comme dirait mon vendeur de téléphone, “on ne va pas se mentir” : je déteste le Cloud. Ça vient de très loin – je me demande même si je faisais de l’informatique – d’une discussion avec une fraîche mouture de Science-Po de l’époque qui me vantait l’avantage du concept “magique” du cloud, et en quoi il était bon que “Mme Michu” n’y comprenne rien. Maintenant que je vois cela de l’intérieur, je dirais que mon sentiment de défiance est renforcé.
Derrière le Cloud il y a du “barre metal”, c’est à dire des serveurs physiques reliés les uns aux autres, très souvent sous Linux, avec une couche d’automatisation complexe qui permet d’offrir des services de plus en plus importants aux clients. Or il est une règle vieille comme le monde : pas d’indépendance sans connaissance… Durant le temps que vous chercherez la technique pour construire des serveurs sur l’infra d’Amazon ou Microsoft, d’autres le passeront à trouver des techniques plus poussées pour vous rendre toujours plus dépendant. Des services rendus propriétaires qui étrangement fonctionnent mieux que le standard opensource… par exemple des bases de données, de la redondance de charge… Le Cloud c’est avec modération, celui qui se sert du Cloud pour des applications critiques est fou, et dans le fond celui qui vend des services Cloud américains aux grands comptes français ne veut pas vraiment de l’indépendance économique française. C’est une situation qui devrait être accompagnée par le politique car dans les DSI, l’argent est bien souvent ROI… et la stratégie sur le long terme se fait rare. Si ces questions liées à la “souveraineté numérique” vous intéressent, je vous invite à lire cet article : Souveraineté numérique : le choix inquiétant fait par la BPI pour l’hébergement des données.
Vous l’aurez compris, je vais prendre un exemple de Cloud bien français même si ce dernier s’étend maintenant dans le monde entier : celui d’OVH. En plus ils utilisent la techno Openstack, ce qui est parfait pour s’initier aux concepts de cloud public/privé. Il faut savoir que le Cloud n’est pas gratuit, 0.01€ vous seront facturés à la création de l’infra ci-dessous, plus si vous décidez de garder le serveur.
Prérequis
Avoir un compte chez OVH et souscrit à l’offre Cloud
Un utilisateur OpenStack
Les variables d’environnement OpenStack
Vos identifiants API et clés d’autorisations OVH
Une clé SSH
L’exécutable Terraform
Les clients OpenStack (nova, glance)
Vous trouverez les pages de tutoriel chez OHV et sur ce site. Normalement pas de soucis majeurs pour effectuer ces opérations, si vous avez néanmoins des problèmes, envoyez-moi un message et je détaillerai ce paragraphe des prérequis.
Utilisation de Terraform pour les instances Cloud public
On va faire très simple, trois fichiers. Un pour la connection à Openstack, un autre pour la création de la machine et son réseau, le dernier pour les variables :
$ tree
.
├── connections.tf
├── simple_instance.tf
└── variables.tf
connections.tf
Rien de bien compliqué ici :
provider "openstack" {
auth_url = "https://auth.cloud.ovh.net/v3.0/"
domain_name = "default"
tenant_name = ""
alias = "ovh"
user_name = "utilisateur_openstack" # vous possédez ces valeurs
password = "le_mot_de_passe" # si vous avez effectué les prérequis
}
simple_instance.tf
Voilà maintenant pour créer un serveur, j’utilise l’adresse IP de la machine d’administration afin de l’autoriser à accéder au serveur Cloud via SSH. Vous pouvez utiliser une autre technique que firewalld, il suffit de changer le script run.sh … Comme d’habitude, j’utilise des images cloud de Fedora Server (ici 32).
resource "openstack_compute_keypair_v2" "mattkey" {
provider = openstack.ovh
name = "mattkey" # A changer : c'est la clé que vous avez préalablement créée dans l'interface openstack OVH
public_key = file("~/.ssh/id_rsa.pub") # C'est la clé que vous avez préalablement créée dans l'interface openstack OVH
}
data "openstack_networking_network_v2" "public_a" {
name = "Ext-Net"
provider = openstack.ovh
}
resource "openstack_networking_port_v2" "public_a" {
name = "test_a_0"
network_id = data.openstack_networking_network_v2.public_a.id
admin_state_up = "true"
provider = openstack.ovh
}
data "http" "myip" {
url = "https://api.ipify.org"
}
data "template_file" "setup" {
template = <<SETUP
#!/bin/bash
# install & configure firewall
dnf install -y firewalld
systemctl start firewalld
systemctl enable --now firewalld
firewall-cmd --permanent --add-service=ssh
firewall-cmd --permanent --add-source=${trimspace(data.http.myip.body)}/32 --zone=trusted
firewall-cmd --permanent --add-service=ssh --zone trusted
firewall-cmd --permanent --remove-service=ssh --zone=public
firewall-cmd --reload
SETUP
}
data "template_file" "userdata" {
template = <<CLOUDCONFIG
#cloud-config
write_files:
- path: /tmp/setup/run.sh
permissions: '0755'
content: |
${indent(6, data.template_file.setup.rendered)}
- path: /etc/systemd/network/30-ens3.network
permissions: '0644'
content: |
[Match]
Name=ens3
[Network]
DHCP=ipv4
runcmd:
- sh /tmp/setup/run.sh
CLOUDCONFIG
}
resource "openstack_compute_instance_v2" "fed-test" {
region = var.region
name = "test-fed-instance"
provider = openstack.ovh
image_name = "Fedora 32"
flavor_name = "s1-2"
user_data = data.template_file.userdata.rendered
key_pair = openstack_compute_keypair_v2.mattkey.name
network {
access_network = true
port = openstack_networking_port_v2.public_a.id
}
}
output "Server_IP" {
value = openstack_networking_port_v2.public_a.all_fixed_ips
}
variables.tf
variable "region" {
type = string
default = "UK1" # cette valeur dépend de votre compte OVH et du fichier openstack .rc
}
Et voilà le tour est joué, théoriquement, l’ip du serveur s’affiche à la fin de l’exécution du `terraform apply`, vous pouvez vous y connecter via SSH avec votre clé privée. Pour tout détruire : `terraform destroy`. Si vous vous souvenez de mes précédents articles, ici ou là, vous pouvez maintenant provisionner la ou les machines avec Ansible.
Derrière ce titre un poil “putaclic” se cache des heures de recherches et des litres de café. Et oui, le réseau et moi ça fait deux… Vous vous souvenez sans doute de mon dernier article portant sur openvswitch, il n’était pas écrit pour rien… généralement quand je commence à fouiller une techno c’est que j’ai une idée derrière la tête.
Jusqu’à présent pour créer un réseau privé, j’utilisais tinc. Je ne vais pas m’appesantir sur ce sujet, mais c’est tout de même assez puissant. Je ne cache pas que dans une logique d’automatisation et d’industrialisation, ce n’est pas franchement sexy. Il faut installer le binaire, le configurer (en mode switch c’est mieux), créer les interfaces tun/tap, les clés, les partager… Il existe des rôles Ansible pour faire le job, vous les trouverez facilement sur Github.
On va s’approcher un peu plus d’un fonctionnement type OpenStack et utiliser les possibilités offertes par OpenVSwitch.
Nous allons créer un bridge interne GRE avec tunnel IPsec :
Pré-requis
Pour ce faire il faut au minimum deux hyperviseurs KVM bare metal, ça fonctionne aussi avec Proxmox et MacOS VMWare (ahahah), OpenStack puisque c’est intégré dans la solution, mais aussi sans rien… Il faut deux IPs joignables qui pointent vers les serveurs. Avoir installé openvswitch, sauf si ça ne vous intéresse pas mais dans ce cas, je me demande pourquoi vous lisez ces lignes.
Configuration
On ne va pas faire une usine à gaz, juste créer des certificats auto-signés avec la cli d’openvswitch sur le premier hôte :
Ensuite on va dire à openvswitch où se trouvent ces fichiers :
ovs-vsctl set Open_vSwitch . \
other_config:certificate=/etc/keys/<hostname1>-cert.pem \
other_config:private_key=/etc/keys/<hostname1>-privkey.pem
A faire sur chacun des serveurs bien sûr et changeant le nom hostname du .pem. C’est terminé pour les certificats et clés. Maintenant la mise en place des bridges ipsec GRE avec openvswitch. Sur l’hôte 1:
ip addr add 192.0.0.1/24 dev br-ipsec
ip link set br-ipsec up
ovs-vsctl add-port br-ipsec tun -- set interface tun type=gre \
options:remote_ip=<ip_de_hote2> \
options:remote_cert=/etc/keys/<hostname2>-cert.pem
ovs-vsctl show
Sur l’hôte 2 :
ip addr add 192.0.0.2/24 dev br-ipsec
ip link set br-ipsec up
ovs-vsctl add-port br-ipsec tun -- set interface tun type=gre \
options:remote_ip=<ip_de_hote1> \
options:remote_cert=/etc/keys/<hostname1>-cert.pem
Et maintenant sous vos yeux ébahis depuis l’hôte 2 :
ping 192.0.0.1
PING 192.0.0.1 (192.0.0.1) 56(84) bytes of data.
64 bytes from 192.0.0.1: icmp_seq=1 ttl=64 time=88.9 ms
64 bytes from 192.0.0.1: icmp_seq=2 ttl=64 time=87.6 ms
64 bytes from 192.0.0.1: icmp_seq=3 ttl=64 time=87.8 ms
^C
--- 192.0.0.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 87.587/88.097/88.926/0.591 ms
Généralisation
Vu que les serveurs sont des hyperviseurs KVM, on va tenter de créer des machines virtuelles qui profiteront du tunnel IPsec. Comme nous l’avons vu dans le dernier article sur openvswitch, il faut intégrer le xml du bridge pour qu’il soit interprété par libvirt :
Faites démarrer une vm sur chaque hyperviseur avec comme réseau net-ipsec, donnez-lui une adresse dans ce réseau et pingez.
Automatisation
Pour travailler dans un environnement automatisé, avec Terraform et Libvirt KVM, il faudra utiliser un alias pour le provider (j’ai mis Toronto en hommage à Glenn Gould, le joueur de foot) :
# cat multi_hyp.tf
provider "libvirt" {
uri = "qemu:///system"
}
provider "libvirt" {
alias = "toronto"
uri = "qemu+ssh://<kvm_user>@<ip_address>/system"
}
resource "libvirt_volume" "local-fedora-qcow2" {
name = "fedora-qcow2"
pool = "default"
source = "https://download.fedoraproject.org/pub/fedora/linux/releases/32/Cloud/x86_64/images/Fedora-Cloud-Base-32-1.6.x86_64.qcow2"
format = "qcow2"
}
resource "libvirt_volume" "remote-fedora-qcow2" {
provider = libvirt.toronto
name = "fedora-qcow2"
pool = "default"
source = "https://download.fedoraproject.org/pub/fedora/linux/releases/32/Cloud/x86_64/images/Fedora-Cloud-Base-32-1.6.x86_64.qcow2"
format = "qcow2"
}
resource "libvirt_cloudinit_disk" "commoninit-local" {
name = "local-commoninit.iso"
pool = "default"
user_data = data.template_file.user_data.rendered
network_config = data.template_file.network_config_local.rendered
}
resource "libvirt_cloudinit_disk" "commoninit-remote" {
provider = libvirt.toronto
name = "remote-commoninit.iso"
pool = "default"
user_data = data.template_file.user_data.rendered
network_config = data.template_file.network_config_remote.rendered
}
data "template_file" "user_data" {
template = file("${path.module}/cloud_init.cfg")
vars = {
hostname = "fedora"
fqdn = "lan"
user = "matt"
ssh_public_key = "ssh-rsa AAAA..."
}
}
data "template_file" "network_config_local" {
template = file("${path.module}/network_config_static.cfg")
vars = {
prefixIP = "192.0.0"
octetIP = "6"
}
}
data "template_file" "network_config_remote" {
template = file("${path.module}/network_config_static.cfg")
vars = {
prefixIP = "192.0.0"
octetIP = "7"
}
}
resource "libvirt_domain" "local-domain" {
name = "local"
memory = "2048"
vcpu = 2
disk {
volume_id = libvirt_volume.local-fedora-qcow2.id
}
cloudinit = libvirt_cloudinit_disk.commoninit-local.id
network_interface {
network_name = "net-ipsec"
}
}
resource "libvirt_domain" "remotehost-domain" {
provider = libvirt.toronto
name = "toronto"
memory = "2048"
vcpu = 2
disk {
volume_id = libvirt_volume.remote-fedora-qcow2.id
}
cloudinit = libvirt_cloudinit_disk.commoninit-remote.id
network_interface {
network_name = "net-ipsec"
}
}
terraform {
required_version = ">= 0.12"
}
Avec les fichiers config de cloudinit qui vont bien :
# cat network_config_static.cfg
version: 2
ethernets:
eth0:
dhcp4: no
dhcp6: no
addresses: [ ${prefixIP}.${octetIP}/24 ]
Et les vms se pingent, on peut également s’y connecter en SSH depuis leurs adresses privées sur 192.0.0.0/24 :
Optimisation
Il peut être franchement intéressant d’optimiser en configurant la valeur du MTU. Par exemple sur ma connexion entre mes deux hyperviseurs kvm (Paris et Beauharnois) en passant d’un MTU 1500 à 1554 j’obtiens :
[root@hyp ~]# iperf -c 192.0.0.2
------------------------------------------------------------
Client connecting to 192.0.0.2, TCP port 5001
TCP window size: 325 KByte (default)
------------------------------------------------------------
[ 3] local 192.0.0.1 port 44128 connected with 192.0.0.2 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.2 sec 10.1 MBytes 8.35 Mbits/sec
[root@hyp ~]# ifconfig eno3 mtu 1554
[root@hyp ~]# iperf -c 192.0.0.2
------------------------------------------------------------
Client connecting to 192.0.0.2, TCP port 5001
TCP window size: 325 KByte (default)
------------------------------------------------------------
[ 3] local 192.0.0.1 port 44130 connected with 192.0.0.2 port 5001
[ ID] Interval Transfer Bandwidth
[ 3] 0.0-10.1 sec 264 MBytes 220 Mbits/sec
Voilà pour cet article, reste maintenant à isoler à l’intérieur avec des VLANS, mais aussi VXLAN… dans un futur… proche… si j’ai le temps…
Nous utilisons des cookies sur notre site Web pour vous offrir l'expérience la plus pertinente en mémorisant vos préférences et des visites répétées. En cliquant sur «Accepter», vous consentez à l'utilisation de TOUS les cookies.
Ce site Web utilise des cookies pour améliorer votre expérience pendant que vous naviguez sur le site Web. Parmi ces cookies, les cookies classés comme nécessaires sont stockés sur votre navigateur car ils sont essentiels au fonctionnement des fonctionnalités de base du site Web. Nous utilisons également des cookies tiers qui nous aident à analyser et à comprendre comment vous utilisez ce site Web. Ces cookies ne seront stockés dans votre navigateur qu'avec votre consentement. Vous avez également la possibilité de désactiver ces cookies. Mais la désactivation de certains de ces cookies peut avoir un effet sur votre expérience de navigation.
Les cookies fonctionnels aident à exécuter certaines fonctionnalités telles que le partage du contenu du site Web sur les plates-formes de médias sociaux, la collecte de commentaires et d’autres fonctionnalités tierces.
Les cookies de performance sont utilisés pour comprendre et analyser les principaux indices de performance du site Web, ce qui contribue à offrir une meilleure expérience utilisateur aux visiteurs.
Cookie
Description
YSC
Ces cookies sont définis par Youtube et sont utilisés pour suivre les vues des vidéos intégrées.
Les cookies analytiques sont utilisés pour comprendre comment les visiteurs interagissent avec le site Web. Ces cookies aident à fournir des informations sur les mesures du nombre de visiteurs, du taux de rebond, de la source du trafic, etc.
Les cookies publicitaires sont utilisés pour fournir aux visiteurs des publicités et des campagnes marketing pertinentes. Ces cookies suivent les visiteurs sur les sites Web et collectent des informations pour fournir des publicités personnalisées.
Cookie
Type
Durée
Description
IDE
1 an et 24 jours
Utilisé par Google DoubleClick et stocke des informations sur la façon dont l'utilisateur utilise le site Web et toute autre publicité avant de visiter le site Web. Ceci est utilisé pour présenter aux utilisateurs des publicités qui les concernent en fonction du profil de l'utilisateur.
test_cookie
15 minutes
Ce cookie est défini par doubleclick.net. Le but du cookie est de déterminer si le navigateur de l'utilisateur prend en charge les cookies.
VISITOR_INFO1_LIVE
5 mois et 27 jours
Ce cookie est défini par Youtube. Utilisé pour suivre les informations des vidéos YouTube intégrées sur un site Web.
Les cookies nécessaires sont absolument essentiels au bon fonctionnement du site Web. Ces cookies assurent les fonctionnalités de base et les fonctions de sécurité du site Web, de manière anonyme.
Cookie
Type
Durée
Description
__cfduid
1 month
Le cookie est utilisé par les services cdn comme CloudFare pour identifier les clients individuels derrière une adresse IP partagée et appliquer les paramètres de sécurité par client. Il ne correspond à aucun identifiant d'utilisateur dans l'application Web et ne stocke aucune information personnellement identifiable.
cookielawinfo-checkbox-performance
1 an
Ce cookie est défini par le plugin GDPR Cookie Consent. Le cookie est utilisé pour stocker le consentement de l'utilisateur pour les cookies dans la catégorie «Performance».