Skip to content

Multi-Vendor-Architektur

TCM365 ist von Grund auf für Multi-Vendor-Erweiterbarkeit konzipiert. Das VendorAdapter-Pattern abstrahiert vendor-spezifische API-Interaktionen hinter einem einheitlichen Interface, sodass die Plattform Microsoft 365, Zscaler, Atlassian Cloud und zukuenftige Vendor unterstützen kann, ohne die Kern-Geschaeftslogik zu ändern.


Designziele

  1. Vendor-Agnostik -- Kern-Plattformfeatures (Snapshots, Drift, Baselines, Compliance) arbeiten auf vendor-agnostischen Datenstrukturen.
  2. Pluggable Adapter -- Neue Vendor werden durch Implementierung eines einzigen Interface und Registrierung bei der Adapter-Registry hinzugefügt.
  3. Konsistente UX -- Benutzer erleben dieselben Workflows unabhängig vom Vendor.
  4. Unabhaengige Lebenszyklen -- Vendor-Adapter können unabhängig aktualisiert werden, ohne andere Vendor oder Kernlogik zu beeinflussen.
  5. Typsichere Ressourcen-Identifikation -- Ressourcen werden über eine strukturierte vendor.service.resource-Namenskonvention identifiziert.

VendorAdapter Interface

Das VendorAdapter-Interface definiert 7 Methoden, die jede Vendor-Implementierung bereitstellen muss:

// backend-js/src/common/interfaces/vendor-adapter.interface.ts

export interface VendorAdapter {
  /** Gibt Vendor-Metadaten zurück (Name, Version, unterstützte Features). */
  getInfo(): VendorInfo;

  /** Gibt die vom Adapter unterstützten Fähigkeiten zurück. */
  capabilities(): VendorCapabilities;

  /** Entdeckt verfügbare Ressourcentypen und Konfigurationen. */
  discover(tenant: VendorTenant): Promise<DiscoveryResult>;

  /** Erfasst einen Point-in-Time Snapshot aller (oder ausgewaehlter) Konfigurationen. */
  capture(tenant: VendorTenant, options?: CaptureOptions): Promise<CaptureResult>;

  /** Erkennt Drift zwischen zwei Snapshots oder zwischen Snapshot und Live-Konfiguration. */
  detectDrift(tenant: VendorTenant, baseline: Snapshot, current?: Snapshot): Promise<DriftResult>;

  /** Wendet eine Konfiguration auf den Ziel-Tenant an (Rollback oder Blueprint). */
  apply(tenant: VendorTenant, configuration: any, options?: ApplyOptions): Promise<ApplyResult>;

  /** Ruft Audit Logs vom Vendor für Change Attribution ab. */
  getAuditLogs(tenant: VendorTenant, timeRange: TimeRange): Promise<AuditLogEntry[]>;
}

Interface-Typen

interface VendorInfo {
  vendorId: string;         // 'microsoft' | 'zscaler' | 'atlassian'
  displayName: string;      // 'Microsoft 365' | 'Zscaler' | 'Atlassian Cloud'
  version: string;          // Adapter-Version
  documentationUrl: string; // Link zur Vendor-Dokumentation
}

interface VendorCapabilities {
  canCapture: boolean;      // Snapshot Capture unterstützt
  canDetectDrift: boolean;  // Drift Detection unterstützt
  canApply: boolean;        // Rollback/Apply unterstützt
  canAudit: boolean;        // Audit Log-Abruf unterstützt
  canDiscover: boolean;     // Resource Discovery unterstützt
  supportedResourceTypes: string[]; // Liste der Ressourcentyp-Identifier
}

interface CaptureResult {
  success: boolean;
  data: Record<string, any>; // Ressourcentyp -> Konfigurationsdaten
  resourceCount: number;
  errors?: CaptureError[];
  duration: number;
}

VendorAdapterRegistry

Die VendorAdapterRegistry ist ein injizierbarer NestJS-Service, der die Adapter-Registrierung und -Suche verwaltet.

@Injectable()
export class VendorAdapterRegistry {
  private adapters = new Map<string, VendorAdapter>();

  /** Registriert einen Vendor-Adapter. Wird bei Modulinitialisierung aufgerufen. */
  register(vendorId: string, adapter: VendorAdapter): void {
    this.adapters.set(vendorId, adapter);
  }

  /** Sucht einen Adapter nach Vendor ID. Wirft Exception, wenn nicht registriert. */
  get(vendorId: string): VendorAdapter {
    const adapter = this.adapters.get(vendorId);
    if (!adapter) {
      throw new NotFoundException(`No adapter registered for vendor: ${vendorId}`);
    }
    return adapter;
  }

  /** Gibt alle registrierten Vendor IDs zurück. */
  getRegisteredVendors(): string[] {
    return Array.from(this.adapters.keys());
  }

  /** Prüft, ob ein Vendor-Adapter registriert ist. */
  has(vendorId: string): boolean {
    return this.adapters.has(vendorId);
  }
}

Registrierungs-Pattern

Jeder Vendor-Adapter registriert sich während der NestJS-Modulinitialisierung:

@Module({
  providers: [M365Adapter, VendorAdapterRegistry],
})
export class M365Module implements OnModuleInit {
  constructor(
    private readonly registry: VendorAdapterRegistry,
    private readonly m365Adapter: M365Adapter,
  ) {}

  onModuleInit() {
    this.registry.register('microsoft', this.m365Adapter);
  }
}

Aktuelle Vendor-Implementierungen

M365Adapter (Microsoft 365)

Vendor ID: microsoft

Der M365 Adapter delegiert je nach Workload an zwei Kern-Services:

Service Workloads Strategie
GraphService Entra ID, Intune, Defender Direkte Microsoft Graph API-Aufrufe
UtcmService Teams, Exchange UTCM Snapshot Job API

Fähigkeiten:

Fähigkeit Status Details
Capture Vollständig 52 Ressourcentypen über 8 Workloads
Drift Detection Vollständig Per-Ressourcentyp-Vergleich mit Aenderungskategorisierung
Rollback Vollständig 52 Ressourcentypen mit Pre-Flight-Validierung
Audit Logs Vollständig Azure AD Directory Audit Log-Integration
Discovery Vollständig Workload- und Berechtigungs-Discovery

Ressourcentypen (52):

microsoft.entra.conditionalAccess
microsoft.entra.namedLocations
microsoft.entra.authenticationMethods
microsoft.entra.authenticationStrengths
microsoft.entra.directorySettings
microsoft.entra.roleAssignments
microsoft.entra.securityDefaults
microsoft.entra.authorizationPolicy
microsoft.entra.crossTenantAccess
microsoft.entra.appRegistrations
microsoft.entra.servicePrincipals
microsoft.intune.deviceCompliance
microsoft.intune.deviceConfiguration
microsoft.intune.enrollmentConfig
microsoft.intune.endpointSecurity
microsoft.intune.appProtection
microsoft.defender.secureScore
microsoft.defender.secureScoreControls
microsoft.teams.*
microsoft.exchange.*
... (52 gesamt)

ZscalerAdapter

Vendor ID: zscaler

Der Zscaler Adapter verwendet zwei API Clients für ZIA und ZPA:

Client Plattform Endpoint-Gruppen
ZscalerZiaClient Zscaler Internet Access 12 Gruppen
ZscalerZpaClient Zscaler Private Access 9 Gruppen

ZIA Endpoint-Gruppen (12):

Gruppe Ressourcen
URL Filtering URL Filtering Rules, URL Categories
Firewall Firewall Rules, IP Destination Groups
DLP DLP Dictionaries, DLP Engines, DLP Rules
SSL Inspection SSL Inspection Rules, Certificates
Security Advanced Threat Protection, Malware Protection
Bandwidth Control Bandwidth Control Rules
Authentication Authentication Settings
Traffic Forwarding GRE Tunnels, VPN Credentials, Static IPs
Location Management Locations, Sub-Locations
User Management Users, Groups, Departments
Admin Management Admin Users, Admin Roles
Sandbox Cloud Sandbox Settings

ZPA Endpoint-Gruppen (9):

Gruppe Ressourcen
App Segments Application Segments, Segment Groups
Server Groups Server Groups, Application Servers
Connectors Connector Groups, Connectors
Access Policies Access Policies, Policy Rules
Forwarding Policies Forwarding Rules
Timeout Policies Timeout Policy Rules
Inspection Policies AppProtection Profiles, Inspection Rules
Certificates Browser Access Certificates, Enrollment Certificates
SCIM SCIM Attribute Headers, SCIM Groups

Authentifizierung und Rate Limiting:

Plattform Auth-Methode Read-Limit Write-Limit
ZIA Cookie-basiert (28-Min TTL) 80 Req / 10s 10 Req / 10s
ZPA OAuth 2.0 (5-Min Pre-Expiry Buffer) 15 Req / 10s 15 Req / 10s

AtlassianAdapter (v2.5.0)

Vendor ID: atlassian

Der Atlassian Adapter verwendet drei spezialisierte API Clients:

Client Plattform Beschreibung
AtlassianJiraClient Jira Cloud 16 Getter-Methoden für Jira v3 REST API
AtlassianConfluenceClient Confluence Cloud Confluence v2 REST API
AtlassianAdminClient Atlassian Admin Admin REST API mit Cursor-Body-Pagination

Fähigkeiten:

Fähigkeit Status Details
Capture Vollständig 28 Resource Types über Jira, Confluence und Org Security
Drift Detection Vollständig deep-diff-basierter Vergleich
Rollback Vollständig Direct Write Methods für unterstützte Resource Types
Audit Logs Vollständig Two-Tier Audit Log-Abruf (Org + Produkt)
Discovery Vollständig Site Discovery und Resource Type Enumeration

Ressourcentypen (28):

Bereich Anzahl Beispiele
Jira 16 Projekte, Workflows, Permission Schemes, Issue Security, Notification Schemes
Confluence 5 Spaces, Global Templates, Space Permissions, Look and Feel
Org Security 7 Authentication Policies, IP Allowlists, Audit Log Settings, External Users

Authentifizierung und Rate Limiting:

Aspekt Details
Auth-Methode OAuth 2LO (Two-Legged OAuth)
Rate Limiting Points-basiertes System (AtlassianRateLimiter)
Pagination Cursor-Body-Pagination für Admin API

Ressourcentyp-Namenskonvention

Ressourcentypen verwenden ein strukturiertes vendor.service.resource-Format:

{vendor}.{service}.{resourceType}
Segment Beschreibung Beispiele
vendor Vendor-Identifier microsoft, zscaler, atlassian
service Service/Produkt innerhalb des Vendors entra, intune, teams, zia, zpa, jira, confluence, orgSecurity
resource Spezifischer Ressourcentyp conditionalAccess, urlFilteringRules

Beispiele:

microsoft.entra.conditionalAccess
microsoft.intune.deviceCompliance
microsoft.teams.messagingSettings
zscaler.zia.urlFilteringRules
zscaler.zia.firewallRules
zscaler.zpa.appSegments
zscaler.zpa.accessPolicies
atlassian.jira.projects
atlassian.jira.workflows
atlassian.confluence.spaces
atlassian.orgSecurity.authenticationPolicies

Resource Registry

Ressourcendefinitionen sind in backend-js/src/config/resource-registry/ gespeichert:

Datei Inhalt
m365-entra.ts Microsoft Entra ID Ressourcendefinitionen
m365-intune.ts Microsoft Intune Ressourcendefinitionen
m365-defender.ts Microsoft Defender Ressourcendefinitionen
m365-teams.ts Microsoft Teams Ressourcendefinitionen
m365-exchange.ts Microsoft Exchange Ressourcendefinitionen
zscaler-zia.ts Zscaler ZIA Ressourcendefinitionen
zscaler-zpa.ts Zscaler ZPA Ressourcendefinitionen
vendor-alias-map.ts Cross-Vendor Ressourcen-Aliasing für einheitliche Abfragen
atlassian-jira.ts Atlassian Jira Ressourcendefinitionen (v2.5.0)
atlassian-confluence.ts Atlassian Confluence Ressourcendefinitionen (v2.5.0)
atlassian-org-security.ts Atlassian Org Security Ressourcendefinitionen (v2.5.0)

Architekturdiagramm

graph TB
    SS[SnapshotService] -->|"Adapter aufloesen"| REG[VendorAdapterRegistry]
    REG --> M365[M365Adapter]
    REG --> ZS[ZscalerAdapter]
    REG --> ATL[AtlassianAdapter]
    REG --> FUTURE["[Zukuenftige Adapter]"]

    M365 --> GS[GraphService]
    M365 --> US[UtcmService]
    ZS --> ZIA[ZscalerZiaClient]
    ZS --> ZPA[ZscalerZpaClient]
    ATL --> JIRA[AtlassianJiraClient]
    ATL --> CONF[AtlassianConfluenceClient]
    ATL --> ADM[AtlassianAdminClient]

    GS --> GAPI["Microsoft Graph API"]
    US --> UAPI["UTCM Snapshot Job API"]
    ZIA --> ZIAAPI["Zscaler ZIA REST API"]
    ZPA --> ZPAAPI["Zscaler ZPA REST API"]
    JIRA --> ATLAPI["Atlassian Cloud REST APIs"]
    CONF --> ATLAPI
    ADM --> ATLAPI

Vendor-agnostischer Datenfluss

Der Plattformkern verarbeitet Daten über eine vendor-agnostische Pipeline:

  1. Benutzer fordert Snapshot Capture für einen VendorTenant an
  2. SnapshotService löst den VendorAdapter über die VendorAdapterRegistry auf
  3. Adapter.capture() ruft vendor-spezifische APIs auf
  4. Adapter gibt ein standardisiertes CaptureResult zurück
  5. SnapshotService speichert das Ergebnis im Tenant-Schema
  6. Drift/Baseline/Compliance-Bewertungen arbeiten auf den standardisierten Daten

Dies stellt sicher, dass alle nachgelagerten Features (Diff, Drift, Baselines, Compliance, Reports) identisch funktionieren, unabhängig davon, welcher Vendor die Daten erzeugt hat.


Neuen Vendor hinzufügen

Um Unterstützung für einen neuen Vendor (z.B. AWS, Google Workspace) hinzuzufuegen, folgen Sie diesen Schritten:

Schritt 1: Child-Entity erstellen

// backend-js/src/entities/aws-vendor-tenant.entity.ts
@ChildEntity('aws')
export class AwsVendorTenant extends VendorTenant {
  @Column({ name: 'aws_account_id', nullable: true })
  awsAccountId: string;

  @Column({ name: 'aws_access_key_id', nullable: true })
  awsAccessKeyId: string;

  @Column({ name: 'aws_secret_access_key', nullable: true })
  awsSecretAccessKey: string;
}

Schritt 2: Migration erstellen

npm run migration:generate src/database/migrations/AddAwsVendorTenantColumns

Tenant-Schema-Updates

Die Migration muss auch alle bestehenden tenant_*-Schemas aktualisieren, wenn der neue Vendor Änderungen an Data-Plane-Tabellen einfuehrt.

Schritt 3: VendorAdapter implementieren

@Injectable()
export class AwsAdapter implements VendorAdapter {
  getInfo(): VendorInfo {
    return {
      vendorId: 'aws',
      displayName: 'Amazon Web Services',
      version: '1.0.0',
      documentationUrl: 'https://docs.aws.amazon.com',
    };
  }

  capabilities(): VendorCapabilities {
    return {
      canCapture: true, canDetectDrift: true, canApply: true,
      canAudit: true, canDiscover: true,
      supportedResourceTypes: ['aws.iam.policies', 'aws.iam.roles', /* ... */],
    };
  }

  async discover(tenant: VendorTenant): Promise<DiscoveryResult> { /* ... */ }
  async capture(tenant: VendorTenant, options?: CaptureOptions): Promise<CaptureResult> { /* ... */ }
  async detectDrift(tenant: VendorTenant, baseline: Snapshot, current?: Snapshot): Promise<DriftResult> { /* ... */ }
  async apply(tenant: VendorTenant, config: any, options?: ApplyOptions): Promise<ApplyResult> { /* ... */ }
  async getAuditLogs(tenant: VendorTenant, timeRange: TimeRange): Promise<AuditLogEntry[]> { /* ... */ }
}

Schritt 4: Modul erstellen und registrieren

@Module({
  imports: [VendorRegistryModule],
  providers: [AwsAdapter, AwsApiClient],
  exports: [AwsAdapter],
})
export class AwsModule implements OnModuleInit {
  constructor(
    private readonly registry: VendorAdapterRegistry,
    private readonly awsAdapter: AwsAdapter,
  ) {}

  onModuleInit() {
    this.registry.register('aws', this.awsAdapter);
  }
}

Schritt 5: Ressourcendefinitionen und AppModule

  1. Ressourcendefinitionen in backend-js/src/config/resource-registry/ erstellen
  2. Modul in app.module.ts importieren
  3. Frontend aktualisieren: Vendor-Option im Tenant-Formular, credential-spezifische Felder, Renderer