---
format: markdown
title: Ansible ff@home aufsetzen
toc: yes
...
# Software 
- Debian 12 (bookworm) oder Ubuntu 22.04
- python3 und ansible 

     ***** Es empfiehlt sich, eine python virtuelle Umgebung zu verwenden *****  
     Die folgenden Pakete mit Root-Rechten installieren, sofern noch nicht geschehen:    

     sudo apt install python3-pip python3-venv            

Die weiteren Befehle alle als normaler User ausführen.   

Im Basisverzeichnis (bei mir /datadisk) die Testumgebung herunterladen mit   

    git clone https:/...  

Für ein neues Projekt, welches noch nicht von einem git geklont werden kann, ein leeres Verzeichnis anlegen mit   

    mkdir ffhome  

Dann in das neue Verzeichnis ffhome wechseln und das lokale git Repository initialisieren mit
 
    git init 

Wenn das Verzeichnis geklont wurde, den Schritt "git init" überspringen.  
Nun im ffhome Verzeichnis (oder in geklonten Verzeichnis) (bei mir /datadisk/ffhome) die python virtuelle Umgebung einrichten:   

    python3 -m venv --prompt ffhome .venv    

Hierdurch wird in ffhome ein Unterverzeichnis .venv angelegt.    

Die virtuelle Umgebung wird in /datadisk/ffhome/.venv eingerichtet und kann aktiviert werden mit        

    source .venv/bin/activate     

Dadurch ändert sich der prompt:      

   (ffhome) 18:16:41[frankb@berglap /datadisk/ffhome ]

Jetzt kann ansible in der ffhome Umgebung installiert werden:   

    pip3 install ansible

Damit ist ansible nur in der virtuellen Umgebung installiert und ausführbar.

Zum späteren Verlassen der Umgebung:
deactivate oder Terminal Fenster schliessen



18:11:20[frankb@berglap /datadisk/ffhome 0] 
Die Verzeichnistruktur der Testumgebung    

    $ tree -L 4 /datadisk/ffhome    

        /datadisk/ffhome
        ├── ansible.cfg
        ├── inventory
        │   ├── hosts.yaml
        │   └── host_vars
        |       ├── bergdesk
        │       │   ├── vars
        │       │   └── vault
        │       ├── berghofen
        │       │   ├── vars
        │       │   └── vault
        │       ├── berglap
        │       │   ├── vars
        │       │   └── vault
        │       └── luna
        │           ├── vars
        │           └── vault
        ├── playbooks
        │   ├── resources
        │   │   └── host
        │   │       ├── tincbergdesk.gz
        │   │       └── tincberghofen.gz
        │   ├── templates
        │   │   ├── tinc-up.lan.j2
        │   │   └── tinc-up.wan.j2
        │   ├── tincbuild.yaml
        │   └── update.yaml
        

Bei den ... sind Zeilen der Übersichtlichkeit halber weggelassen.    

Weiter sind auch die versteckten Verzeichnisse .venv und evtl .git nicht aufgeführt.


Die verwendete ansible.cfg:   


    [defaults]
    inventory=./inventory/
    roles_path=./roles
    playbook_dir=./playbooks/
    interpreter_python=auto_silent
    #vault_password_file=~/.vault-password
    log_path=/tmp/ansible_ffhome.log
    cow_selection=random
    nocows=True
    stdout_callback=yaml
    display_args_to_stdout=True

    [privilege_escalation]
    become=True

    [ssh_connection]
    pipelining=True


Datei mit den beteiligten Hosts inventory/hosts.yaml

    ---
    all:
      vars:
        ansible_port: 24
        ansible_user: frankb
        ansible_become: true
    
    apus:
      hosts:
        berghofen:
          ansible_host: 192.168.178.51
          ansible_user: fb
          ansible_become_password: "{{ berghofen_password }}"
    
    desktops:
      hosts:
        bergdesk:
          ansible_host: 192.168.178.201
          ansible_become_pass: '{{ bergdesk_password }}'
    
        berglap:
          ansible_host: 192.168.178.52
          ansible_become_pass: '{{ berglap_password }}'
    
        luna:
          ansible_host: 192.168.178.224
          ansible_become_pass: '{{ luna_password }}'
    
    altlast:
      hosts:
        hoerde:
          ansible_host: 193.43.220.136
          ansible_become: true
          ansible_become_method: su
    
    supernodes:
      hosts:
        31.172.33.20:
          ansible_port: 22
        snng-dus01.ffdo.de:
          ansible_port: 22
        snng-dtm01.ffdo.de:
          ansible_port: 22

Die Gruppen apus und desktops enthalten die testhosts, die Gruppen altlast und supernodes sind nicht komplett einbezogen.


Im Verzeichnis inventory/host_vars sind Variablen für die einzelnen hosts, u.a. die passwords, in vars unverschlüsselt, in vault aes256 geschützt. Weiter sind Parameter für tinc enthalten, nur in vars, unverschlüsselt.
Beispielhaft für berghofen

$ cat berghofen/vars


    ---
    berghofen_password: "{{ vault_berghofen_password }}"
    tinc_bindto: 192.168.178.51
    wan_broadcast_ip: 193.43.220.191
    wan_ip: 193.43.220.162/27

    lan_broadcast_ip: 192.168.34.255
    lan_ip: 192.168.34.1/24


$ cat berghofen/vault


    $ANSIBLE_VAULT;1.2;AES256;xx
    35656536383233636434636533613830303439316263636436363932333636626462616461636537
    3838626266396332363236643361626134393238636133640a646333333866643161356333626564
    32373735343033633666353763376230646137663639373438393537663031643562376365396337
    3161646534666236350a303366373433373833373066353030363766616166666361376637393464
    30613139313661643932373239333865616338653132613530393161656466326561633537383535
    3631356664643139383037636565346630643036353364333866

In der vault Datei (hier Klartext)

    ---
    vault_berghofen_password: hier das echte PW eintragen


dann die vault Datei verschlüsseln mit 


    $ ansible-vault encrypt vault --vault-id xxxxx@prompt


anzeigen kann man die Datei mit 


    $ ansible-vault view vault   


und wieder entschlüsseln mit 


    $ ansible-vault decrypt vault




Es gibt z.Zt. zwei playbooks: update.yaml und tincbuild.yaml


$ cat playbooks/update.yaml


    ---
    # name: update yaml
    - hosts: [desktops,apus,altlast]
    
      tasks:
        - name: Testausgabe
          debug: msg="Hallo von {{ ansible_hostname }} Ansible managed!"
    
        - name: df -h Aufruf
          command: df -h /
          changed_when: false
          register: df_cmd
    
        - debug:
            msg: '{{df_cmd.stdout_lines}} {{ansible_distribution }}'
    
        - name: ping meine hosts
          ansible.builtin.ping:
          changed_when: false
    
    #   - name: Warte auf enter Taste
    #     ansible.builtin.pause:
    
        - name: Ist flatpak installiert
          stat:
            path: /usr/bin/flatpak
          register: flatpak_file
          when: ansible_os_family == "Debian"

        - name: Update flatpak Pakete falls snap vh
          command: /usr/bin/flatpak update
          when: ansible_os_family == "Debian" and flatpak_file.stat.exists

        - name: Ist snap installiert
          stat:
            path: /usr/bin/snap
          register: snap_file
          when: ansible_os_family == "Debian"

        - name: Update snap Pakete falls snap vh
          command: /usr/bin/snap refresh
          when: ansible_os_family == "Debian" and snap_file.stat.exists


        - name: apt update mit upgrade und autoremove
          ansible.builtin.apt:
            update_cache: yes
            cache_valid_time: 3600
            autoremove: yes
            upgrade: 'yes'
          when:  ansible_os_family == "Debian"

        - stat:
            path: /var/run/needrestart
          register: needrestart_file

        - name: reboot falls erforderlich
          ansible.builtin.reboot:
          when:
            - ansible_os_family == "Debian" and
             ( needrestart_file.stat.exists or
               rebootreq_file.stat.exists
             )

        - name: playbook hier beenden bei ! debian
          meta: end_play
          when: ansible_os_family != "Debian"

    

$ cat playbooks/tincbuild.yaml    

    ---
    # name: tincbuild yaml
    - hosts: [desktops, apus, altlast]
      vars:
        TINC_VERSION: 1.1pre18
        tinc_dev: /dev/net/tun
        tinc_mode: switch
        tinc_adr_family: ipv4
        tinc_max_timeout: 30

        tinc_port:
          lan: 10001
          wan: 661


      tasks:

    #   - name: Show facts available on the system
    #     ansible.builtin.debug:
    #       var: ansible_facts


    # das folgende klappt leider nicht {{tinc_port.{{ item }}}} syntaxfehler
    #  geschachtelte variablen gehen nicht.
    #   - name: willi
    #     debug: msg="var {{ tinc_port.{{lan}}}}"

        - name: Testausgabe
          debug: msg="Hallo von {{ ansible_hostname }} Ansible managed!"

        - name: df -h Aufruf
          command: df -h /
          changed_when: false
          register: df_cmd

        - debug:
            msg: '{{df_cmd.stdout_lines}} {{ansible_distribution }}'

    #    - name: ping meine hosts
    #      ansible.builtin.ping:
    #      changed_when: false

    #   - name: Warte auf enter Taste
    #     ansible.builtin.pause:


        - name: playbook hier beenden bei ! debian
          meta: end_play
          when: ansible_os_family != "Debian"


        - name: Ist tinc Konfigurationsverzeichnis vorhanden
          stat:
            path: /etc/tinc
          register: tinckonf

        - name: Gibt es Sicherung von tinc Konfiguration
          # ueberlebt keinen reboot
          stat:
            path: /tmp/tinc{{ ansible_hostname }}.gz
          register: tincgz

        - name: Konfiguration packen wenn noetig
          community.general.archive:
            path: /etc/tinc
            dest: /tmp/tinc{{ ansible_hostname }}.gz
          when: tinckonf.stat.exists

        - name: Gesicherte Konfiguration auf Controlnode kopieren
          ansible.builtin.fetch:
            src: /tmp/tinc{{ ansible_hostname }}.gz
            dest: resources/host/
            flat: true
          when: tinckonf.stat.exists

          #    - name: Gesicherte Konfiguration auf targetnode loeschen
          #      file:
          #        name: /tmp/tinc{{ ansible_hostname }}.gz
          #        state: absent
          #      when: tincgz.stat.exists

        - name: Abhängigkeiten fuer tinc installieren
          apt:
            pkg: "{{ item }}"
            state: present
          with_items:
            - build-essential
            - libncurses-dev
            - libreadline-dev
            - pkg-config
            - zlib1g-dev
            - liblzo2-dev
            - libssl-dev
            - texinfo

        - name: create directory for tinc
          file:
            name: /opt/tinc
            state: directory

        - name: Download tinc source
          get_url:
            url: "https://www.tinc-vpn.org/packages/tinc-{{TINC_VERSION}}.tar.gz"
            dest: /opt/tinc/tinc-{{TINC_VERSION}}.tar.gz
          register: gettinc

        - name: tinc-Quellen entpacken
          unarchive:
            src: /opt/tinc/tinc-{{TINC_VERSION}}.tar.gz
            dest: /usr/src
            remote_src: true
          when:
            - gettinc.changed

        - name: Pruefen tinc programm vh
          stat:
            path: /usr/sbin/tinc
          register: tinc_bin

        - name: tinc kompilieren und installieren
          shell: "cd /usr/src/tinc-{{TINC_VERSION}}
            &&    ./configure --prefix=/usr --sysconfdir=/etc --runstatedir=/run
                  --localstatedir=/var --with-systemd
            &&    make
            &&    make install"
          when:
            - tinc_bin.stat.exists == False or gettinc.changed

        - name: Gibt es schon eine (alte) tinc Konfiguration
          stat:
            path: /etc/tinc
          register: tinc_etc

        - name: tinc Konfigurationsreste beseitigen
          file:
            path: /etc/tinc
            state: absent
          when:
            - tinc_etc.stat.exists

        - name: tinc lan/wan vorkonfigurieren in mehreren Schritten
          shell: "tinc --net={{ item }} init {{ansible_hostname}}"
          with_items:
            - lan
            - wan
    #     when:
    #       - tinc_bin.stat.exists == False or gettinc.changed

        - name: 2048er priv und pub keys löschen
          shell: "rm /etc/tinc/{{ item }}/*.priv
            &&    rm /etc/tinc/{{ item }}/hosts/*"
          with_items:
            - lan
            - wan
    #     when:
    #       - tinc_bin.stat.exists == False or gettinc.changed

        - name: 4096er keys generieren
          shell: "tinc --net={{ item }} -b generate-keys 4096"
          with_items:
            - lan
            - wan
    #     when:
    #       - tinc_bin.stat.exists == False or gettinc.changed

        - name: tinc.conf einrichten
          shell: "tinc --net={{ item }} set Device {{tinc_dev}}
            &&    tinc --net={{ item }} set Mode {{tinc_mode}}
            &&    tinc --net={{ item }} set AddressFamily {{tinc_adr_family}}
            &&    tinc --net={{ item }} set MaxTimeout {{tinc_max_timeout}}
            &&    tinc --net={{ item }} set BindToAddress {{tinc_bindto}}"
          with_items:
            - lan
            - wan
    #     when:
    #       - tinc_bin.stat.exists == False or gettinc.changed

        - name: lan/wan ports setzen
          # das geht leider nicht in loop, da Variablen nicht geschachtelt
          shell: "tinc --net=lan set Port {{ tinc_port.lan }}
            &&    tinc --net=wan set Port {{ tinc_port.wan }}"

        - name: tinc-up anpassen
          ansible.builtin.template:
            src: templates/tinc-up.{{ item }}.j2
            dest: /etc/tinc/{{ item }}/tinc-up
          with_items:
            - lan
            - wan
    #     when:
    #       - tinc_bin.stat.exists == False or gettinc.changed

        - meta: end_play


Für tinc-up werden folgende templates verwendet    

$ cat playbooks/templates/tinc-up.lan.j2

    #!/bin/sh

    ip addr add {{ lan_ip }} brd {{ lan_broadcast_ip }} dev $INTERFACE
    ip link set $INTERFACE mtu 1504 up



$ cat playbooks/templates/tinc-up.wan.j2

    #!/bin/sh

    ip addr add {{ wan_ip }} brd {{ wan_broadcast_ip }} dev $INTERFACE
    ip link set $INTERFACE mtu 1504 up


Aufruf der beiden playbooks mit

    $ ansible-playbook -b  playbooks/update.yaml -i inventory/hosts.yaml --ask-vault-pass 
    $ ansible-playbook -b  playbooks/tincbuild.yaml -i inventory/hosts.yaml --ask-vault-pass

Gekürzte Ausgabe:


    (ffhome) 20:55:59[frankb@berglap /datadisk/ffhome 4] ansible-playbook -b  playbooks/update.yaml -i inventory/hosts.yaml --ask-vault-pass 
    Vault password: 
    
    PLAY [desktops,apus,altlast] ***************************************************************
    TASK [Gathering Facts] *********************************************************************
    [WARNING]: Platform linux on host berglap is using the discovered Python interpreter at
    /usr/bin/python3.10, but future installation of another Python interpreter could change the
    meaning of that path. See https://docs.ansible.com/ansible-
    core/2.17/reference_appendices/interpreter_discovery.html for more information.
    ok: [berglap]
    [WARNING]: Platform linux on host bergdesk is using the discovered Python interpreter at
    /usr/bin/python3.11, but future installation of another Python interpreter could change the
    meaning of that path. See https://docs.ansible.com/ansible-
    core/2.17/reference_appendices/interpreter_discovery.html for more information.
    ok: [bergdesk]
    fatal: [luna]: UNREACHABLE! => changed=false 
      msg: 'Failed to connect to the host via ssh: ssh: connect to host 192.168.178.224 port 24: No route to host'
      unreachable: true
    [WARNING]: Platform linux on host berghofen is using the discovered Python interpreter at
    /usr/bin/python3.11, but future installation of another Python interpreter could change the
    meaning of that path. See https://docs.ansible.com/ansible-
    core/2.17/reference_appendices/interpreter_discovery.html for more information.
    ok: [berghofen]
    [WARNING]: Platform freebsd on host hoerde is using the discovered Python interpreter at
    /usr/local/bin/python3.9, but future installation of another Python interpreter could
    change the meaning of that path. See https://docs.ansible.com/ansible-
    core/2.17/reference_appendices/interpreter_discovery.html for more information.
    ok: [hoerde]
    
    TASK [Testausgabe] *************************************************************************
    ok: [bergdesk] => 
      msg: Hallo von bergdesk Ansible managed!
    ok: [berglap] => 
      msg: Hallo von berglap Ansible managed!
    ok: [berghofen] => 
      msg: Hallo von berghofen Ansible managed!
    ok: [hoerde] => 
      msg: Hallo von hoerde Ansible managed!
    
    TASK [df -h Aufruf] ************************************************************************
    ok: [berglap]
    ok: [bergdesk]
    ok: [berghofen]
    ok: [hoerde]
    
    TASK [debug] *******************************************************************************
    ok: [bergdesk] => 
      msg: '[''Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf'', ''/dev/sdb1       439G     98G  319G   24% /''] Debian'
    ok: [berglap] => 
      msg: '[''Dateisystem             Größe Benutzt Verf. Verw% Eingehängt auf'', ''/dev/mapper/system-root  444G    298G  124G   71% /''] Ubuntu'
    ok: [berghofen] => 
      msg: '[''Dateisystem    Größe Benutzt Verf. Verw% Eingehängt auf'', ''/dev/sda6        18G    5,1G   12G   30% /''] Debian'
    ok: [hoerde] => 
      msg: '[''Filesystem                       Size    Used   Avail Capacity  Mounted on'', ''s3pool25/jail/hoerde.ffdo.net    3.9G    891M    3.0G    22%    /''] FreeBSD'
    
    TASK [ping meine hosts] ********************************************************************
    ok: [berglap]
    ok: [bergdesk]
    ok: [berghofen]
    ok: [hoerde]
    
    ...
    
    PLAY RECAP *********************************************************************************
    bergdesk                   : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    berghofen                  : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    berglap                    : ok=6    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
    hoerde                     : ok=5    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   
    luna                       : ok=0    changed=0    unreachable=1    failed=0    skipped=0    rescued=0    ignored=0   
    

