259 lines
18 KiB
TeX
259 lines
18 KiB
TeX
%
|
|
% Chapter: Technische Umsetzung
|
|
%
|
|
|
|
\chapter{Technische Umsetzung}
|
|
\section{Berechtigungsverwaltung}
|
|
\subsection{Ausarbeitung der Herangehensweise}
|
|
Zunächst wurde gebrainstormed, welche Herangehensweisen hier möglich sind.
|
|
Ein Artefakt des Brainstormings ist eine Mind-Map, die unter \fullref{app:ideensammlung} zu finden ist.
|
|
|
|
\subsubsection{Ansatz 1}
|
|
Der aus dieser Mindmap, nach individueller Meinung des Autors, vielversprechenste Ansatz ist es,
|
|
die \ac{1P}-Restful-API zu verwenden.
|
|
Bei diesem Ansatz würden Administrator*innen und Entwickler*innen API-Keys für \ac{1P} erhalten.
|
|
Entwickler*innen hätten mit ihren Keys bestimmte Leseberechtigungen $r$ und Administratoren
|
|
die Berechtigung $r$ zu verändern.
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=0.75\textwidth]{images/dev-stuff-via-api-keys.png}
|
|
\captionof{figure}{Relationsdiagramm: Ansatz 1 | 1Password-API}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:ansatz-1-mit-api-keys}
|
|
\end{nicepic}
|
|
|
|
Dieser Ansatz wurde zeitnah als unumsetzbar erkannt und verworfen, da \ac{1P} das nachträgliche Verändern
|
|
von API-Key-Berechtigungen nicht erlaubt.
|
|
|
|
\subsubsection{Ansatz 2}
|
|
Der nächste Lösungsansatz befasst sich mit einer Abstraktionsebene: Der \ac{MASA}.
|
|
Hier ist die grundlegende Idee, dass es eine serverseitige Anwendung gibt, die sich \ac{MASA} nennt.
|
|
Diese Anwendung übernimmt die Aufgabe anhand eines hinterlegten \ac{1P}-API-Keys Secrets
|
|
aus dem \ac{1P}-Vault des Partnerunternehmens abzufragen und an Entwickler*innen weiterzureichen.
|
|
Die \ac{MASA} provisioniert eigene API-Keys an Entwickler*innen und vermermerkt serverseitig,
|
|
welcher API-Key berechtigt ist, welche \ac{1P}-Einträge abzufragen.
|
|
Der API-Key könnte grundlegende Informationen wie zum Beispiel Entwickler*innennamen und Ablaufzeitpunkte des
|
|
Keys einbetten. Dieser Ansatz trägt viel Sicherheitsverantwortung, da eine mögliche Ausnutzung einer
|
|
Sicherheitslücke der \ac{MASA} direkt in den Firmen-Passwortmanager führen würden.
|
|
Um diesem Risikofaktor entgegenzuwirken würde der \ac{1P}-Key der \ac{MASA} verschlüsselt werden und die
|
|
\ac{MASA} würde nur einen Teil des Entschlüsselungs-Keys vorrätig halten. Der andere Teil wäre in jedem Entwickler*innen-Key
|
|
eingebettet. Dadurch wäre gewährleistet, dass ein*e Angreifer*in, selbst bei sehr weitreichendem Zugriff
|
|
in die \ac{MASA}, nicht auf das Innere des Passwortmanagers zugreifen könne, da die \ac{MASA} dazu selbstständig
|
|
gar nicht im Stande wäre. Da Entwickler*innen lediglich ein Schlüsselfragment des Verschlüsselungs-Schlüssels
|
|
in ihrem Key eingebettet hätten, der einen serverseitigen Schlüssel der \ac{MASA} zum auslesen benötigt,
|
|
bestünde auch keine Gefahr, dass ein*e Entwickler*in anhand seines bzw. ihres Keys ungeschützten Zugang zum Passwortmanager
|
|
erhalten würde. Dieser Ansatz erlaubt für weitreichende Flexibilität, da sämtliche Logik, die sich mit Berechtigungen
|
|
beschäftigt, anwendungsfallspezifisch geplant und umgesetzt wäre.
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=0.75\textwidth]{images/masa-diagram.png}
|
|
\captionof{figure}{Relationsdiagramm: Ansatz 2 | MASA}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:ansatz-2-mit-masa}
|
|
\end{nicepic}
|
|
|
|
|
|
Letztendlich entschied sich der Stakeholder gegen die Umsetzung der \ac{MASA}, da dieser Ansatz für zu
|
|
Aufwändig betrachtet wird und für den durch sie erbrachten Vorteil zu viel Aufwand und Angriffsfläche schaffen würde.
|
|
|
|
\subsubsection{Ansatz 3}
|
|
Der letzte Lösungsansatz befasst sich mit dem Erstellen dedizierter Vaults für jede*n Entwickler*in $e$.
|
|
Hierbei existiert eine Python-Toolbox, die anhand eine Yaml-Datei Referenzen auf diese Passwort-Einträge
|
|
in $\text{Vault}_e$ legt und von dort entfernt, wenn ein solcher Zugriff laut der Yaml-Datei nicht mehr vorgesehen ist.
|
|
Diese Einträge können über feste Eintrags-IDs und über Regex bezogen auf die Eingrags-Titel einem/r Entwickler*in vorgesehen werden.
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=0.75\textwidth]{images/dev-stuff-python.png}
|
|
\captionof{figure}{Relationsdiagramm: Ansatz 3 | Python-Toolbox}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:ansatz-3-mit-python}
|
|
\end{nicepic}
|
|
|
|
Letztendlich entschied sich der Stakeholder für Ansatz 3, da er ihm kostengünstig und aursreichend erschien.
|
|
|
|
\subsection{Kodierung}
|
|
Um den vom Stakeholder ausgewählten Ansatz 3 wie geplant umzusetzen, wurden zunächst
|
|
die Dokumentationen diverser \ac{1P}-Schnittstellen konsultiert. Schnell offenbarte sich eine Alternative
|
|
zu API-Keys: Die \ac{1P}-Desktop-Anwendung stellt eine CLI-API bereit.
|
|
Die CLI-API der Desktop-Anwendung zu verwenden, würde drei Probleme lösen:
|
|
\begin{description}
|
|
\item [Kosten und hedonische Qualität] \hfill \\
|
|
Einen API-Key zu erstellen und zu übermitteln ist kostenspielig und umständlich. Ein \ac{1P}-Konto haben dem gegenüber bereits alle Entwickler*innen des Partnerunternehmens.
|
|
\item [Authentifizierung und Sicherheit] \hfill \\
|
|
Anstatt einen API-Key unsicher zu speichern und in relevante Programme (=Ansible) zu laden, wird die Authentifizierung zu \ac{1P} ausgelagert.
|
|
\item [Manuelle Einsicht] \hfill \\
|
|
Da über die CLI-Methode der Zugriff auf die Entwickler*innen-Vaults direkt über die \ac{1P}-Desktop-App geschieht, kann diese den Vault-Inhalt auch dem Nutzer in ihrem GUI offenbaren.
|
|
\item [Integrationsaufwand] \hfill \\
|
|
Die Verwendung der \ac{1P}-Restful-API erscheint dem Autor nach ihrer Dokumentation sehr aufwändig und kompliziert. Eine CLI-API zu verwenden würde somit in der Umsetzung Projektressourcen sparen.
|
|
\end{description}
|
|
|
|
So begründet fällt die Wahl der Schnittstelle zu \ac{1P} auf ihre CLI-API.
|
|
Um schnelle Softwareentwicklung mit minimalem Overhead zu gewährleisten und um für eine spätere Einbindung in Ansible
|
|
bereits in Vorleistung zu treten, fällt die Wahl der Programmiersprache auf Python. Ansible-Module können mit Python geschrieben werden. \cite{bib:ansible-docs-python-module}
|
|
Es wurde eine rudimentäre Architektur entworfen, die beschreibt, welche Komponente des Werkzeuges aus welchen kleineren Komponenten besteht.
|
|
Am unteren Ende dieses Aggregatbaumes stehen atomare Operationen. Im Kontext dieses Werkzeuges sind atomare Operationen Operationen,
|
|
die vom \ac{1P}-CLI ausgeführt werden. Diese Operationen implementiert also \ac{1P} selbst.
|
|
Hierbei handelt es sich nur um Lese, Erstell- und Löschvorgänge. Das Erfassen, auf welchen Eintrag welche*r Entwickler*in Zugriff hat,
|
|
und auf welche nicht, übernimmt \textit{sync-dev-vault.py}. Die Funktionen der andere Skripte ergeben sich in Gänze aus ihren Dateinamen.
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=1\textwidth]{images/config-file.png}
|
|
\captionof{figure}{Struktur der Zugriffs-config.yml}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:config-file}
|
|
\end{nicepic}
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=0.75\textwidth]{images/program-structure.png}
|
|
\captionof{figure}{Diagramm: Programmstruktur Secret-Synchronizer}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:programmstruktur-secret-synchronizer}
|
|
\end{nicepic}
|
|
|
|
Die Funktionsweise des Programmes ist wie folgt:
|
|
\begin{enumerate}
|
|
\item{Wende die Schritte $2..n$ auf alle zu synchronisierenden Entwickler*innen $d \in D$ an.}
|
|
\item{Lösche alle Einträge in $e_d \in d$.}
|
|
\item{Kopiere alle vorgesehenen Einträge $e_m \in \text{Master-Vault}$ nach $d$ und ergänze das Feld \enquote{Originale Eintrags-ID} mit dem \ac{UUID} von $e_m$.}
|
|
\item{Erstelle ein ID-Mapping-Objekt von $e_d$ zu $e_m$ in $d$.}
|
|
\end{enumerate}
|
|
|
|
|
|
\subsubsection{Performanzprobleme und Optimierung}
|
|
Eine Schwierigkeit, die sich im Rahmen der Umsetzung offenbart hat, ist, dass das \ac{1P}-CLI sehr langsam ist.
|
|
Soll die Liste aller Einträge eines Vaults ausgelesen werden, dauert das nach Erfahrungen des Autors etwa eine Sekunde, da es sich hierbei um \emph{eine} Anfrage handelt.
|
|
Diese Listenansicht zeigt jedoch nur begränzte Informationen der Einträge.
|
|
Möchte man ein bestimmtes Feld eines Eintrages \(z.B. \enquote{Originale Eintrags-ID}\) in Erfahrug bringen, müssen alle Informationen eines Eintrages
|
|
abgefragt werden. Hier ist ein CLI-API-Aufruf pro Eintrag erforderlich. Sind einem*r Entwickler*in z.B. 30 Einträge zugeordnet, so dauert das Finden eines Eintrags
|
|
mit einer bestimmten originalen ID im Durchschnitt $n=30; \frac{n}{2} = 15$ Sekunden. Im langsamsten Fall wären es $n=30$ Sekunden.
|
|
Ein Kopierforgang stellt zwei Aufrufe dar (=\textit{lesen,erstellen}), also dauert das Kopieren von 30 Einträgen $n=30; 2n = 60$ Sekunden.
|
|
Das \ac{1P}-CLI kann zwar für Detail-Aufrufe mehrere Eintrags-IDs auf Standard-Input annehmen und bearbeiten, jedoch zeigen Versuche des Autors dies zu implementieren, dass das nicht die
|
|
Zeitkomplexität von $O(n)$ verändert. Das heisst, eine Anfrage für 10 Einträge zu stellen, dauert in etwa zehn mal so lange,
|
|
wie zehn Anfragen für jeweils einen Eintrag zu stellen.
|
|
|
|
Eine spätere Ergänzung, um die programmatische Auslesung der Einträge in $O(n)$ anstatt $O(n^2)$ zu gewährleisten, ist die Unterhaltung von Mapping-Objekten in Entwickler*innen-Vaults.
|
|
Je Entwickler*innen-Vault wird abschließend der Synchronisierung ein Mapping-Objekt erstellt.
|
|
Diese Mapping-Objekte halten die Informationen vorrätig, welche öffentliche \ac{1P}-\ac{UUID} zu welcher privaten \ac{1P}-\ac{UUID} gehört.
|
|
Ohne diese Mapping-Objekte müssten für jeden Eintrag in Entwickler*innen-Vaults der nach einer öffentlichen \ac{UUID} identifiziert wird (=Fremdschlüssel \enquote{Originale Eintrags-ID}), alle Einträge im Entwickler*innen-Vault
|
|
abgefragt werden, bis ein Eintrag mit \enquote{Originaler Eintrag-ID = <x>} gefunden wird.
|
|
Mit diesen Mapping-Objekten kann ein beliebiger Eintrag anhand einer öffentlichen ID
|
|
binnen zwei Anfragen erfasst werden: Anfragen des Mapping-Objektes und Anfragen des privaten Objektes.
|
|
Das entspricht einer Zeitkomplexität von $O(2n) = O(n)$.
|
|
|
|
Desweiteren kann \ac{1P} Eintragsdaten lokal zwischenspeichern.
|
|
Diese Option lässt sich mit dem Flag \textit{--cache} auf Leseoperationen verwenden
|
|
und beschleunigt das Auslesen der angefragten Informationen, soweit diese im Cache existieren.
|
|
Auf Unix-ähnlichen Betriebssystemen ist dieses Verhalten
|
|
standardmäßig aktiviert. \cite{bib:1password-cli-caching}
|
|
|
|
\subsubsection{Sicherheitsbedenken}
|
|
Die Konfigurationsdatei definiert Zielvaults, nach ihren \acp{UUID}. Anhand dieser IDs sieht ein*e Administrator*in keine Vaultnamen.
|
|
Wenn nun aus etwaigen Gründen dort die ID eines Nutzvaults des Partnerunternehmems aufgeführt wäre, würde das
|
|
Werkzeug alle sich dort befindlichen Zugänge löschen. Das wäre ein Super-GAU in Form von Datenverlust.
|
|
|
|
\subsubsection{Sicherheitsvorkehrungen}
|
|
Um das zu verhindern, wurde eine Liste mit wichtigen Vault-IDs fest einkodiert.
|
|
Alle Erstell- oder Löschmethoden müssen einen Vault-ID-Parameter erhalten, selbst wenn dieser technisch nicht notwendig ist.
|
|
Wenn diese Vault-ID nun in der Liste der fest kodierten Nutzvault-IDs vorkommt, meldet die Methode einen deskriptiven Fehler und beendet die Programmausführung.
|
|
Somit ist gewährleistet, dass selbst bei einer fatalen Fehlkonfiguration kein Datenverlust entseht.
|
|
|
|
\section{Integration in Ansible}
|
|
Es ist Anforderung, dass \ac{1P}-Einträge von Entwickler*innen innerhalb von Ansible-Playbooks
|
|
dereferenziert und verwendet werden können.
|
|
|
|
\ac{1P} unterstützt nativ das Ersetzen von \ac{1P}-Referenzen in Dateien durch Secrets.
|
|
Diese Technik nennt sich \enquote{\ac{1PSA}}.
|
|
Diese Technologie ist jedoch nicht für die hier vorliegende Aufgabenstellung verwertbar, da die dem zugrunde liegende
|
|
Berechtigungsverwaltung auf Vault-Basis steht. Entweder hat ein*e Entwickler*in Zugriff auf einen gesamten Vault, oder er*sie keinen Zugriff auf den gesamten Vault.
|
|
Eine feingranularere Steuerung ist hier nicht möglich, jedoch für die hier gegebenen Anforderungen nötig. \cite{bib:1password-secrets-automation}
|
|
\ac{1PSA} erfasst \acp{UUID} und ist daher mit dem Konzept von Entwickler*innen-Vaults inkompatibel, da
|
|
Entwickler*innen hierbei eigene Kopien der originalen Einträge führen, die jeweils eigene \acp{UUID} haben.
|
|
Externe Entwickler*innen haben somit keinen Zugriff auf die originalen, öffentlichen \acp{UUID} und die privaten \acp{UUID}, die im Entwickler*innen-Vault
|
|
vorhanden sind, gelten jeweils nur für ein*e Entwickler*in. Das erfordert eine maßgeschneiderte, programmatische Lösung:
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=0.75\textwidth]{images/docker-ansible-structure.png}
|
|
\captionof{figure}{Relationsdiagramm: Docker-Ansible-Struktur, um \ac{1P}-Einträge zu dereferenzieren}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:docker-ansible-structure}
|
|
\end{nicepic}
|
|
|
|
Die hierfür angedachte Architektur erfordert, dass öffentliche \acp{UUID} in Host-Konfigurationen in Docker-Ansible aufgeführt sind.
|
|
Also eine UUID, die für alle Entwickler*innen greifbar ist.
|
|
|
|
Ab hier wird die Nutzergruppe \enquote{Entwickler*innen} in zwei Untergruppen strukturiert:
|
|
\begin{description}
|
|
\item [Interne Entwickler*innen] \hfill \\
|
|
Interne, festangestellte Entwickler*innen haben Vollzugriff auf den \ac{1P} und somit auch Zugriff auf die
|
|
in den Host-Konfigurationen vermerkte, öffentliche \ac{UUID} eines Eintrages.
|
|
Da diese Entwickler*innen keinen Entwickler*innen-Vault haben, müssen sie direkt auf diese notierte, öffentliche \ac{UUID} zugreifen.
|
|
\item [Externe Entwickler*innen] \hfill \\
|
|
Externe Entwickler*innen verfügen über einen Entwickler*innen-Vault, nicht jedoch über direkten Zugriff auf die vermerkte, öffentliche \ac{UUID}.
|
|
Falls der*die jeweilige Entwickler*in Zugriff auf einen verlinkten Eintrag hat, dann nur auf eine Kopie des Eintrages in dessen*deren jeweiligen Entwickler*innen-Vaults.
|
|
Diese Kopie hat eine andere \ac{UUID} als die, die in der Host-Konfiguration steht. Sie ist ja auf technischer Ebene ein anderer Eintrag, nur mit identischem Inhalt.
|
|
Die in den Host-Konfigurationen vermerkten, öffentlichen \acp{UUID} müssen also zunächst in eine private, sich im Entwickler*innen-Vault befindliche, \ac{UUID} übersetzt werden.
|
|
Hierfür besitzen
|
|
\end{description}
|
|
|
|
Um diese Problemstellung anzugehen, wird ein Ansible Filtermodul entworfen. Ein Filtermodul dient als
|
|
Texttransformator und kann in Jinja-Templates, wie sie von Ansible verwendet werden, wie folgt verwendet werden:\\
|
|
\texttt{\{\{\ \enquote{hello world} | uppercase \}\}}.\\
|
|
\cite{bib:ansible-filter-plugins}
|
|
Dieses Beispiel führt das \enquote{uppercase}-Filtermodul an.
|
|
Ein Beispiel mit dem im Rahmen dieser Ausarbeitung bereitgestellten Filtermodul würde so aussehen:\\
|
|
\texttt{\{\{ smtp.password | resolve\_1p\_secret \}\}}.
|
|
|
|
\begin{nicepic}
|
|
\includegraphics[width=1\textwidth]{images/flowchart-resolve-1p-secret.png}
|
|
\captionof{figure}{Flussdiagramm: Businesslogik des 1P-Resolver-Filtermoduls}
|
|
\caption*{Quelle: Eigene Darstellung}
|
|
\label{fig:flowchart-filtermodule-resolve-1p-secret}
|
|
\end{nicepic}
|
|
|
|
\subsection{Akzeptierte Formate}
|
|
Das Filtermodul akzeptiert mehrere, verschiedene Eingabeformate und ist rückwärtskompatibil.
|
|
|
|
\subsubsection*{Kein erkanntes Format}
|
|
Wird kein bekanntes Format erkannt, wird der Wert unverändert zurückgegeben. Das ermöglicht Rückwärtskompatibilät,
|
|
sodass nach-wie-vor hardgecodede Secrets funktionieren, und nicht versehentlich als \ac{UUID} interpretiert werden.
|
|
Das gewährleistet eine flüssigere Migration der Host-Konfigurationen, da somit bestehende Dateien weiterhin valide sind.
|
|
|
|
\subsubsection*{1P:<...>}
|
|
Beginnt der Wert mit \enquote{1P:}, so wird alles Nachfolgende als \ac{UUID} interpretiert.
|
|
Es wird versucht, das Feld \enquote{password} aus diesem Eintrag zu dereferenzieren.
|
|
|
|
\subsubsection*{Objektformat}
|
|
Wird ein Yaml-Objekt übergeben, so werden die Keys \enquote{1P\_secret\_uuid} und \enquote{1P\_field\_id} erwartet.
|
|
Diese definieren die Werte der Eintrags-\ac{UUID} und der Feld-ID in der ein Secret steht. Das ermöglicht z.B. auch den Benutzernamen (\textit{1P\_field\_id: username}) eines
|
|
Eintrages abzufragen, anstelle nur des Passworts. Ist keine Feld-ID gegeben, so wird auf das Standardfeld \enquote{password} zurückgefallen.
|
|
|
|
|
|
\subsection{Übersetzung der UUIDs}
|
|
Um die private \ac{UUID} zu ermitteln, die zu der öffentlichen \ac{UUID} gehört, die in der Host-Konfiguration steht,
|
|
frägt das Filtermodul den Mapping-Eintrag aus dem jeweiligen Entwickler*innen-Vault an und schlägt die öffentliche \ac{UUID} darin nach.
|
|
Die daraus resultierende \ac{UUID} kann von einem*r externen Entwickler*in angefragt werden.
|
|
|
|
\subsection{Unterscheidung zwischen internen und externen Entwickler*innen}
|
|
Ist in der Docker-Ansible-Konfigurationsdatei eine Entwickler*innen-Vault-\ac{UUID} definiert, so wird von einem*r externe*n Entwickler*in ausgegangen.
|
|
Ist stattdessen definiert, dass die in den Host-Konfigurationen angegebenen, öffentlichen \acp{UUID} direkt angefragt werden sollen, wird
|
|
von einem*r interne*n Entwickler*in ausgegangen. Sind beide Konfigurationen zugleich gegeben, wird ein Inkompatibilitätsfehler erhoben.
|
|
Ist ein*e interne*r Entwickler*in angenommen, so wird der Mapping-Schritt übersprungen. Der verbleibende Prozess bleibt unberührt.
|
|
|
|
\subsection{Kommunikation mit 1Password}
|
|
Ist eine \ac{UUID} ermittelt, auf die der*ie Nutzer*in Zugriff hat, wird diese über das \ac{1P}-CLI angefragt.
|
|
Dieser Aufruf ist: \textit{op item get <UUID>} mit dem Zusatz \textit{--format json}, um die Ausgabe programmatisch auswerten zu können.
|
|
|
|
\subsection{Performanz und Benchmarks}
|
|
Um diese Konfiguration zu testen, werden in einem Testszenario fünf Werte aus \ac{1P} ausgelesen:
|
|
\begin{itemize}
|
|
\item Datenbank-Host
|
|
\item Datenbank-Port
|
|
\item Datenbank-Benutzername
|
|
\item Datenbank-Passwort
|
|
\item Datenbank-Name
|
|
\end{itemize}
|
|
Diese Einträge abzufragen dauert durch das imperformante \ac{1P}-CLI rund 17 Sekunden.
|
|
|
|
\subsection{Optimierung}
|
|
Es bieten sich einige Möglichkeiten an den in \fullref{fig:flowchart-resolve-1p-secret} zu beschleunigen.
|