Terraform & cloud-init & vmware

Automatisiertes Deployment von virtuellen Maschinen in eine VMWare Umgebung mit Hilfe von Terraform und cloud-init.

als wird ein Terraform Konfiguration benötigt. Ich habe das alles in 3 Dateien aufgeteilt.
  • main.tf
  • terraform.tfvars
  • variables.tf
zuerst erstellen wir die Datei variables.tf um warm zu werden ;-)
variable "vsphere_server" {}
variable "vsphere_user" {}
variable "vsphere_password" {}
variable "vsphere_name" {}
variable "vsphere_dom" {}
variable "vsphere_datacenter" {}
variable "vsphere_datacenter_folder" {}
variable "vsphere_datastore" {}
variable "vsphere_resource_pool" {}
variable "vsphere_network" {}
variable "vsphere_template" {}
variable "vsphere_guest_id" {}
dann setzen wir die Variablen in der neuen Datei terraform.tfvars
# define the vsphere server
vsphere_server = "dev-vm-test-cl-01.demo.local"
# the username to access (use \\ instead \)
vsphere_user = "vsphere.local\\dev"
# password for the user
vsphere_password = "..................."
# name of the guest system
vsphere_name = "demo-vm"
# domain name 
vsphere_dom = ".demo.local"
# datacenter in vmware
vsphere_datacenter = "demo-dc"
# if you use folders define here
vsphere_datacenter_folder = "DEMO"
# datastore to store the machine
vsphere_datastore = "DEMOSAN"
# resource pool 
vsphere_resource_pool = "DEMO"
# the name of the network who the machine should use
vsphere_network = "DEMO-NETZ"
# which template should terraform use to build the machine ?
vsphere_template = "CENTOS-7-DEMO"
# define the guest id
vsphere_guest_id = "centos7_64Guest"
um das deploment durchzuführen benötigen wir noch einen user für die Anmeldung am Gast den wir in der main.tf definieren können hierzu erstellen wir uns erstmal den passwort hash. Ich hab hier mal einfach TEST genommen für ein produktives Deployment nicht zu empfehlen !
python3 -c 'import crypt,getpass; print(crypt.crypt(getpass.getpass(), crypt.mksalt(crypt.METHOD_SHA512)))'
Password:
xxxxxxxxxxxxxxxxxxxxx1Q0T3BfTTFcA.T7byqXbNyyyyyyyyyyyyyyyyyyy
jetzt kümmern wir uns um die main.tf die das eigentliche deployment übernimmt. Die user-data und meta-data werden der Maschine direkt als Template mitgegeben, für mehr Informationen zu cloud-init könnt ihr hier die Doku lesen.
provider "vsphere" {
        vsphere_server = "${var.vsphere_server}"
        user = "${var.vsphere_user}"
        password = "${var.vsphere_password}"
        allow_unverified_ssl= true
}

data "vsphere_datacenter" "dc" {
         name = "${var.vsphere_datacenter}"
}

data "vsphere_datastore" "datastore" {
        name = "${var.vsphere_datastore}"
        datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

data "vsphere_resource_pool" "pool" {
  name          = "${var.vsphere_resource_pool}"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

data "vsphere_network" "network" {
  name          = "${var.vsphere_network}"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

data "vsphere_virtual_machine" "template" {
  name          = "${var.vsphere_template}"
  datacenter_id = "${data.vsphere_datacenter.dc.id}"
}

data "template_file" "meta_init" {
        template = <<EOF
{
        "local-hostname": "$${local_hostname}"
}
EOF

vars = {
        local_hostname = "${var.vsphere_name}${var.vsphere_dom}"
}
}

data "template_file" "cloud_init" {
        template = <<EOF
#cloud-config
# set hostname & keyboard layout
bootcmd:
  - [ sh, -c, "echo 'DEPLOY-DATE-TIME - $${deploy_date}' > /cloud-init-info.txt"]
  - [ localectl, set-keymap, de ]
  - [ hostnamectl, set-hostname, $${hostname} ]
  - [ setenforce, 0 ]
  - [ sed, -i, 's/enforcing/disabled/g', /etc/selinux/config ]
growpart:
  mode: auto
  devices: ['/']
  ignore_growroot_disabled: false
users:
  - name: demo
    groups: wheel
    lock_passwd: false
    passwd: xxxxxxxxxxxxxxxxxxxxx1Q0T3BfTTFcA.T7byqXbNyyyyyyyyyyyyyyyyyyy
    shell: /bin/bash
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    ssh-authorized-keys:
      - ssh-rsa AAAAB................. DEPLOY@HOST
# set root password 
chpasswd:
  list: |
     root:xxxxxxxxxxxxxxxx4IlB$pLkby1WsExxxxxxxxxxxxxxxx
EOF
        vars = {
                hostname = "${var.vsphere_name}${var.vsphere_dom}"
                deploy_date = formatdate ("DD'.'MM'.'YYYY hh:mm ZZZ",timestamp())
        }
}

resource "vsphere_virtual_machine" "vm" {
  name             = "${var.vsphere_name}${var.vsphere_dom}"
  resource_pool_id = "${data.vsphere_resource_pool.pool.id}"
  datastore_id     = "${data.vsphere_datastore.datastore.id}"
  folder           = "${var.vsphere_datacenter_folder}"
  num_cpus = 4
  memory   = 8096
  guest_id = "${var.vsphere_guest_id}"

  network_interface {
    network_id = "${data.vsphere_network.network.id}"
  }

  disk {
    label = "disk0"
    size  = 150
  }

  clone {
    template_uuid = "${data.vsphere_virtual_machine.template.id}"
  }

  extra_config = {
        "guestinfo.metadata" = "${base64gzip(data.template_file.meta_init.rendered)}"
        "guestinfo.metadata.encoding" = "gzip+base64"
        "guestinfo.userdata" = "${base64gzip(data.template_file.cloud_init.rendered)}"
        "guestinfo.userdata.encoding" = "gzip+base64"
        }
}
Terrafom & cloud-init wäre dann fertig , jetzt müssen wir uns noch ein Image bauen. Ich habe dafür das CentOS-7-x86_64-GenericCloud.qcow2 Image von CentOs heruntergeladen. Und wie hier beschrieben konvertiert. Für die Anpassung des Templates musste ich mir eine extra cloud-config schreiben die nur eins macht, einen Benutzer mit login anlegen ;-) Dazu habe ich mir unter Ubuntu das Packet cloud-utils installiert. Dann erstellst du eine Datei config.yaml mit diesem Inhalt.
#cloud-init
users:
  - name: image
    groups: wheel
    lock_passwd: false
    passwd: $6$..............................zVjyZr4fj1
    shell: /bin/bash
    sudo: ['ALL=(ALL) NOPASSWD:ALL']
    ssh-authorized-keys:
      - DEIN SSH KEY
dann bauen wir daraus ein ISO File
cloud-localds config.iso config.yaml
Jetzt muss die konvertierte vmdk auf den Datastore hochgeladen werden. Dann eine neue Maschine anlegen und als Festplatte die vorhandene vmdk Datei auswählen, in das CDROM die hochgeladene iso Datei einlegen und verbinden (auch beim einschalten). Beim Bootvorgang sieht cloud-init die cd und erkennt die user-data darauf. Es wird ein User angelegt der zuvor definiert wurde. Ich lasse das ISO auf dem Datastore für kommende Images die ich erstellen muss. Wir ihr das Passwort erstellt habe ich ja bereits weiter oben erklärt.Nach dem erstellen der VM anmelden und dann installieren wir im system noch open-vm-tools und cloud-init-vmware-guestinfo
yum install open-vm-tools
Jetzt machen wir die Kiste noch sauber das man ein sauberes Image ziehen kann. Das ist bereits hier schön erklärt. Wenn du damit fertig bist die virtuelle Maschine ausschalten und in ein Template klonen, am besten nimmst du als Namen für das Template CENTOS-7-DEMO. Jetzt sollte wenn du bootest ein neuer Benutzer angelegt werden mit dem Namen und dem Passwort TEST. selinux sollte ausgeschaltet sein usw....

Quellen :
https://stafwag.github.io/blog/blog/2019/03/03/howto-use-centos-cloud-images-with-cloud-init/
https://superuser.com/questions/1307260/convert-qcow2-image-to-vsphere-vmdk
https://stackoverflow.com/questions/37794846/convert-qcow2-to-vmdk-and-make-it-esxi-6-0-compatible
https://github.com/vmware/cloud-init-vmware-guestinfo
https://github.com/vmware/simple-k8s-test-env/tree/master/e2e
https://blah.cloud/infrastructure/using-cloud-init-for-vm-templating-on-vsphere/
https://community.spiceworks.com/how_to/151558-create-a-rhel-centos-6-7-template-for-vmware-vsphere
https://cloudinit.readthedocs.io/en/latest/

Docker & QEMU auf einem Rechner

Problem : Ich habe Docker und QEMU auf einem Rechner laufen. Nach der Docker Installation war es der virtuellen Maschine in QEMU nicht mehr möglich ins Internet zu zugreifen.

Lösung :
In meinem Fall waren die Default Einstellungen der Docker-CE Installation Schuld. Durch das korrigieren der IP-Tables konnte ich das Problem in meinem Fall beheben.
iptables -A FORWARD -i br1 -o br1 -j ACCEPT
Der Fehler tritt auch manchmal auf wenn der Linux Server in QEMU installiert ist und die lokale Firewall Einstellung das forwarding nicht zulässt.

Websockets mit Apache hinter haproxy

Ich hab hier eine Anwendung die WebSocket verwendet und im HA Umfeld läuft (haproxy). Leider benötigt diese Anwendung auch Sessions (sticky) deswegen musste ich ein wenig basteln ;-) Bei mir läuft eine node.js Anwendung hinter dem haproxy , für andere Fälle muss man vermutlich noch etwas mit der Konfiguration spielen.

haproxy config websockets
Erstmal habe ich in der vhost des Apaches dafür gesorgt das ws auf den richtigen Server geleitet wird.

vhost
   RewriteEngine On
   RewriteCond %{HTTP:Connection} Upgrade [NC]
   RewriteCond %{HTTP:Upgrade} websocket [NC]
   RewriteRule /demoapp/(.*) ws://demo-srv-01:3030/$1 [P,L]

   RedirectMatch           ^/demoapp$   TARGET-URL/demoapp/
   ProxyPass               /demoapp/    http://demo-srv-01:3030/
   ProxyPassReverse        /demoapp/    http://demo-srv-01:3030/

Dann hab ich die haproxy Konfiguration auf die Anwendung zugeschnitten.

haproxy
listen demoapp:3030
  bind *:3030
  # EIO ist immer gesetzt wenn über Websocket
  acl IsStatus urlp(EIO) 3
  use_backend ws_demoapp if IsStatus
  use_backend default if !IsStatus

backend ws_demoapp
  mode http
  # eventuell gesetzte Header löschen
  rspdel ^Server:.*
  rspdel ^X-Powered-By:.*
  # und durch Server demoapp Header ersetzen
  rspadd Server:\ demoapp
  # diese Seite muss HTTP-Status 200 liefern damit der HA erkennt das alles ok ist
  option httpchk HEAD /lbhealth
  # check auf Status wenn der 200 ist ist alles ok
  http-check expect status 200
  # using the `io` cookie set upon handshake
  cookie io prefix indirect nocache
  # Server Endpunkte
  server wsdemoapp01 ws-demo-srv-01:8200 check cookie monitor01
  server wsdemoapp02 ws-demo-srv-02:8200 check cookie monitor02

backend default
  mode http
  # eventuell gesetzte Header löschen
  rspdel ^Server:.*
  rspdel ^X-Powered-By:.*
  # und durch Server demoapp Header ersetzen
  rspadd Server:\ demoapp
  # Server Endpunkte wird normal gebalancet
  balance leastconn
  server node01 ws-demo-srv-01:80 check cookie node01
  server node02 ws-demo-srv-02:80 check cookie node02
Quellen & Info :
https://de.wikipedia.org/wiki/WebSocket
https://load-balancer.info/themen/sticky-session-load-balancing/
https://nodejs.org/en/about/
https://www.haproxy.org/#desc
https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API

SSH : Tunneln zu Maschine hinter Jumphost

Da ich mir das nie merken kann schreib ich das hier mal auf ;-)
Ich möchte die Status Seite eines haproxys in einem Netz anzeigen das nur über einen Jumphost erreichbar ist. Dafür braucht man 2 Tunnel einen auf den Jumphost und dann von Jumphost auf das Zielsystem. Der Jumphost und das Zielsystem müssen mit Key verbunden sein das ein Tunnel möglich ist. ( SSH Login mit authorized_keys )
ssh -L 9090:localhost:9090 USERNAME@JUMPHOST -i SSHKEY ssh -L 9090:localhost:9090 -N USERNAME@TARGETSYSTEM

Ubuntu ( 16.04 / 18.04 ) : SQL Developer von Oracle installieren

Ich bin seit langem nur noch auf Linux unterwegs und habe jetzt den SQL Developer von Oracle gebraucht. Unter Windows ist das ja nur eine setup.exe doppelklicken und durchklicken unter Ubuntu gibt es ein bisschen mehr das man beachten sollte.
So habe ich den SQL Developer unter Ubuntu 16.04 und Ubuntu 18.04 installiert.
Als erstes patchen wir das Ubuntu durch und installieren das openjdk-8-jdk (noch ist der Oracle SQL Developer nur bis Java 9.1 stable)
sudo apt update && sudo apt dist-upgrade -y && sudo apt install openjdk-8-jdk
Dann laden wir die gewünschte Version der Oracle SQL Developers von dieser Seite : Oracle SQL Developer (Other Platforms)
Jetzt installieren wir das Tool durch diese Befehle.
sudo unzip ~/Downloads/sqldeveloper/sqldeveloper-18.4.0-376.1900-no-jre.zip -d /usr/local/bin/
sudo ln -s /usr/local/bin/sqldeveloper/sqldeveloper.sh /bin/sqldeveloper
sudo sed -i 's#"`dirname $0`"#/usr/local/bin/sqldeveloper#g' /usr/local/bin/sqldeveloper/sqldeveloper.sh
Jetzt sollte der sqldeveloper einfach starten wenn man ihn im Terminal startet (durch sqldeveloper).Eventuell werdet ihr beim starten des SQL Developers nach der JDK Installation gefragt dann könnt ihr diesen Pfad angeben
/usr/lib/jvm/java-8-openjdk-amd64
sollte hier etwas schief gehen könnt ihr den Pfad direkt in den Configdateien überprüfen. [ username ] ist hier natürlich euer Benutzername.
  /home/[ username ]/.sqldeveloper/18.4.0/product.conf
  /usr/local/bin/sqldeveloper/sqldeveloper/bin/sqldeveloper.conf
Ich persönlich möchte den SQL Developer nicht immer aus dem Terminal starten deswegen habe ich mir noch eine .desktop datei angelegt.
sudo -s
echo -e "[Desktop Entry]\nName=Oracle SQL Developer\nExec=/usr/local/bin/sqldeveloper/sqldeveloper.sh\nIcon=/usr/local/bin/sqldeveloper/icon.png\nCategories=Development;Office;Science\nType=Application\nStartupNotify=false" > /usr/share/applications/sqldeveloper.desktop
Oracle SQL Developer
Quellen:
https://www.oracle.com/technetwork/developer-tools/sql-developer/downloads/index.html

Nextcloud 14 : Thunderbird Lightning

Mein Thunderbird Lightning konnte nach dem Update meiner Server und der Nextcloud nicht mehr auf die Kontakte und dem Kalender in der Nextcloud zugreifen. Nachdem ich etwas recherchiert habe konnte ich den Fehler lösen indem ich network.cookie.same-site.enabled auf false gesetzt habe.
Dazu geht ihr in Thunderbird auf Edit -> Preferences -> Advanced -> Config Editor dort sucht ihr network.cookie.same-site.enabled und setzt den Wert auf false. Nach einem Neustart von Thunderbird sollte alles wieder funktionieren. So wie es aussieht wird das auch erst in Nextcloud 15 gefixt. (NC15 - Fix)
https://github.com/nextcloud/server/issues/10134
https://bugzilla.mozilla.org/show_bug.cgi?id=1468912
“Das einzig sichere System müsste ausgeschaltet, in einem versiegelten und von Stahlbeton ummantelten Raum und von bewaffneten Schutztruppen umstellt sein.”
Gene Spafford (Sicherheitsexperte)