Build-Serveraufbau

Der F-Droid-Build-Server isoliert die Herstellung eines jeden Pakets in einer sauberen, abgeschirmten und sicheren Wegwerfarbeitsumgebung einer virtuellen Maschine. Das Herstellen tausender Apps, insbesondere mit automatisierten und/oder unbeaufsichtigten Prozessen, muss vom Sicherheitsaspekt her als eine gefahrbringende Beschäftigung angesehen werden. Das ist umso mehr der Fall, wenn die hergestellten Produkte zudem weit verbreitet werden und dies in einer halbautomatischen Weise geschieht (“Es stehen Aktualisierungen bereit”).

Nehmen wir an, ein einspielendes Quell-Repository ist beschädigt. Dann finden wir hier eine kleine Auswahl an Maßnahmen, die ein Angreifer in einer solchen Situation ergreifen könnte:

  1. Eigene Build-Schritte anwenden, die faktisch alle ausgeführt werden, als würde der Benutzer den Build erstellen.
  2. Auf den Schlüsselspeicher zugreifen.
  3. Die erstellten APK-Dateien oder Quelltexttarballs für andere Anwendungen im Repository ändern.
  4. Verändern von Metadaten (zu denen auch Build-Skripte gehören, die wiederum ebenfalls die Fähigkeit mitbringen, alles auszuführen) für andere Anwendungen im Repository.

Durch die vollständige Isolierung bleiben die Auswirkungen zumindest auf die jeweilige Anwendung beschränkt. Die Build-Umgebung wird nicht nur frisch für jeden Build geschaffen und danach verworfen, sie wird auch komplett von der Signatur-Umgebung abgeschirmt.

Abgesehen von Sicherheitsproblemen gibt es einige Anwendungen, die seltsame Anforderungen haben, wie z.B. alte Versionen des NDK. Es wäre nicht praktikabel (oder zumindest extrem chaotisch), damit zu beginnen, das SDK auf einem Mehrzwecksystem zu modifizieren und wiederherzustellen, aber innerhalb der Grenzen einer Virtual Machine zum einmaligen Gebrauch ist alles möglich.

Hinzu kommt der offensichtliche Vorteil, eine standardisierte und vollständig reproduzierbare Umgebung zu besitzen, in der gebaut wird. Darüber hinaus schafft sie die Voraussetzungen für spezialisierte, maßgeschneiderte Build-Umgebungen für besondere Anwendungsfälle.

Überblick über die Einrichtung

Ausgehend von einer komplett sauberen minimalen Debian/Stable-Installation richten Sie so einen funktionierenden Build-Server ein. Diese Anleitung geht davon aus, dass Sie bereits den fdroidserver aufgesetzt haben. Das Ausführen der fdroidserver-Werkzeuge direkt aus git heraus (z. B. ~/fdroidserver/fdroid build org.adaway), wird wahrscheinlich fürs Erste am einfachsten sein, da die Build-Server-Setup-Skripte noch nicht wirklich fertig sind, um sie sauber zusammenzupacken. Außerdem wird es wahrscheinlich nur auf Debian, Ubuntu und anderen Debian-Derivaten funktionieren, da F-Droid nur Debian in seiner Infrastruktur verwendet (wir begrüßen Portierungsbeiträge!).

Der Basisserver braucht mindestens Debian/Jessie oder es müssen einige schwierige Anpassungen vorgenommen werden. Wenn Sie Ubuntu oder eine abgeleitete Distribution nutzen, können Sie alle Pakete, die in Ihrer Version fehlen, wie vagrant-cachier, von diesem PPA beziehen: https://launchpad.net/~fdroid/+archive/ubuntu/buildserver/

Installieren Sie zunächst die notwendigen Pakete und legen Sie einen neuen Benutzer an, der den gesamten Prozess hier ausführen soll, z. B. fdroid. Dies sind nur die Pakete, die von allen Builds benötigt werden. Möglicherweise müssen Sie zusätzliche Pakete installieren, um Apps herzustellen, z. B. mercurial oder subversion. Sobald die Pakete installiert sind und der Benutzer fdroid erstellt wurde, sollte in diesem Prozess nichts mehr mit root oder sudo ausgeführt werden.

root:~# apt-get install vagrant virtualbox git python3-certifi \
        python3-libvirt python3-requestbuilder python3-yaml \
        python3-clint python3-vagrant python3-paramiko python3-pyasn1 \
        python3-pyasn1-modules python3-requests python3-git
root:~# adduser --disabled-password fdroid
root:~# su fdroid

Klonen Sie den Quellcode und konfigurieren Sie die Buildserver-Einstellungen, was als Benutzer fdroid ausgeführt wird:

fdroid:~$ cd ~
fdroid:~$ git clone https://gitlab.com/fdroid/fdroidserver.git
fdroid:~$ cp fdroidserver/examples/makebuildserver.config.py fdroidserver/

Sie müssen außerdem sicherstellen, dass die Einrichtung Ihrer ANDROID_HOME-Umgebungsvariable korrekt ist.

Der Einfachheit halber können Sie optional die ausführbare Datei „fdroid“ zu Ihrem Pfad hinzufügen:

fdroid:~$ echo "PATH=\$PATH:$HOME/fdroidserver" >> ~/.bashrc

Erstellen Sie das Basis-Buildserver-Image…. (das Herunterladen der Basebox und aller SDK-Plattformen kann lange dauern).

fdroid:~$ cd fdroidserver
fdroid:~/fdroidserver$ ./makebuildserver

Holen Sie sich alle App-Metadaten aus dem fdroiddata-Repo …

fdroid:~/fdroidserver$ cd ~
fdroid:~$ git clone https://gitlab.com/fdroid/fdroiddata.git
fdroid:~$ cp fdroidserver/examples/config.yml fdroiddata/
fdroid:~$ sed -i "s@^[# ]*build_server_always.*@build_server_always: true@" fdroiddata/config.yml

Einrichten eines Build-Servers

Zusätzlich zum zuvor beschriebenen Basis-Setup liefern wir eine Vagrant-kompatible Debian/Stretch-Basebox namens „fdroid/basebox-stretch64“ aus.

Das Bootstrapping der Debian-Vagrant-Boxen für unseren Buildserver erfolgt von Grund auf. Der Bezug und die Überprüfung unserer vorgefertigten Vagrant-Boxen ist voll automatisiert. (Sollten Sie Interesse an diesem Prozess haben oder das Bootstrapping selbst in die Hand nehmen wollen, sollten Sie sich das hier ansehen: F-Droid Base Box)

Erstellen der F-Droid-Buildserver-Box

Navigieren Sie zu Ihrem Klon des F-Droid Server git und beginnen Sie mit der Erstellung von makebuildserver.config.py, wofür ./examples/makebuildserver.config.py als Referenz verwendet wird - schauen Sie sich die Einstellungen und die Dokumentation dort an, um zu entscheiden, ob etwas an Ihre Umgebung angepasst werden muss. Es gibt einen Pfad zum Abrufen der Basisbox, falls sie nicht existiert, sowie eine passende Proxy-Definition, die beide evtl. für Ihre Umgebung angepasst werden müssen. Sie können dann in das Verzeichnis fdroidserver wechseln und makebuildserver ausführen.

# navigieren Sie zu Ihrem Klon des F-Droid-Servers
cd .../fdroidserver

# kopieren Sie die Beispiel-Konfigurationsdatei
cp examples/makebuildserver.config.py makebuildserver.config.py

# starten Sie den Build Ihres Basebox-Images
./makebuildserver

Dies wird lange dauern, viel Bandbreite und Speicherplatz beanspruchen - der größte Teil davon wird für die Installation der notwendigen Teile des Android-SDKs für die verschiedenen Plattformen aufgewendet. Zum Glück müssen Sie das nur gelegentlich tun. Sobald Sie ein funktionierendes Build-Server-Image haben, können Sie, wenn sich Rezepte ändern (z.B. wenn Pakete hinzugefügt werden müssen), dieses Skript einfach erneut ausführen, dann wird das vorhandene an Ort und Stelle aktualisiert.

Sobald es fertig ist, haben Sie eine neue Basis-Box namens „buildserver“, die für die eigentlichen Builds verwendet wird. Sie können dann Pakete wie gewohnt erstellen, aber bei Ausführung von fdroid build --server ... erfolgt die App-Erstellung nun isoliert innerhalb der virtuellen Maschine.

Beim ersten Build wird eine neue virtuelle Maschine erstellt, die die „Buildserver“-Box als Basis verwendet. Ein Snapshot dieses sauberen Maschinenzustandes wird für zukünftige Builds gespeichert, um die Leistung zu verbessern. Sie können das Verwerfen dieses Snapshots und den Neuaufbau von Grund auf erzwingen, indem Sie den Schalter fdroid build --resetserver ... verwenden.

Cache-Anpassungen für makebuildserver (optional)

Die wichtigsten SDK/NDK-Downloads werden automatisch zwischengespeichert, um den nächsten Durchgang zu beschleunigen. Aber es gibt keine einfache Möglichkeit, dies für längere Abschnitte zu tun, die das android-Tool des SDK verwendet, um Plattformen, Add-ons und Tools zu installieren. Anstatt jedoch ein automatisches Caching zu erlauben, können Sie ein vorgefülltes Cache-Verzeichnis angeben, das nicht nur diese Downloads, sondern auch .tar.gz-Dateien für alle relevanten Ergänzungen enthält. Wenn die Provisionierungsskripte diese erkennen, werden sie anstelle der Android-Tools verwendet. Haben Sie zum Beispiel buildserver/addons/cache/platforms/android-19.tar.gz, wird sie für die Installation der Android-19-Plattform verwendet, anstatt sie mit android update sdk --no-ui -t android-19 erneut herunterzuladen. Es ist möglich, aus einer lokalen Installation des SDK die Cache-Dateien dieser Ergänzungen zu erstellen, diese eingeschlossen:

cd /pfad/zum/android-sdk/platforms
tar czf android-19.tar.gz android-19
mv android-19.tar.gz /path/to/buildserver/addons/cache/platforms/

Wenn Sie bereits einen Buildserver erstellt haben, ist es auch möglich, diese Dateien direkt vom Buildserver zu beziehen:

vagrant ssh -- -C 'tar -C ~/android-sdk/platforms czf android-19.tar.gz android-19'
vagrant ssh -- -C 'cat ~/android-sdk/platforms/android-19.tar.gz' > /pfad/zum/fdroidserver/buildserver/cache/platforms/android19.tar.gz

Builds ausführen

Bei Verwendung des Buildservers ist es am einfachsten, fdroid direkt von einem Git-Checkout von fdroidserver aus zu starten. Wenn Sie die fdroidserver-Tools noch nicht installiert und eingerichtet haben, müssen Sie das als nächstes tun: Server und Repo-Werkzeuge installieren. Das liefert alle Abhängigkeiten, die benötigt werden, um fdroidserver von git auszuführen.

Jetzt sind Sie bereit, Builds auszuführen. Testen Sie die neueste Version von fdroid:

fdroid:~/fdroidserver$ cd ~/fdroiddata
fdroid:~/fdroiddata$ ~/fdroidserver/fdroid build org.fdroid.fdroid -l --server

Optionale Verwendung von QEMU/KVM/libvirt statt VirtualBox

Es ist auch möglich, QEMU/KVM-Gast-VMs über libvirt anstelle des standardmäßigen VirtualBox zu laden. VirtualBox ist immer noch das empfohlene Setup, da auch von f-droid.org verwendet wird, aber es gibt Fälle, in denen es nicht möglich ist, VirtualBox auszuführen, wie z.B. auf einer Maschine, auf der bereits QEMU/KVM-Gäste laufen. Um die libvirt-Bilddateien direkt mit vagrant package lesbar zu machen, muss QEMU von libvirt so konfiguriert werden, dass es immer auf libvirt.libvirt gesetzt wird.

root:~# apt-get install vagrant vagrant-mutate vagrant-libvirt ebtables dnsmasq-base \
        python3-libvirt libvirt-clients libvirt-daemon-system qemu-kvm qemu-utils git \
        python3-yaml python3-clint python3-vagrant python3-pyasn1 python3-pyasn1-modules \
        python3-requests python3-git
root:~# cat << EOF >> /etc/libvirt/qemu.conf
user = "libvirt"
group = "libvirt"
dynamic_ownership = 1
EOF
root:~# service libvirtd restart

Erstellen Sie dann neben makebuildserver eine makebuildserver.config.py und fügen Sie hinzu:

vm_provider = 'libvirt'
Debian/stretch und Ubuntu/xenial
root:~# adduser fdroid libvirt
root:~# adduser fdroid libvirt-qemu
älteres Debian und Ubuntu
root:~# adduser fdroid libvirtd
root:~# adduser fdroid kvm

Erweitertes verschachteltes KVM Setup:

Dieser Abschnitt ist nicht relevant für die Verwendung von F-Droid in einem normalen Setup. Wenn Sie das Flag fdroid build --server in einem KVM laufen lassen wollen, hilft Ihnen dieses Kapitel beim Start.

Beachten Sie die folgenden grundlegenden Schachtelungseinstellungen:

bare metal host (l0)
\- F-Droid VM (l1)
   \- F-Droid builder VM (l2)

Die obigen Schritte beschreiben, wie man (l1) und makebuildserver einrichtet (l2).

Zuerst müssen Sie prüfen, ob die CPU den Befehlssatz vmx (oder svm auf amd) unterstützt. Mit diesem Befehl können Sie Details über Ihre CPU auflisten:

root:~# cat /proc/cpuinfo

Bei (l0) müssen Sie prüfen, ob die Verschachtelung aktiviert ist:

root:~# cat /sys/module/kvm_intel/parameters/nested

Wenn es nicht aktiviert ist, können Sie es einschalten durch Ausführen von:

echo "options kvm-intel nested=Y" > /etc/modprobe.d/kvm-intel.conf

Sie müssen einen Neustart durchführen, damit dies wirksam wird.

Als nächstes müssen Sie sicherstellen, dass Ihre (l1) VM-Konfiguration die zum Verschachteln benötigten CPU-Funktionen weiterleitet. Öffnen Sie also Ihre Konfiguration für die VM /etc/libvirt/qemu/my-vm.xml und fügen Sie einen CPU-Block in Ihr Domain-Tag ein. (virt-manager bietet auch eine Benutzeroberfläche für diese Tätigkeit.)

<cpu mode='custom' match='exact'>
    <model fallback='allow'>SandyBridge</model>
    <vendor>Intel</vendor>
    <feature policy='require' name='vmx'/>
</cpu>

Die tatsächlich benötigte Konfiguration hängt von Ihrer CPU ab. Details finden Sie im libvirts Handbuch. Das wichtigste ist vmx (oder svm auf amd) an das Gastsystem weiterzuleiten.

Dies ist das Setup, das in den Continuous Integration-Builds als Teil der reproduzierbaren Builds verwendet wird. Sie können dies auf dem Jenkins-Setup des Debian-Projekts in Aktion sehen:

* https://jenkins.debian.net/job/reproducible_setup_fdroid_build_environment_profitbricks7/