Eigenes Debian/Ubuntu Repository aufbauen

Kurzanleitung zum Aufbau eines APT Paket Repository

In dem Repo wird das hallo-1.0.0.deb verwendet um die Funktion von reprepro zu zeigen, wie eigene DEB Pakete erstellt werden können könnt Ihr hier lesen.

Installation der benötigten Pakete

apt install reprepro apache2 gnupg

Erstellen eines GnuPG keys

Mit diesem GPG Schlüssel wird das Repository und die Pakete signiert. Der Public Key muss auf den Zielsystemen als Trusted Key hinterlegt werden. (siehe unten)

Beim Erstellen des Keys muss man ein Passwort angeben. Nur mit diesem Passwort ist es möglich neue Pakete zum Repository hinzuzufügen und zu signieren.

gpg --gen-key

...

Ihr Name ("Vorname Nachname"): Mein APT Repo Key
Email-Adresse: example@example.org
Sie haben diese User-ID gewählt:
    "Mein APT Repo Key <example@example.org>"

Ändern: (N)ame, (E)-Mail oder (F)ertig/(A)bbrechen? f

...


Öffentlichen und geheimen Schlüssel erzeugt und signiert.

pub   rsa3072 2020-10-11 [SC] [verfällt: 2022-10-11]
      4A2E138451DD449DF3FC2C7A28C7770DD3A8D10D
uid                      Mein APT Repo Key <example@example.org>
sub   rsa3072 2020-10-11 [E] [verfällt: 2022-10-11]

Der Key hat eine Gültigkeit von 2 Jahren, man kann die Gültigkeit verlängern. Das geht mit „gpg –edit-key <key_id>“. Mit „expire“ kann die Gültigkeit des Keys geändert werden.

gpg --edit-key 4A2E138451DD449DF3FC2C7A28C7770DD3A8D10D


gpg (GnuPG) 2.2.12; Copyright (C) 2018 Free Software Foundation, Inc.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Geheimer Schlüssel ist vorhanden.

sec  rsa3072/28C7770DD3A8D10D
     erzeugt: 2020-10-11  verfällt: 2022-10-11  Nutzung: SC
     Vertrauen: ultimativ     Gültigkeit: ultimativ
ssb  rsa3072/236EB690D64E39D8
     erzeugt: 2020-10-11  verfällt: 2022-10-11  Nutzung: E
[ ultimativ ] (1). Mein APT Repo Key <example@example.org>



gpg> expire
Ändern des Verfallsdatums des Hauptschlüssels.
Bitte wählen Sie, wie lange der Schlüssel gültig bleiben soll.
         0 = Schlüssel verfällt nie
      <n>  = Schlüssel verfällt nach n Tagen
      <n>w = Schlüssel verfällt nach n Wochen
      <n>m = Schlüssel verfällt nach n Monaten
      <n>y = Schlüssel verfällt nach n Jahren
Wie lange bleibt der Schlüssel gültig? (0) 10y
Key verfällt am Mi 09 Okt 2030 21:37:42 CEST
Ist dies richtig? (j/N) j

sec  rsa3072/28C7770DD3A8D10D
     erzeugt: 2020-10-11  verfällt: 2030-10-09  Nutzung: SC
     Vertrauen: ultimativ     Gültigkeit: ultimativ
ssb  rsa3072/236EB690D64E39D8
     erzeugt: 2020-10-11  verfällt: 2022-10-11  Nutzung: E
[ ultimativ ] (1). Mein APT Repo Key <example@example.org>


gpg> key 1
gpg> expire
Ändern des Verfallsdatums des Unterschlüssels.
Bitte wählen Sie, wie lange der Schlüssel gültig bleiben soll.
         0 = Schlüssel verfällt nie
      <n>  = Schlüssel verfällt nach n Tagen
      <n>w = Schlüssel verfällt nach n Wochen
      <n>m = Schlüssel verfällt nach n Monaten
      <n>y = Schlüssel verfällt nach n Jahren
Wie lange bleibt der Schlüssel gültig? (0) 10y
Key verfällt am Mi 09 Okt 2030 21:37:42 CEST
Ist dies richtig? (j/N) j

sec  rsa3072/28C7770DD3A8D10D
     erzeugt: 2020-10-11  verfällt: 2030-10-09  Nutzung: SC
     Vertrauen: ultimativ     Gültigkeit: ultimativ
ssb  rsa3072/236EB690D64E39D8
     erzeugt: 2020-10-11  verfällt: 2030-10-09  Nutzung: E
[ ultimativ ] (1). Mein APT Repo Key <example@example.org>


gpg> quit
Änderungen speichern? (j/N) j



Erstellen des REPO Verzeichnis

mkdir -p /var/www/repos/apt/debian
mkdir /var/www/repos/apt/debian/conf

Erstellen der Apache Konfiguration

In dem einfachen Beispiel habe ich einfach die Default Virtual Host Konfiguration ersetzt/angepasst, diese liegt unter /etc/apache2/sites-enabled/000-default

<VirtualHost *:80>
        ServerAdmin webmaster@localhost
        DocumentRoot /var/www

        ErrorLog ${APACHE_LOG_DIR}/error.log
        CustomLog ${APACHE_LOG_DIR}/access.log combined

        <Directory /var/www/repos/ >
                Options Indexes FollowSymLinks Multiviews
                Require all granted
        </Directory>

        <Directory "/var/www/repos/apt/*/db/">
                Require all denied
        </Directory>

        <Directory "/var/www/repos/apt/*/conf/">
                Require all denied
        </Directory>

        <Directory "/var/www/repos/apt/*/incoming/">
                Require all denied
        </Directory>
</VirtualHost>

Entfernen von unnötigen Files im Webroot /var/www

rm -rf /var/www/html

Testen der Apache Konfiguration und Neustart des Services

root@repodev:~# apache2ctl configtest
Syntax OK
root@repodev:~# /etc/init.d/apache2 reload
[ ok ] Reloading apache2 configuration (via systemctl): apache2.service.

Erstellen der distributions Datei

Die Datei beschreibt das Repository und für welche Distribution das Repo ist. Bei „SignWith“ wird die Key ID verwendet von dem eben erzeugten GPG Key.

Bei Codename wird der OS Release Name angegeben, z.B. bei Debian „buster“ oder bei Ubuntu „bionic“, das hängt von dem jeweiligen Release ab. Ist es ein generisches Repo dann kann hier auch z.B. „all“ genommen werden.

/var/www/repos/apt/debian/conf/distributions

Origin: Mein APT Repo
Label: Mein APT Repo
Codename: <osrelease>
Architectures: i386 amd64
Components: main
Description: APT repository fuer meine Pakete
SignWith: 4A2E138451DD449DF3FC2C7A28C7770DD3A8D10D

/var/www/repos/apt/debian/conf/options

verbose
basedir /var/www/repos/apt/debian
ask-passphrase

Exportieren des GnuPG Keys

Der GPG Key muss in den APT Keyring auf dem Zielsystem aufgenommen werden damit man Pakete installieren kann. Dazu exportieren wir Ihn und stellen Ihn auf dem Webserver bereit.

gpg --armor --output /var/www/repo_key.key --export 4A2E138451DD449DF3FC2C7A28C7770DD3A8D10D

Ein Paket veröffentlichen

Mit folgendem Befehl wird ein neues Paket z.B. hallo_1.0.0.deb dem Repository hinzugefügt. Wird das Kommando ausgeführt fragt der Assistent nach dem Passwort für den GPG Key.

# reprepro -b /var/www/repos/apt/debian includedeb <osrelease> /root/hallo_1.0.0.deb

/root/hallo-1.0.0.deb: component guessed as 'main'
Exporting indices...




Im Browser kann man ebenfalls prüfen ob die Veröffentlichung geklappt hat, dazu http://<repo>/repos/apt/debian/pool/main/h/hallo/ aufrufen:

Installieren von hallo aus eigenem Repository

Als erstes muss der Public Key des Repositorys zur Keychain hinzugefügt werden.

wget -O - http://10.10.10.10/repo_key.key|apt-key add -

Erstellen einer sources.list.d Datei

Die Datei wird verwendet um das Repo einzubinden.

/etc/apt/sources.list.d/mein_repo.list

deb http://10.10.10.10/repos/apt/debian <osrelease> main

Aktualisieren von APT Quellen

# apt update


Holen:1 http://10.10.10.10/repos/apt/debian all InRelease [2.331 B]
Holen:2 http://10.10.10.10/repos/apt/debian all/main amd64 Packages [1.018 B]
OK:3 http://deb.debian.org/debian buster InRelease
Holen:4 http://deb.debian.org/debian buster-updates InRelease [51,9 kB]
OK:5 http://security.debian.org/debian-security buster/updates InRelease
Es wurden 55,2 kB in 1 s geholt (86,7 kB/s).
Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.
Statusinformationen werden eingelesen.... Fertig
Alle Pakete sind aktuell.

Installieren ….

# apt install hallo

Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.
Statusinformationen werden eingelesen.... Fertig
Die folgenden NEUEN Pakete werden installiert:
  hallo
0 aktualisiert, 1 neu installiert, 0 zu entfernen und 0 nicht aktualisiert.
Es müssen 776 B an Archiven heruntergeladen werden.
Nach dieser Operation werden 0 B Plattenplatz zusätzlich benutzt.
Holen:1 http://10.10.10.10/repos/apt/debian all/main amd64 hallo all 1.0-0 [776 B]
Es wurden 776 B in 0 s geholt (0 B/s).
Vormals nicht ausgewähltes Paket hallo wird gewählt.
(Lese Datenbank ... 35202 Dateien und Verzeichnisse sind derzeit installiert.)
Vorbereitung zum Entpacken von .../archives/hallo_1.0-0_all.deb ...
Entpacken von hallo (1.0-0) ...
hallo (1.0-0) wird eingerichtet ...

Viel Spaß 😉

Eigene Debian/Ubuntu Pakete bauen

Dies ist eine Kurzanleitung wie man selber einfache Debian/Ubuntu Pakete bauen kann um z.B. eigene Skripte etc. zu verteilen.

Als erstes wird ein Projekt Ordner erstellt. z.B. hallo-1.0.0

mkdir ~/hallo-1.0.0

Die Struktur innerhalb des Ordners wird so auf das System kopiert, legt Ihr z.B. eine „hallo“ Datei in ~/hallo-1.0.0/usr/bin ab wird sie im Zielsystem in /usr/bin installiert.

mkdir -p ~/hallo-1.0.0/usr/bin
mkdir -p ~/hallo-1.0.0/DEBIAN

Im Ordner DEBIAN liegen die Dateien die für das bauen des Pakets benötigt werden.

nano ~/hallo-1.0.0/usr/bin/hallo

Hier ein kleines Testscript das wir verteilen wollen:

#!/bin/sh
echo "Hallo :-)"

Rechte die für Dateien gesetzt sind werden übernommen.

chmod +x ~/hallo-1.0.0/usr/bin/hallo

Jetzt noch die „control“ Datei im Ordner DEBIAN erstellen. Die Datei beinhaltet den Namen, die Beschreibung und Abhänigkeiten.

Package: hallo
Version: 1.0-0
Section: base
Priority: optional
Architecture: all
Maintainer: "Der Packetbauer <example@example.org>"
Description: Ein Skript das Hallo sagt

Das bauen des Pakets wird mit dpkg-deb gemacht.

dpkg-deb --build ~/hallo-1.0.0/

Ist die Erstellung des Pakets erfolgreich kommt folgende Meldung:

mt@dev:~# dpkg-deb --build hallo-1.0.0/
dpkg-deb: Paket »hallo« wird in »hallo-1.0.0.deb« gebaut.

Im Verzeichnis sollte jetzt eine hallo-1.0.0.deb vorhanden sein.

Kleine Kontrolle:

mt@dev:~# dpkg -I hallo-1.0.0.deb
 neues Debian-Paket, Version 2.0.
 Größe 776 Byte: control-Archiv= 340 Byte.
     172 Byte,     7 Zeilen      control
 Package: hallo
 Version: 1.0-0
 Section: base
 Priority: optional
 Architecture: all
 Maintainer: "Der Packetbauer <example@example.org>"
 Description: Ein Skript das Hallo sagt

Jetzt kann man versuchen das Paket zu installieren:

mt@dev:~# dpkg -i hallo-1.0.0.deb
Vormals nicht ausgewähltes Paket hallo wird gewählt.
(Lese Datenbank ... 34468 Dateien und Verzeichnisse sind derzeit installiert.)
Vorbereitung zum Entpacken von hallo-1.0.0.deb ...
Entpacken von hallo (1.0-0) ...
hallo (1.0-0) wird eingerichtet ...

Die Installation war erfolgreich, in /usr/bin ist jetzt die Datei hallo und ist ausführbar.

mt@dev:~# ls -la /usr/bin/hallo
-rwxr-xr-x 1 root root 27 Okt 11 16:52 /usr/bin/hallo

mt@dev:~# hallo
Hallo :-)

Beim Entfernen des Pakets werden die Dateien entsprechend entfernt.

mt@dev:~# apt remove hallo
Paketlisten werden gelesen... Fertig
Abhängigkeitsbaum wird aufgebaut.
Statusinformationen werden eingelesen.... Fertig
Die folgenden Pakete werden ENTFERNT:
  hallo
0 aktualisiert, 0 neu installiert, 1 zu entfernen und 0 nicht aktualisiert.
Nach dieser Operation werden 0 B Plattenplatz zusätzlich benutzt.
Möchten Sie fortfahren? [J/n] j
(Lese Datenbank ... 34469 Dateien und Verzeichnisse sind derzeit installiert.)
Entfernen von hallo (1.0-0) ...

Natürlich ist noch wesentlich mehr möglich und das ist ein einfaches Beispiel. Zum Nachlesen eignen sich folgende Seiten:

https://wiki.debian.org/Packaging/Intro?action=show&redirect=IntroDebianPackaging

https://packaging.ubuntu.com/html/

borgbackup und borgmatic mit pip installieren

borgmatic ist erst in Ubuntu 20.04 in den offiziellen Paketquellen und wenn man das aktuellste haben möchte empfiehlt sich die Installation über pip.

Benötigte Pakete

apt install python3-setuptools python3-dev python3-pip libssl-dev libacl1-dev build-essential

Anschließend mit pip3 borgmatic und borgbackup installieren:

pip3 install borgbackup borgmatic

Ich hatte bei mir das Problem das borgmatic und borg in den cronjobs nicht funktioniert haben. borgmatic/borgbackup werden in /usr/local/bin installiert das per Default dem Cron nicht bekannt sind. In der Crontab folgende Ergänzung durchführen (Achtung diese Änderung ist global und gilt für alle Cronjobs):

# m h  dom mon dow   command

PATH=/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin
LD_LIBRARY_PATH=/usr/local/lib

24 23 * * * borgmatic -v 1 2>&1 | mail -s "Backup Report" example@example.com

SMTP Mailserver testen mit swaks

swaks – Swiss Army Knife SMTP, the all-purpose smtp transaction tester

Unter Ubuntu/Debian lässt sich swaks einfach installieren:

apt install swaks

Hier ein paar einfache Testcases um verschiedene Einlieferungsverfahren zu testen:

SMTP ohne Authentifizierung testen

swaks --server <server_to_test> --from absender@example.com --to empfaenger@example.net

SMTP mit Authentifizierung (PLAIN)

swaks --server <server_to_test> --from absender@example.com --to empfaenger@example.net --auth --auth-user absender@example.com --auth-password foobar

SMTP mit Authentifizierung (SMTPS auf Port 465)

swaks --server <server_to_test> --from absender@example.com --to empfaenger@example.net --auth --auth-user absender@example.com --auth-password foobar --port 465 -tlsc

SMTP mit Authentifizierung (SUBMISSION auf Port 587)

swaks --server <server_to_test> --from absender@example.com --to empfaenger@example.net --auth --auth-user absender@example.com --auth-password foobar --port 587 -tls

GITEA Install script for Ubuntu 20.04 LTS

Get source code: https://github.com/lanbugs/gitea_installer

This script pulls the newest version of gitea and install all requirements.

The following will be installed:

  • GIT
  • Nginx
  • MariaDB
  • Letencrypt (optional)
  • UFW (optional)

Use it 😉

This installer script is for Ubuntu 20.04 LTS, other versions may work but not tested.

Available flags:

-f FQDN - Systemname of GITEA system
-e EMAIL - E-Mail for letsencrypt
-i IP - IPv4 address of this system
-p PASSWORD - Used for GITEA DB
-r SQLROOT - MySQL ROOT password
-l LETSENCRYPT - Use letsencrypt
-u UFW - Use UFW

Install

wget https://github.com/lanbugs/gitea_installer/raw/main/gitea_installer.sh
chmod +x gitea_installer.sh

./gitea_installer.sh -f git.example.com -e admin@example.com -i 10.10.10.10 -p securepassword -r sqlrootpw -l -u

Finished screen:

--------------------------------------------------------------------------------------
 GITEA 1.12.5 installed on system git.example.com
--------------------------------------------------------------------------------------
 Mysql database        : giteadb
 Mysql user            : gitea
 Mysql password        : securepassword
 Mysql character set   : utf8mb4
--------------------------------------------------------------------------------------
 Mysql root user       : root
 Mysql root password   : sqlrootpw
--------------------------------------------------------------------------------------
 System is accessable via https://git.example.com
--------------------------------------------------------------------------------------
 >>> You must finish the initial setup <<<
--------------------------------------------------------------------------------------
 Site Title            : Enter your organization name.
 Repository Root Path  : Leave the default /home/git/gitea-repositories.
 Git LFS Root Path     : Leave the default /var/lib/gitea/data/lfs.
 Run As Username       : git
 SSH Server Domain     : Use git.example.com
 SSH Port              : 22, change it if SSH is listening on other Port
 Gitea HTTP Listen Port: 3000
 Gitea Base URL        : Use https://git.example.com/
 Log Path              : Leave the default /var/lib/gitea/log
--------------------------------------------------------------------------------------
 Following firewall rules applied:
Status: active

     To                         Action      From
     --                         ------      ----
[ 1] 22/tcp                     ALLOW IN    Anywhere
[ 2] 80/tcp                     ALLOW IN    Anywhere
[ 3] 443/tcp                    ALLOW IN    Anywhere
[ 4] 22/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 5] 80/tcp (v6)                ALLOW IN    Anywhere (v6)
[ 6] 443/tcp (v6)               ALLOW IN    Anywhere (v6)

--------------------------------------------------------------------------------------

When the script finished you must complete the installation of gitea in the web UI.

SQL settings

You should choose utf8mb4 encoding.

SQL

Common settings

Common

Additional settings

Common

Check_MK: Export hosts to SecureCRT Session Manager

This script exports over an Check_MK View hosts and imports them in SecureCRT Session Manager.

Get source code: https://github.com/lanbugs/check_mk_to_securecrt_export

The bundled python of SecureCRT not support urllib / urllib2 (see https://forums.vandyke.com/showthread.php?t=13127). I don`t want to modify the bundled python version of SecureCRT, so i have decided to put the code in an extra binary. The advantage is that you can deploy the binary to every client without install python on every client. You can encrypt the source code (that is NOT 100% safe) so the credentials stored in the helper are not visable.

Sessions will be imported in the Folder _Check_MK_Imports. Existing Sessions will be overwirtten in the folder. Note: If a host is removed in Check_MK it will not be removed in SecureCRT. This is a feature improvement, for the moment simple delete the _Check_MK_Imports folder and sync it again.

Requirements

  • Installed python 3.8 with pyinstaller and tinyaes installed.
  • Automation User on Check_MK instance
  • Extra view in Check_MK with the hosts to export to SecureCRT

Install python requirements on dev machine

pip install pyinstaller[encryption] tinyaes

Create automation user in Check_MK

Check_MK Create User
  • Create user as normal monitoring user with automation secret

Create view in Check_MK

Go to edit views

Check_MK Edit Views

Clone allhosts view, click on the copy symbol.

Check_MK Allhosts view

Modify name of the view, hide the view from sidebar and make it available for all.

Check_MK Allhosts view

Modify the view that only 3 colums are present. Remove sorting and grouping, it is not required.

Check_MK Allhosts view

You can apply custom filtering for the view if you want e.g. filter on a special hostgroup.

Test the view

You can try to access the view with your securecrt automation user in every browser, the response should be json content.

https://<check_mk host>/<check_mk instance>/check_mk/view.py?view_name=<view name>&output_format=json&_username=<securecrt automation user>&_secret=<password>

Example resonse

[
 [
  "host", 
  "host_ipv4_address", 
  "wato_folder_abs"
 ], 
 [
  "router", 
  "2.2.2.2", 
  "Main directory / network"
 ], 
 [
  "lalala", 
  "1.2.3.4", 
  "Main directory / network"
 ], 
 [
  "lololo", 
  "2.3.4.5", 
  "Main directory / network / test"
 ], 
 [
  "linux", 
  "1.2.3.6", 
  "Main directory / network"
 ]
]

Modify sourcecode of the helper

Add the correct URL and credentials in the file.

########################################################################################################################
# Settings

# User to interact with check_mk, user need automation secret!
user = "securecrt"

# Automation secret
secret = "OYPCCOJCCMDPQSCLYFOI"

# URL to check_mk instance with ? at the end
url = "https://checkmk.company.local/core/check_mk/view.py?"

# View which shows prefiltered the hosts you want to create sessions in check_mk
# Only tree columns are allowed in the view:
# hostname,ip,wato_folder_abs
# See details on https://github.com/lanbugs/check_mk_to_securecrt_export or https://lanbugs.de
# You can test it manually if you browse the url in an browser.
# e.g. https://checkmk.company.corp/central_mon/check_mk/view.py?view_name=allhosts_securecrt&_username=securecrt&_secret=OYPCCOJCCMDPQSCLYFOI&output_format=json
# You should see an json output with the 3 columns.
view_name = "allhosts_securecrt"

Build the helper binary

Without encryption

c:\Python38\python.exe -OO -m PyInstaller -D check_mk_to_securecrt_export_helper.py

With encryption

Define an 16 char encryption key like 0123456789ABCDEF (not safe!)

c:\Python38\python.exe -OO -m PyInstaller --key=0123456789ABCDEF -D check_mk_to_securecrt_export_helper.py

Deploy files

Create a folder like c:\securecrt_scripts

  1. Copy the dist folder to c:\securecrt_scripts
  2. Copy the check_mk_to_securecrt_export.py to c:\securecrt_scripts

Use it!

Run it once …

Goto Script > Run …

SecureCRT RUN

Select check_mk_to_securecrt_export.py

SecureCRT RUN

Run it 🙂

Add button to button bar

Enable the button bar.

View > Button Bar

SecureCRT RUN

On the bottom of SecureCRT you can see now the button bar. Right click on the bar and click „New Button…“

SecureCRT Button Bar

Select in the dropdown Function „Run Script“

SecureCRT Button Bar Add

Choose in „Run Script“ the check_mk_to_securecrt_export.py, in Arguments you can add your username, every session will be created with thhis username.

SecureCRT Button Bar Config

Finished 🙂

SecureCRT Button Bar Button

Finally

SecureCRT Session Manager

Have fun 🙂

Known bugs

  • Sometimes you must sync twice that all sessions are created, this is under investigation.

Backup mit Borgmatic und Borg zu eigenem Backup Server via SSH

Borgmatic ist ein Tool um borgbackup zu steuern. Hier eine kleine Anleitung.

Backup Server vorbereiten

Erstellen eines Backup Users

Unter diesem User werden die Backup Repos zur Verfügung gestellt.

adduser backupmaster

Installieren von Borg

Bei Ubuntu/Debian befindet sich borgbackup im Standard Repo.

apt install borgbackup

Repo anlegen

Für jedes Backup habe ich ein eigenes Repo, dies wird auf dem Backup Server initial mit borg init angelegt.

borg init -e repokey-blake2 backuprepo_srv1

SSH Key generieren für Borgbackup

Für jedes Repo wird ein individueller SSH Key erzeugt, dieser wird verwendet um sich per Public/Private Key gegen den Backup Server zu authentifizieren.

ssh-keygen -t ed25519 -C borgbackup

SSH Key für Repo berechtigen in authorized_keys

Der Public Key wird in die authorized_keys eingetragen mit dem command=““ Block. Wenn sich jemand mit dem Private Key anmeldet wird das angegebene Kommando ausgeführt. In diesem Fall wird borg serve gestartet, es wird das Repo bereitgestellt.

su - backupmaster
mkdir .ssh
touch .ssh/authorized_keys

Inhalt authorized_keys

Bei dem borg serve Kommando wird der Pfad des Borg Repos angegeben. append-only erlaubt ausschließlich das hinzufügen zum Repo, ein gesamtes Löschen ist nicht möglich. Siehe https://borgbackup.readthedocs.io/en/stable/usage/notes.html#append-only-mode

command="borg serve --restrict-to-path /home/backupmaster/backuprepo_srv1 --append-only" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIHEdIlaEyXfB1FlzRioCOQboN+ZWRTG2nawkDuRb4oUm borgbackup

Backup auf einer Maschine einrichten

Installieren der nötigen Pakete

Die benötigten Pakete sind alle im Standard Repo bei Ubuntu/Debian dabei.

apt install borgmatic borgbackup mailutils

Conf. Verzeichnis erstellen

Für Borgmatic muss ein Konfigurationsverzeichnis erstellt werden. In diesem Verzeichnis wird eine YAML Datei abgelegt mit den Backup Einstellungen.

mkdir -p /root/.config/borgmatic

config.yaml

In source_directories werden alle zu sichernden Ordner als Liste angegeben. Bitte auf die korrekte Formatierung der YAML Datei achten. Unter repositories muss der Backupserver incl. Repo Namen angegeben werden. Unter storage kann man einen Encryption Key angeben für das Backup falls der Backupserver z.B. im Internet steht. Bei ssh_command wird der Private Key des oben erzeugten SSH Keys angegeben, diesen nach /root/.config/borgmatic/id_ed25519 kopieren. Bei retention kann die Aufbewahrungszeit der Backups festgelegt werden. Wer noch Scripte vor oder nach dem Backup starten möchte kann das unter „hooks“ machen. In meinem Fall exportiere ich die Liste der installierten Pakete in eine Datei.

location:
    source_directories:
        - /etc
        - /srv
        - /opt
        - /var/lib
        - /var/mail
        - /var/www
        - /root
        - /home

    one_file_system: true

    repositories:
        - backupmaster@backupserver.example.org:backuprepo_srv1

    exclude_caches: true

storage:
    compression: auto,zstd
    encryption_passphrase: CHANGE ME
    archive_name_format: '{hostname}-{now}'
    ssh_command: ssh -i /root/.config/borgmatic/id_ed25519 -p 22

retention:
    keep_daily: 3
    keep_weekly: 7
    keep_monthly: 12
    keep_yearly: 2
    prefix: '{hostname}-'

consistency:
    checks:
        - repository
        - archives

    check_last: 3
    prefix: '{hostname}-'

hooks:
    before_backup:
        - dpkg-query -f '${binary:Package}\n' -W > /root/packages_list.txt

Cronjob für regelm. Backups

Damit das Backup auch regelmäig läuft empfiehlt es sich einen Cronjob einzurichten. In diesem Beispiel jeden Tag um 23:15, das Ergebnis schicke ich mir als E-Mail.

contab -e


15 23 * * * borgmatic -v 1 2>&1 | mail -s "Backup Report" admin@example.org

Viel Spaß 🙂

Schnelle rsync Alternative für Filetransfer

Auf der Suche nach einer alternative zu RSYNC für einen einmaligen Filetransfer zwischen Linux Systemen bin ich auf folgenden Artikel gestoßen. Die Methode eignet sich besonders wenn das Zielsystem leer ist und keine Files verglichen werden müssen.

https://www.schirmacher.de/display/Linux/Fastest+rsync+command

root@sourcesystem:/home/sourcefolder# tar cf - * | mbuffer -m 1024M | ssh 10.23.49.1 '(cd /home/targetfolder; tar xf -)'
in @ 120.1 MB/s, out @ 84.3 MB/s, 123.3 GB total, buffer  68% full

Debian/Ubuntu Liste installierter Pakete exportieren und diese Liste auf einem neuen System installieren

Mit diesem Befehl lassen sich die installieren Pakete in eine Liste exportieren.

sudo dpkg-query -f '${binary:Package}\n' -W > packages_list.txt

Mit xargs lässt sich apt install z.B. auf einem neuen System füttern.

sudo xargs -a packages_list.txt apt install

ssm – SSH Shell Menu

SSH Shell Menu written in Python

Source: https://github.com/lanbugs/ssm

This is a small python script for a shell menu to manage and open ssh connections.

Requirements

  • python3
  • dialog
  • pythondialog

Installation

Install Debian packages and pip packages

apt install python3 python3-pip python3-setuptools dialog
pip3 install -r requirements.txt

Copy ssm to /usr/bin/local

cp ssm /usr/bin/local
chmod +x /usr/bin/local/ssm

Create connections.ini

Create folder in home directory and create connections.ini file.

mkdir ~/.ssm
cd ~/.ssm
touch connections.ini

For each server create a new section in the connections.ini file.

[example1]
description=Example1 Server
user=root
server=server1.example.org
port=22
options=
ssh_key=

You can use different ssh keys for each connection and you can define options for ssh command.

Use it!

ssm

Python: Snippet – Default dictionary

Normalerweise erzeugt ein Python Dictionary einen „KeyError“ wenn auf einen Key zugegriffen wird der nicht existiert. Mit „defaultdict“ werden die Keys einfach erzeugt wenn sie nicht existieren.

Ohne defaultdict:

>>> d = {}
>>> d['foobar']
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
KeyError: 'foobar'

Mit defaultdict:

>>> from collections import defaultdict
>>> d = defaultdict(dict)
>>> d['foobar']
{}
>>>

Das Verhalten hat natürlich Vorteile wenn man mehrdimensionale Dictionarys bauen will:

Ohne defaultdict:

>>> d = {}
>>> d['first'] = {}
>>> d['first']['second'] = "foobar"
>>> d
{'first': {'second': 'foobar'}}

Mit defaultdict:

>>> d = defaultdict(dict)
>>> d['first']['second'] = "foobar"
>>> d
defaultdict(<class 'dict'>, {'first': {'second': 'foobar'}})

Mehr dazu:

https://docs.python.org/3/library/collections.html

Ansible AWX/Tower Check_MK inventory plugin

Das Script kann als Inventory Plugin verwendet werden für AWX / Tower um Hosts von Check_MK in AWX / Tower zu importieren. Dazu muss ein View angelegt werden mit folgenden Spalten: Host, IPv4 address, Host Tags

Der Automation User muss Rechte auf den View und die entsprechende Contact Group haben.

#!/usr/bin/bash

cat << EOF > /tmp/check_mk_inventory.py
#!/usr/bin/env python3

########################################################################################################################
# Configuration for Check_MK
username = "automation user"
password = "xxxxxxxxxxxxxxxxxxxx"
base_url = "https://checkmk-dev/dev/check_mk/view.py"
view = "host_inventory"

import urllib.parse
import urllib.request
import os
import re
import sys
import argparse
import ssl

ssl._create_default_https_context = ssl._create_unverified_context

try:
	import json
except ImportError:
	import simplejson as json


class CheckMKInventory(object):

	def __init__(self):
		self.inventory = {}
		self.read_cli_args()

		# Called with `--list`.
		if self.args.list:
			self.inventory = self.checkmk_inventory()
		# Called with `--host [hostname]`.
		elif self.args.host:
			# Not implemented, since we return _meta info `--list`.
			self.inventory = self.empty_inventory()
		# If no groups or vars are present, return an empty inventory.
		else:
			self.inventory = self.empty_inventory()

		print(json.dumps(self.inventory))

	# Generator for key->value from view
	def generator(self, x):
		header = x[:1][0]
		data = x[1:]
		result = []

		for d in data:
			result.append(dict(zip(header, d)))

		return result

	def checkmk_inventory(self):
		# Values for post
		values = {
			"output_format": "python",
			"view_name": view,
			"_username": username,
			"_secret": password
		}

		# Encode and post to check_mk
		proxy_handler = urllib.request.ProxyHandler({})
		opener = urllib.request.build_opener(proxy_handler)
		data = urllib.parse.urlencode(values)
		data = data.encode('utf-8')
		req = urllib.request.Request(base_url, data)
		resp = opener.open(req)

		# Try to read the response
		try:
			hosts_view_raw = eval(resp.read())
			hosts_view_dict = self.generator(hosts_view_raw)

		except Exception as e:
			print(e)
			sys.exit(1)

		results = dict()
		results['_meta'] = {}
		results['_meta']['hostvars'] = {}
		results['checkmk'] = {}
		results['checkmk']['hosts'] = []

		for h in hosts_view_dict:

            try:
				path = re.findall(r".*\/wato\/(.*)\/.*", h['host_tags'])[0]
	
			except Exception as e:
				path = ""
		
			results['_meta']['hostvars'][h['host']] = {
				"ansible_host": h['host_ipv4_address'],
				"path": path,
				
			}
	
			results['checkmk']['hosts'].append(h['host'])

		return results


	# Empty inventory for testing.
	def empty_inventory(self):
		return {'_meta': {'hostvars': {}}}

	# Read the command line args passed to the script.
	def read_cli_args(self):
		parser = argparse.ArgumentParser()
		parser.add_argument('--list', action = 'store_true')
		parser.add_argument('--host', action = 'store')
		self.args = parser.parse_args()

# Get the inventory.
CheckMKInventory()


EOF

chmod 0500 /tmp/check_mk_inventory.py
unset PYTHONPATH
/tmp/check_mk_inventory.py "$@"
rm -f /tmp/check_mk_inventory.py

ARISTA bash event-handler in different VRF context

We are using an event-handler with on-startup-config write to push the configuration to our langley service which push the config in the central GIT repository.

To execute an bash command in an different vrf context use „ip netns exec ns-<VRF>“ before the command. See the example:

event-handler ARCHIVE_LANGLEY
   trigger on-startup-config
   action bash sudo ip netns exec ns-MANAGEMENT curl https://10.1.1.1/langley/put/$(hostname)/1 --upload-file /mnt/flash/startup-config

Python & ARISTA eAPI

Von ARISTA wird ein Python Paket bereitgestellt um über die eAPI mit dem Switch arbeiten zu können. Dies ist eine Qickanleitung.

pyeapi Paket installieren

pip install pyeapi

ARISTA eAPI aktivieren

Auf dem Arista Switch muss die API Schnittstelle aktiviert werden.

!
management api http-commands
   protocol http
   protocol https
    no shutdown
   !
   vrf MANAGEMENT
      no shutdown
!

Beispiel: Aktuelle running-config auslesen

#!/usr/bin/env python3

import pyeapi
import os, ssl

username = "apiuser"
password = "supersecret"
ip = "10.1.1.1"

# redhat ssl verification patch
if (not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None)):
    ssl._create_default_https_context = ssl._create_unverified_context


conn = pyeapi.client.connect(
    transport='https',
    host=ip,
    username=username,
    password=password,
    port=443,
    timeout=60)

node = pyeapi.client.Node(conn)

print(node.running_config)

Beispiel: Command execute & result

#!/usr/bin/env python3

import pyeapi
import os, ssl

username = "apiuser"
password = "supersecret"
ip = "10.1.1.1"

# redhat ssl verification patch
if (not os.environ.get('PYTHONHTTPSVERIFY', '') and getattr(ssl, '_create_unverified_context', None)):
    ssl._create_default_https_context = ssl._create_unverified_context


conn = pyeapi.client.connect(
    transport='https',
    host=ip,
    username=username,
    password=password,
    port=443,
    timeout=60)

node = pyeapi.client.Node(conn)

result = node.enable(commands=["show running-config"], encoding="text")

print result[0]['result']['output']

Gitolite mit ansible-playbook post-update hook zum Verteilen von Konfigurationen etc.

Gitolite ist ein einfaches Script um einen GIT Repo Server aufzusetzen. Zusammen mit Ansible nutze ich es um z.B. Zonenfiles auf Bind9 Server zu verteilen. Das werde ich in diesem Beispiel zeigen. Ansible und Gitolite laufen auf dem gleichen Server.

Installation Gitolite

apt install gitolite3

Während der Installation wird nach dem Public SSH Key des Admins gefragt. Dieser Key ist später berechtigt das gitolite-admin Repo welches die Konfiguration der Repos beinhaltet zu benutzen.

Klonen von gitolite-admin und neues Repo dns anlegen

git clone gitolite3@10.1.1.1:gitolite-admin

gitolite-admin/conf/gitolite.conf

repo gitolite-admin
    RW+    =     admin

repo testing
    RW+    =     @all

repo dns
    option hook.post-update = dns-deploy
    RW+    =     admin

Hier wurde bereits ein post-update Hook definiert. Es müssen aber noch weitere Einstellungen angepasst werden damit dieser funktioniert.

Neue User legt man an in dem Verzeichnis gitolite-admin/keydir <username>.pub mit dem entsprechenden SSH Key anlegt. Die Usernamen können dann in der gitolite.conf verwendet werden. Ausführliche Doku zu gitolite -> http://gitolite.com/gitolite/index.html

Installation ansible 

apt install software-properties-common
apt-add-repository ppa:ansible/ansible
apt update
apt install ansible

Konfiguration ansible

Ich verwende auf den Zielsystemen eigene User um Dinge per Ansible zu deployen. Im Beispiel heist der „vader“.

/etc/ansible/hosts

[dns-servers]
ns1.example.com
ns2.example.com

/etc/ansible/group_vars/dns-servers

---
ansible_ssh_private_key_file: /etc/ansible/sshkey/id_rsa
ansible_ssh_user: vader
ansible_sudo_pass: lukeiamyourfather

SSH Key für vader erzeugen

mkdir -p /etc/ansible/sshkey
ssh-keygen -t rsa -f /etC/ansible/sshkey/id_rsa

Den SSH Key ohne Passwort ablegen.

Anpassen der Verzeichnis Rechte:

chmod 600 /etc/ansible/sshkey
chmod 600 /etc/ansible/sshkey/id_rsa
chmod 644 /etc/ansible/sshkey/id_rsa.pub
chown root:root -R /etc/ansible/sshkey

Auf den Zielsystemen muss jeweils der User vader mit dem Passwort eingerichtet werden und der Public Key des SSH Keys muss als authorized_key hinterlegt werden. Der User muss in der sudo Gruppe sein.

gitolite hook aktivieren

/var/lib/gitolite3/.gitolite.rc

Bei folgenden Zeilen Kommentierung entfernen.

LOCAL_CODE                =>  "$ENV{HOME}/local",

# allow repo-specific hooks to be added
            'repo-specific-hooks',

Verzeichnis anlegen für Hooks

mkdir -p /var/lib/gitolite3/local/hooks/repo-specific

dns-deploy hook anlegen

/var/lib/gitolite3/local/hooks/repo-specific/dns-deploy

sudo /opt/ansible/dns.sh

Owner und Rechte ändern

chown gitolite3:gitolite3 dns-deploy
chmod +x dns-deploy

Das Hook Script liegt in /opt/ansible:

mkdir -p /opt/ansible

/opt/ansible/dns.sh

#!/bin/bash


REPO=/opt/ansible/dns

if [ ! -d $REPO ]
then
   ssh-agent bash -c 'ssh-add /etc/ansible/sshkey/id_rsa; git clone gitolite3@10.1.1.1:dns'
else
    cd $REPO
    ssh-agent bash -c 'ssh-add /etc/ansible/sshkey/id_rsa; git pull'
fi


cd $REPO
ansible-playbook dns.yaml

# End

Das Hook Script klont das Repo dns nach /opt/ansible/dns und führt das ansible playbook aus dem Repo aus.

Anpassen der Rechte

chown root:root /opt/ansible/dns.sh
chmod 700 /opt/ansible/dns.sh

Den gitolite3 User berechtigen das Script ohne Passwort per Sudo auszuführen

/etc/sudoers

gitolite3 ALL = (root) NOPASSWD: /opt/ansible/dns.sh

Das Repo dns

git clone gitolite3@10.1.1.1:dns
cd dns

Anlegen des Playbooks

dns.yaml

---

- hosts: dns-servers

  tasks:

  - name: create or update named.conf.local
    become: true
    register: named
    template:
      src: /opt/ansible/dns/conf/named.conf.local.j2
      dest: /etc/bind/named.conf.local
    vars:
      zones:
      - lanbugs.de

  - name: create or update zone files
    become: true
    register: zones
    template: src={{ item }} dest=/etc/bind/{{ item | basename | regex_replace('\.j2','') }} owner=root group=root mode=0644
    with_fileglob:
      - /opt/ansible/dns/conf/db.*.j2

  - name: restart bind
    become: true
    service:
      name: bind9
      state: restarted
    when: zones.changed or named.changed

DNS Files

mkdir conf

named.conf.local.j2

{% for zone in zones %}
zone "{{ zone }}" {
    type master;
    file "/etc/bind/db.{{ zone}}";
};
{% endfor %}

db.lanbugs.de.j2

; BIND db file for lanbugs.de

$TTL 86400

{% include "zone_header.j2" %}

@               1800    IN      TXT     "v=spf1 a mx ptr ~all"

test            1800    IN      A       1.2.3.4


$ORIGIN lanbugs.de.

zone_header.j2

@       IN      SOA     ns1.example.com.      admin.example.com. (
                        2018111101      ; serial number YYMMDDNN
                        28800           ; Refresh
                        7200            ; Retry
                        864000          ; Expire
                        86400           ; Min TTL
                        )

@       1800    IN      NS      ns1.example.com.
@       1800    IN      NS      ns2.example.com.

@       1800    IN      MX      10 mail1.example.com.
@       1800    IN      MX      20 mail2.example.com.

Files zu dns Repo hinzufügen und pushen

git add -A
git commit -m "initial commit"

git push
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 311 bytes | 311.00 KiB/s, done.
Total 3 (delta 1), reused 0 (delta 0)
remote: Identity added: /etc/ansible/sshkey/id_rsa (Ansible vader)
remote: From 10.1.1.1:dns
remote:    63bf7b0..63d2dc5  master     -> origin/master
remote: Updating 63bf7b0..63d2dc5
remote: Fast-forward
remote:  conf/db.lanbugs.de.j2 | 1 -
remote:  1 file changed, 1 deletion(-)
remote: 
remote: PLAY [dns-servers] *************************************************************
remote: 
remote: TASK [Gathering Facts] *********************************************************
remote: ok: [ns1.example.com]
remote: ok: [ns2.example.com]
remote: 
remote: TASK [create or update named.conf.local] ***************************************
remote: ok: [ns1.example.com]
remote: ok: [ns2.example.com]
remote: 
remote: TASK [create or update zone files] *********************************************
remote: changed: [ns1.example.com] => (item=/opt/ansible/dns/conf/db.lanbugs.de.j2)
remote: changed: [ns2.example.comc] => (item=/opt/ansible/dns/conf/db.lanbugs.de.j2)
remote: 
remote: TASK [restart bind] ************************************************************
remote: changed: [ns1.example.com]
remote: changed: [ns2.example.com]
remote: 
remote: PLAY RECAP *********************************************************************
remote: ns1.example.com        : ok=4    changed=2    unreachable=0    failed=0   
remote: ns2.example.com        : ok=4    changed=2    unreachable=0    failed=0   
remote: 
To 10.1.1.1:/dns
   63bf7b0..63d2dc5  master -> master

Have fun 🙂

 

 

 

 

 

DCBX and PFC on DELL EMC S4112 switches and S2D (storage spaces direct) for Hyper-V

This is a basic configuration for DCB and PFC to use with Windows 2016 and „storage spaces direct“ on Dell EMC S4xxx switches. CoS class is set to 4.

conf t

int vlan 30
 description S2D
 ip address 10.10.10.10 255.255.255.0
 mtu 9216
 no shut
exit

dcbx enable

policy-map type qos TRUSTMAP
 class class-trust
  trust dot1p
 exit
exit

class-map type network-qos PFCMAP
 match qos-group 4
exit

policy-map type network-qos QOSMAP
 class PFCMAP
  pause buffer-size 100 pause-threshold 50 resume-threshold 10
  pfc-cos 4
 exit
exit

system qos
 service-policy input type qos TRUSTMAP
exit

interface range ethernet 1/1/1-1/1/12
 no flowcontrol receive
 no flowcontrol transmit
 switchport mode trunk
 switchport trunk allowed vlan 30
 service-policy input type network-qos QOSMAP
 mtu 9216
 priority-flow-control mode on
exit

exit

Config on Windows:

# Clear previous configuration
Get-NetQosTrafficClass |  Remove-NetQosTrafficClass
Get-NetQosPolicy | Remove-NetQosPolicy

# Disable the DCBx setting
Set-NetQosDcbxSetting -Willing $false -Confirm $true

# create Qos policies and tag each type of traffic with the relevant priority
# SMB PFC priority = 4
New-NetQosPolicy 'SMB' -NetDirectPortMatchCondition 445 -PriorityValue8021Action 4

# Set VLAN ID for SMB-NICs
Get-NetAdapter | ? Name -like '*SMB*' | Set-NetAdapterAdvancedProperty -RegistryKeyword 'VlanID' -RegistryValue 30 -Verbose
Get-NetAdapter | ? Name -like '*SMB*' | Get-NetAdapterAdvancedProperty -RegistryKeyword 'VlanID'

# Enable Priotity Flow Control (PFC) on a specific priority (5). Disable for others....
Enable-NetQosFlowControl -Priority 4
Disable-NetQosFlowControl 0,1,2,3,5,6,7

# Enable QoS on SMB-NICs
Get-NetAdapter | ? Name -like '*SMB*'| Enable-NetAdapterQos