Files
appServerPortalUI/doc/AI_Gen.tex
2026-06-06 17:14:11 +02:00

250 lines
6.2 KiB
TeX
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
\documentclass[a4paper,11pt]{article}
\usepackage[utf8]{inputenc}
\usepackage[T1]{fontenc}
\usepackage{geometry}
\usepackage{hyperref}
\usepackage{graphicx}
\usepackage{longtable}
\usepackage{textcomp}
\geometry{margin=2.5cm}
\title{Service-Portal Architektur}
\author{schooltech.ch}
\date{\today}
\begin{document}
\maketitle
\section{Zielsetzung}
Ziel ist der Aufbau eines zentralen Login- und Navigations-Portals für mehrere unabhängige Web-Services,
die in Docker-Containern betrieben werden.
Benutzer authentifizieren sich einmal zentral und können anschließend zwischen Services wechseln,
die jeweils unter eigenen Subdomains bereitgestellt werden.
\section{Architekturübersicht (final)}
\subsection{Domänenstruktur}
\begin{itemize}
\item \texttt{server.schooltech.ch} Login- und Navigations-Portal (SPA)
\item \texttt{<service>.server.schooltech.ch} einzelne Services (Docker Container)
\end{itemize}
\subsection{High-Level Architektur}
\begin{verbatim}
Internet
|
| HTTPS (*.server.schooltech.ch)
v
+---------------------+
| Nginx ReverseProxy |
| TLS, Auth, Routing |
+---------------------+
| |
| +--> Service Container (abc)
| abc.server.schooltech.ch
|
+--> Portal / Auth Service
server.schooltech.ch
\end{verbatim}
\subsection{Zentrale Prinzipien}
\begin{itemize}
\item Ein Einstiegspunkt (Nginx)
\item Zentrale Authentifikation (auth\_request)
\item Services kennen kein Login
\item Keine iFrames, kein Subpath-Rewrite
\item Services laufen auf Root-Pfad (\texttt{/})
\end{itemize}
\section{Authentifikation (Variante A: auth\_request)}
\subsection{Prinzip}
Nginx prüft jeden Request zu einem Service mittels \texttt{auth\_request} gegen den Auth-Service.
Der Auth-Service entscheidet ausschließlich anhand der Session (Cookie).
\subsection{Ablauf}
\begin{enumerate}
\item Benutzer loggt sich am Portal ein
\item Portal setzt Session-Cookie:
\begin{verbatim}
Domain=.server.schooltech.ch
Secure; HttpOnly; SameSite=None
\end{verbatim}
\item Benutzer ruft Service-Subdomain auf
\item Nginx fragt \texttt{/internal/auth} beim Auth-Service an
\item Bei Erfolg: Request wird an Service weitergeleitet
\end{enumerate}
\subsection{Header-Weitergabe}
Optional injiziert Nginx folgende Header:
\begin{itemize}
\item \texttt{X-Remote-User}
\item \texttt{X-User-Roles}
\end{itemize}
\section{Nginx Konfiguration}
\subsection{Wildcard Server Block}
\begin{verbatim}
server {
listen 443 ssl;
server_name *.server.schooltech.ch;
ssl_certificate /etc/letsencrypt/live/server.schooltech.ch/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/server.schooltech.ch/privkey.pem;
location / {
auth_request /internal/auth;
proxy_pass http://$upstream;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location = /internal/auth {
proxy_pass http://auth:3000/internal/auth;
proxy_pass_request_body off;
proxy_set_header Content-Length "";
proxy_set_header X-Original-URI $request_uri;
}
}
\end{verbatim}
\subsection{Upstream Mapping}
\begin{verbatim}
map $host $upstream {
default portal:3000;
abc.server.schooltech.ch abc:8080;
xyz.server.schooltech.ch xyz:8080;
}
\end{verbatim}
\section{Docker Compose (Übersicht)}
\begin{verbatim}
version: "3.9"
services:
nginx:
image: nginx:alpine
ports:
- "443:443"
volumes:
- ./nginx:/etc/nginx/conf.d
- ./certs:/etc/letsencrypt
depends_on:
- auth
- portal
networks:
- internal
auth:
image: node:18
command: node auth.js
networks:
- internal
portal:
image: node:18
command: node portal.js
networks:
- internal
abc:
image: service-abc
networks:
- internal
networks:
internal:
driver: bridge
\end{verbatim}
\section{UI-Konzept (Navigations-Portal)}
\subsection{Funktion}
\begin{itemize}
\item Login
\item Anzeige verfügbarer Services
\item Wechsel per Full Navigation
\item Kollabierbare Kopfzeile
\item Floating Launcher (Bookmark)
\end{itemize}
\subsection{UI-Layout}
\begin{verbatim}
+------------------------------------------------+
| LOGO | Service A | Service B | User |
+------------------------------------------------+
| |
| Service Overview / Last Service / Status |
| |
+------------------------------------------------+
\end{verbatim}
\subsection{HTML-Prototyp (Portal)}
\begin{verbatim}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="utf-8" />
<title>Service Portal</title>
<style>
header {
position: fixed;
top: 0; left: 0; right: 0;
height: 60px;
backdrop-filter: blur(6px);
background: rgba(0,0,0,0.3);
color: white;
display: flex;
align-items: center;
padding: 0 20px;
}
main {
padding-top: 80px;
}
.service {
display: inline-block;
margin: 10px;
padding: 20px;
border: 1px solid #ccc;
cursor: pointer;
}
</style>
</head>
<body>
<header>
<strong>Portal</strong>
</header>
<main>
<div class="service" onclick="go('abc')">Service ABC</div>
<div class="service" onclick="go('xyz')">Service XYZ</div>
</main>
<script>
function go(name) {
localStorage.setItem("lastService", name);
window.location.href = "https://" + name + ".server.schooltech.ch";
}
</script>
</body>
</html>
\end{verbatim}
\subsection{Portal als Docker Container}
Das Portal wird als eigener Container betrieben und statisch oder via Node.js ausgeliefert.
Es besitzt keine direkte Kopplung zu den Services außer über URLs.
\section{Zusammenfassung}
Diese Architektur ermöglicht:
\begin{itemize}
\item saubere Trennung von Portal und Services
\item zentrale Sicherheit
\item einfache Erweiterbarkeit
\item wartbare Reverse-Proxy-Konfiguration
\end{itemize}
\end{document}