2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00
2026-07-20 21:41:51 +02:00

Universal License Platform

Eigenständiger Lizenz-Server mit drei getrennten Portalen, signierten Offline-/Hybrid-/Online-Lizenzen und einer wiederverwendbaren Go-Clientbibliothek.

Enthalten

  • Admin / Management: initialisiert die Write-once-Schlüssel, verwaltet Accounts, sieht alle Lizenzen und den Audit-Trail.
  • Reseller / Autor: stellt Lizenzen aus und verwaltet ausschließlich die eigenen Datensätze, ohne Zugriff auf private Schlüssel.
  • Kunde / Client: sieht die zugeordneten Lizenzen, Laufzeiten, Status und fertige Client-Konfigurationen.
  • Validierungs-API: validiert signierte Lizenzen, prüft Sperrstatus und stellt kurzlebige, Ed25519-signierte Leases aus.
  • Hybrid offline: ein Client verwendet bei temporärer Nichterreichbarkeit eine zuvor verifizierte Lease bis zum signierten Grace-Ende.
  • Client-SDK: sdk/go/licenseclient für Feature Gates, Limits, Hintergrund-Refresh und sicheren Lease-Cache.

Sicherheitsmodell

  • Issuer- und Lease-Private-Keys werden mit AES-256-GCM verschlüsselt im Datenspeicher abgelegt.
  • Lizenz-Tokens werden ebenfalls verschlüsselt gespeichert; zur Online-Prüfung wird zusätzlich nur ein SHA-256-Hash gebunden.
  • Die Schlüsselerzeugung ist write-once. Sobald Schlüssel existieren, verschwindet der Button und das Backend lehnt jeden weiteren Generierungsversuch ab.
  • Passwörter werden mit PBKDF2-HMAC-SHA256 und individuellem Salt gespeichert.
  • Sessions sind HttpOnly, SameSite=Strict, zeitlich begrenzt und CSRF-geschützt.
  • Login und Validierungs-API besitzen einfache Rate Limits.
  • Sicherheitsheader, restriktive CSP und ein persistenter Audit-Trail sind aktiviert.
  • Reseller- und Kundenzugriffe werden serverseitig nach Eigentum bzw. Zuordnung gefiltert.

Der JSON-Datenspeicher ist atomar und mit Dateimodus 0600 geschrieben, aber für eine einzelne Serverinstanz gedacht. Vor Active/Active-Betrieb sollte Store durch PostgreSQL oder eine andere transaktionale Datenbank ersetzt werden.

Schnellstart

cd license-platform
cp .env.example .env
openssl rand -base64 32       # als LICENSE_MASTER_KEY eintragen
openssl rand -base64 32       # als LICENSE_ADMIN_API_TOKEN eintragen
# Ein langes Bootstrap-Passwort in LICENSE_BOOTSTRAP_ADMIN_PASSWORD eintragen.
docker compose up -d --build

Danach http://localhost:8091 öffnen und mit dem Bootstrap-Admin anmelden. Der Bootstrap-Account wird nur angelegt, wenn der Datenspeicher noch keinen Administrator enthält.

Im Admin-Portal werden die beiden Schlüsselpaare einmalig erzeugt. Private Schlüssel werden nie in der Oberfläche angezeigt.

Server-URL und automatische Erkennung

Für Hybrid- und Online-Lizenzen schreibt die Plattform LICENSE_PUBLIC_URL signiert in verification.serverUrl. Das Go-SDK löst die URL in dieser Reihenfolge auf:

  1. licenseclient.Config.ServerURL
  2. Environment LICENSE_SERVER_URL
  3. signierte verification.serverUrl aus der Lizenz
  4. /.well-known/license-server relativ zur Produkt-Base-URL

Damit kann die URL weiterhin per ENV überschrieben werden, muss bei üblichen Installationen aber nicht doppelt gepflegt werden.

Clientbibliothek

package main

import (
    "context"
    "embed"
    "os"
    "time"

    "github.com/b1tsblog/license-platform/pkg/licensekit"
    "github.com/b1tsblog/license-platform/sdk/go/licenseclient"
)

//go:embed trusted-keys.json
var trustedKeys []byte

func main() {
    trust, err := licensekit.ParseTrustStore(trustedKeys)
    if err != nil {
        panic(err)
    }

    client := licenseclient.New(context.Background(), licenseclient.Config{
        Product:        "my-product",        // im Produkt fest verdrahten
        ClientVersion:  "2.1.0",
        Token:          os.Getenv("LICENSE_TOKEN"),
        TrustStore:     trust,                // nur Public Keys einbetten
        BaseURL:        "https://app.example.org",
        InstanceID:     os.Getenv("LICENSE_INSTANCE_ID"),
        Mode:           licensekit.ModeHybrid,
        CacheFile:      "/data/license-lease.json",
        RefreshEvery:   15 * time.Minute,
        RequestTimeout: 5 * time.Second,
        // ServerURL ist optional: ENV oder signierter Token werden erkannt.
    })
    client.Start(context.Background())
    defer client.Close()

    if client.Has("advanced_export") {
        // Feature freischalten
    }
    if users, ok := client.Limit("users"); ok {
        _ = users
    }
}

Der Trust Store kann nach der Initialisierung unter GET /api/v1/trust-store geladen und in das Clientprodukt eingebettet werden. Ein Trust Store darf niemals kundenseitig frei konfigurierbar sein.

API

Öffentlich:

  • GET /.well-known/license-server
  • GET /api/v1/trust-store
  • POST /api/v1/licenses/validate
  • Kompatibilitätsalias: POST /v1/introspect

Management mit Authorization: Bearer $LICENSE_ADMIN_API_TOKEN:

  • GET /api/v1/licenses
  • POST /api/v1/licenses
  • POST /api/v1/licenses/import for already signed tokens
  • POST /api/v1/licenses/{id}/revoke
  • POST /api/v1/licenses/{id}/restore

Beispiel zur Validierung:

curl -sS http://localhost:8091/api/v1/licenses/validate \
  -H 'Content-Type: application/json' \
  -d '{
    "token":"LICENSE_TOKEN",
    "product":"my-product",
    "baseUrl":"https://app.example.org",
    "instanceId":"optional-instance"
  }'

Die vollständige Beschreibung liegt in openapi.yaml. Die bisherigen /v1/admin/licenses-Routen bleiben als Kompatibilitätsalias verfügbar, sodass das ältere licenseweb bestehende Tokens registrieren kann.

Eine schrittweise Übernahme vorhandener Schlüssel und Lizenzen ist in docs/MIGRATION.md beschrieben.

ENV-Variablen

Variable Zweck
LICENSE_PUBLIC_URL Öffentliche Basis-URL; wird in Hybrid-/Online-Lizenzen signiert
LICENSE_MASTER_KEY / _FILE Base64-kodierter 32-Byte-Schlüssel für AES-256-GCM
LICENSE_BOOTSTRAP_ADMIN_USER initialer Admin-Benutzername
LICENSE_BOOTSTRAP_ADMIN_PASSWORD / _FILE initiales Passwort, mindestens 12 Zeichen
LICENSE_ADMIN_API_TOKEN / _FILE optionaler Management-API-Bearer
LICENSE_DATA_FILE persistenter JSON-Datenspeicher
LICENSE_SESSION_TTL Session-Laufzeit, Standard 12h
LICENSE_DEFAULT_LEASE_TTL Standard-Lease, 1h
LICENSE_MAX_LEASE_TTL serverseitiges Maximum, 24h
LICENSE_SECURE_COOKIES bei TLS true; HTTPS-URL aktiviert es automatisch

Betrieb

go test ./...
go vet ./...
go test -race ./...
go build ./cmd/server

Für produktive Installationen gehören LICENSE_MASTER_KEY, Bootstrap-Passwort und API-Token in Docker/Kubernetes Secrets. TLS sollte am Reverse Proxy terminiert werden; LICENSE_PUBLIC_URL muss dabei die externe HTTPS-URL enthalten.

Description
No description provided
Readme MIT 82 KiB
Languages
Go 88%
HTML 10.7%
PowerShell 0.6%
Dockerfile 0.4%
Makefile 0.3%