One place for hosting & domains

      Verwenden der PDO-PHP-Erweiterung zur Durchführung von MySQL-Transaktionen in PHP unter Ubuntu 18.04


      Der Autor wählte die Kooperation Open Sourcing Mental Illness, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

      Einführung

      Eine MySQL-Transaktion ist eine Gruppe von logisch zusammenhängenden SQL-Befehlen, die in der Datenbank als eine einzige Einheit ausgeführt werden. Transaktionen werden verwendet, um die ACID (Atomicity, Consistency, Isolation und Durability)-Konformität in einer Anwendung durchzusetzen. Dabei handelt es sich um eine Reihe von Standards, die die Zuverlässigkeit der Verarbeitungsvorgänge in einer Datenbank regeln.

      Atomarität (Atomicity) gewährleistet den Erfolg zusammengehöriger Transaktionen oder einen vollständigen Ausfall, wenn ein Fehler auftritt. Konsistenz (Consistency) garantiert die Gültigkeit der an die Datenbank übermittelten Daten gemäß der definierten Geschäftslogik. Isolierung (Isolation) ist die korrekte Ausführung von gleichzeitigen Transaktionen, wobei sichergestellt wird, dass die Auswirkungen verschiedener Clients, die sich mit einer Datenbank verbinden, sich nicht gegenseitig beeinflussen. Dauerhaftigkeit (Durability) stellt sicher, dass logisch zusammenhängende Transaktionen dauerhaft in der Datenbank verbleiben.

      Über eine Transaktion ausgegebene SQL-Anweisungen sollten entweder erfolgreich sein oder ganz fehlschlagen. Wenn eine der Abfragen fehlschlägt, macht MySQL die Änderungen rückgängig und sie werden niemals in die Datenbank übertragen.

      Ein gutes Beispiel zum Verständnis der Funktionsweise von MySQL-Transaktionen ist eine E-Commerce-Website. Wenn ein Kunde eine Bestellung aufgibt, fügt die Anwendung je nach Geschäftslogik Datensätze in mehrere Tabellen ein, wie beispielsweise: orders und orders_products. Mehrtabellen-Datensätze, die sich auf eine einzelne Bestellung beziehen, müssen als eine einzige logische Einheit atomar an die Datenbank gesendet werden.

      Ein weiterer Anwendungsfall ist in einer Bankanwendung. Wenn ein Kunde Geld überweist, werden einige Transaktionen an die Datenbank gesendet. Das Konto des Senders wird belastet und dem Konto der Partei des Empfängers gutgeschrieben. Die beiden Transaktionen müssen gleichzeitig bestätigt werden. Wenn eine von ihnen fehlschlägt, kehrt die Datenbank in ihren ursprünglichen Zustand zurück, und es sollten keine Änderungen auf der Festplatte gespeichert werden.

      In diesem Tutorial werden Sie die PDO-PHP-Erweiterung verwenden, die eine Schnittstelle für die Arbeit mit Datenbanken in PHP bietet, um MySQL-Transaktionen auf einem Ubuntu 18.04-Server durchzuführen.

      Voraussetzungen

      Bevor Sie beginnen, benötigen Sie Folgendes:

      Schritt 1 — Erstellen einer Beispieldatenbank und von Tabellen

      Als Erstes erstellen Sie eine Beispieldatenbank und fügen einige Tabellen hinzu, bevor Sie mit MySQL-Transaktionen zu arbeiten beginnen. Melden Sie sich zunächst als root bei Ihrem MySQL-Server an:

      Wenn Sie dazu aufgefordert werden, geben Sie Ihr MySQL-Root-Passwort ein und drücken Sie ENTER, um fortzufahren. Erstellen Sie dann eine Datenbank. Für die Zwecke dieses Tutorials werden wir die Datenbank sample_store nennen:

      • CREATE DATABASE sample_store;

      Sie sehen die folgende Ausgabe:

      Output

      Query OK, 1 row affected (0.00 sec)

      Erstellen Sie einen Benutzer namens sample_user für Ihre Datenbank. Denken Sie daran, PASSWORD durch einen starken Wert zu ersetzen:

      • CREATE USER 'sample_user'@'localhost' IDENTIFIED BY 'PASSWORD';

      Erteilen Sie Ihrem Benutzer uneingeschränkte Berechtigungen für die Datenbank sample_store:

      • GRANT ALL PRIVILEGES ON sample_store.* TO 'sample_user'@'localhost';

      Laden Sie abschließend die MySQL-Berechtigungen neu:

      Sobald Sie Ihren Benutzer angelegt haben, sehen Sie die folgende Ausgabe:

      Output

      Query OK, 0 rows affected (0.01 sec) . . .

      Mit der vorhandenen Datenbank und dem Benutzer können Sie jetzt mehrere Tabellen erstellen, um zu demonstrieren, wie MySQL-Transaktionen funktionieren.

      Melden Sie sich vom MySQL-Server ab:

      Sobald das System Sie abmeldet, sehen Sie die folgende Ausgabe:

      Output

      Bye.

      Melden Sie sich dann mit den Anmeldeinformationen des Benutzers sample_user an, den Sie gerade angelegt haben:

      • sudo mysql -u sample_user -p

      Geben Sie das Passwort für den sample_user ein und drücken Sie ENTER, um fortzufahren.

      Wechseln Sie zu sample_store, um sie zur aktuell ausgewählten Datenbank zu machen:

      Sobald die Datenbank ausgewählt ist, sehen Sie die folgende Ausgabe:

      Output

      Database Changed.

      Erstellen Sie als Nächstes eine Tabelle products:

      • CREATE TABLE products (product_id BIGINT PRIMARY KEY AUTO_INCREMENT, product_name VARCHAR(50), price DOUBLE) ENGINE = InnoDB;

      Mit diesem Befehl wird eine Tabelle products mit einem Feld namens product_id erstellt. Verwenden Sie einen Datentyp BIGINT, der einen großen Wert von bis zu 2^63-1 aufnehmen kann. Sie verwenden dasselbe Feld als PRIMARY KEY zur eindeutigen Identifizierung von Produkten. Das Schlüsselwort AUTO_INCREMENT weist MySQL an, den nächsten numerischen Wert zu erzeugen, wenn neue Produkte eingefügt werden.

      Das Feld product_name ist vom Typ VARCHAR und kann bis zu 50 Buchstaben oder Zahlen enthalten. Für den Produktpreis (price) verwenden Sie einen Datentyp DOUBLE, um Fließkommaformate in Preisen mit Dezimalzahlen zu berücksichtigen.

      Zuletzt verwenden Sie die InnoDB als ENGINE, weil sie im Gegensatz zu anderen Speicher-Engines wie MyISAM MySQL-Transaktionen komfortabel unterstützt.

      Sobald Sie Ihre Tabelle products erstellt haben, erhalten Sie die folgende Ausgabe:

      Output

      Query OK, 0 rows affected (0.02 sec)

      Fügen Sie als Nächstes einige Artikel zur Tabelle products hinzu, indem Sie die folgenden Befehle ausführen:

      • INSERT INTO products(product_name, price) VALUES ('WINTER COAT','25.50');
      • INSERT INTO products(product_name, price) VALUES ('EMBROIDERED SHIRT','13.90');
      • INSERT INTO products(product_name, price) VALUES ('FASHION SHOES','45.30');
      • INSERT INTO products(product_name, price) VALUES ('PROXIMA TROUSER','39.95');

      Nach jeder Operation INSERT sehen Sie eine Ausgabe ähnlich der folgenden:

      Output

      Query OK, 1 row affected (0.02 sec) . . .

      Überprüfen Sie dann, ob die Daten der Tabelle products hinzugefügt wurden:

      Sie sehen eine Liste mit den vier von Ihnen eingefügten Produkten:

      Output

      +------------+-------------------+-------+ | product_id | product_name | price | +------------+-------------------+-------+ | 1 | WINTER COAT | 25.5 | | 2 | EMBROIDERED SHIRT | 13.9 | | 3 | FASHION SHOES | 45.3 | | 4 | PROXIMA TROUSER | 39.95 | +------------+-------------------+-------+ 4 rows in set (0.01 sec)

      Als Nächstes erstellen Sie eine Tabelle customers, die grundlegende Informationen über Kunden enthält:

      • CREATE TABLE customers (customer_id BIGINT PRIMARY KEY AUTO_INCREMENT, customer_name VARCHAR(50) ) ENGINE = InnoDB;

      Wie in der Tabelle products verwenden Sie für die customer_id den Datentyp BIGINT, wodurch sichergestellt wird, dass die Tabelle viele Kunden mit bis zu 2^63-1 Datensätzen unterstützt. Das Schlüsselwort AUTO_INCREMENT erhöht den Wert der Spalten, sobald Sie einen neuen Kunden einfügen.

      Da die Spalte customer_name alphanumerische Werte akzeptiert, verwenden Sie den Datentyp VARCHAR mit einer Beschränkung von 50 Zeichen. Auch hier verwenden Sie die InnoDB Storage ENGINE zur Unterstützung von Transaktionen.

      Nachdem Sie den vorherigen Befehl zum Anlegen der Tabelle customers ausgeführt haben, sehen Sie die folgende Ausgabe:

      Output

      Query OK, 0 rows affected (0.02 sec)

      Sie fügen der Tabelle drei Musterkunden hinzu. Führen Sie die folgenden Befehle aus:

      • INSERT INTO customers(customer_name) VALUES ('JOHN DOE');
      • INSERT INTO customers(customer_name) VALUES ('ROE MARY');
      • INSERT INTO customers(customer_name) VALUES ('DOE JANE');

      Sobald die Kunden hinzugefügt wurden, sehen Sie eine Ausgabe ähnlich der folgenden:

      Output

      Query OK, 1 row affected (0.02 sec) . . .

      Überprüfen Sie dann die Daten in der Tabelle customers:

      Sie sehen eine Liste mit den drei Kunden:

      Output

      +-------------+---------------+ | customer_id | customer_name | +-------------+---------------+ | 1 | JOHN DOE | | 2 | ROE MARY | | 3 | DOE JANE | +-------------+---------------+ 3 rows in set (0.00 sec)

      Anschließend erstellen Sie eine Tabelle orders, um die von verschiedenen Kunden aufgegebenen Bestellungen aufzuzeichnen. Führen Sie den folgenden Befehl aus, um die Tabelle orders zu erstellen:

      • CREATE TABLE orders (order_id BIGINT AUTO_INCREMENT PRIMARY KEY, order_date DATETIME, customer_id BIGINT, order_total DOUBLE) ENGINE = InnoDB;

      Verwenden Sie die Spalte order_id als PRIMARY KEY. Der Datentyp BIGINT ermöglicht Ihnen die Aufnahme von bis zu 2^63-1 Bestellungen und wird nach jeder Bestellungseinfügung automatisch erhöht. Das Feld order_date enthält das tatsächliche Datum und die tatsächliche Uhrzeit der Bestellung, daher verwenden Sie den Datentyp DATETIME. Die customer_id bezieht sich auf die zuvor von Ihnen erstellte Tabelle customers.

      Sie sehen die folgende Ausgabe:

      Output

      Query OK, 0 rows affected (0.02 sec)

      Da die Bestellung eines einzelnen Kunden mehrere Artikel enthalten kann, müssen Sie eine Tabelle orders_products erstellen, um diese Informationen zu speichern.

      Führen Sie den folgenden Befehl aus, um die Tabelle orders_products zu erstellen:

      • CREATE TABLE orders_products (ref_id BIGINT PRIMARY KEY AUTO_INCREMENT, order_id BIGINT, product_id BIGINT, price DOUBLE, quantity BIGINT) ENGINE = InnoDB;

      Verwenden Sie die ref_id als PRIMARY KEY, der nach jeder Einfügung eines Datensatzes automatisch erhöht wird. Die order_id und product_id beziehen sich auf die Tabelle orders und products. Die Spalte price ist vom Datentyp DOUBLE, um gleitende Werte aufzunehmen.

      Die Speicher-Engine InnoDB muss mit den zuvor erstellten Tabellen übereinstimmen, da sich die Bestellung eines einzelnen Kunden unter Verwendung von Transaktionen gleichzeitig auf mehrere Tabellen auswirkt.

      Ihre Ausgabe bestätigt die Erstellung der Tabelle:

      Output

      Query OK, 0 rows affected (0.02 sec)

      Sie werden vorerst keine Daten zu den Tabellen orders und orders_products hinzufügen. Dies geschieht jedoch später mit einem PHP-Skript, das MySQL-Transaktionen implementiert.

      Melden Sie sich vom MySQL-Server ab:

      Ihr Datenbankschema ist nun vollständig und Sie haben es mit einigen Datensätzen gefüllt. Nun werden Sie eine PHP-Klasse für die Handhabung von Datenbankverbindungen und MySQL-Transaktionen erstellen.

      Schritt 2 – Entwerfen einer PHP-Klasse zur Abwicklung von MySQL-Transaktionen

      In diesem Schritt erstellen Sie eine PHP-Klasse, die PDO (PHP Data Objects) zur Abwicklung von MySQL-Transaktionen verwendet. Die Klasse wird eine Verbindung zu Ihrer MySQL-Datenbank herstellen und Daten atomar in die Datenbank einfügen.

      Speichern Sie die Klassendatei im Stammverzeichnis Ihres Apache-Webservers. Erstellen Sie dazu mit Ihrem Texteditor eine Datei DBTransaction.php:

      • sudo nano /var/www/html/DBTransaction.php

      Fügen Sie dann den folgenden Code in die Datei ein: Ersetzen Sie PASSWORD mit dem in Schritt 1 erstellten Wert:

      /var/www/html/DBTransaction.php

      <?php
      
      class DBTransaction
      {
          protected $pdo;
          public $last_insert_id;
      
          public function __construct()
          {
              define('DB_NAME', 'sample_store');
              define('DB_USER', 'sample_user');
              define('DB_PASSWORD', 'PASSWORD');
              define('DB_HOST', 'localhost');
      
              $this->pdo = new PDO("mysql:host=" . DB_HOST . ";dbname=" . DB_NAME, DB_USER, DB_PASSWORD);
              $this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
              $this->pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
          }
      

      Zu Beginn der Klasse DBTransaction verwendet das PDO die Konstanten (DB_HOST, DB_NAME, DB_USER und DB_PASSWORD) zur Initialisierung und Verbindung mit der Datenbank, die Sie in Schritt 1 erstellt haben.

      Anmerkung: Da wir hier MySQL-Transaktionen in kleinem Maßstab demonstrieren, haben wir die Datenbankvariable in der Klasse DBTransaction deklariert. In einem großen Produktionsprojekt würden Sie normalerweise eine separate Konfigurationsdatei erstellen und die Datenbankkonstanten aus dieser Datei mit Hilfe einer PHP-Anweisung require_once laden.

      Als Nächstes legen Sie zwei Attribute für die Klasse PDO fest:

      • ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION: Dieses Attribut weist PDO an, eine Ausnahme auszulösen, wenn ein Fehler auftritt. Solche Fehler können zum Debugging protokolliert werden.
      • ATTR_EMULATE_PREPARES, false: Diese Option deaktiviert die Emulation vorbereiteter Anweisungen und erlaubt der MySQL-Datenbank-Engine, die Anweisungen selbst vorzubereiten.

      Fügen Sie nun den folgenden Code in Ihre Datei ein, um die Methoden für Ihre Klasse zu erstellen:

      /var/www/html/DBTransaction.php

      . . .
          public function startTransaction()
          {
              $this->pdo->beginTransaction();
          }
      
          public function insertTransaction($sql, $data)
          {
              $stmt = $this->pdo->prepare($sql);
              $stmt->execute($data);
              $this->last_insert_id = $this->pdo->lastInsertId();
          }
      
          public function submitTransaction()
          {
              try {
                  $this->pdo->commit();
              } catch(PDOException $e) {
                  $this->pdo->rollBack();
                  return false;
              }
      
                return true;
          }
      }
      

      Speichern und schließen Sie die Datei, indem Sie STRG + X, Y, dann ENTER drücken.

      Um mit MySQL-Transaktionen zu arbeiten, erstellen Sie in der Klasse DBTransaction drei Hauptmethoden: startTransaction, insertTransaction und submitTransaction.

      • startTransaction: Diese Methode weist PDO an, eine Transaktion zu starten, und schaltet Auto-Commit aus, bis ein Commit-Befehl ausgegeben wird.

      • insertTransaction: Diese Methode benötigt zwei Argumente. Die Variable $sql enthält die auszuführende SQL-Anweisung, während die Variable $data ein Array mit Daten ist, die an die SQL-Anweisung gebunden werden sollen, da Sie vorbereitete Anweisungen verwenden. Die Daten werden als Array an die Methode insertTransaction übergeben.

      • submitTransaction: Diese Methode bindet die Änderungen dauerhaft an die Datenbank, indem Sie einen Befehl commit() ausgibt. Wenn jedoch ein Fehler auftritt und die Transaktionen ein Problem haben, ruft die Methode die Methode rollBack() auf, um die Datenbank in ihren ursprünglichen Zustand zurückzuversetzen, falls eine PDO-Ausnahme ausgelöst wird.

      Ihre Klasse DBTransaction initialisiert eine Transaktion, bereitet die verschiedenen auszuführenden SQL-Befehle vor, und bindet schließlich die Änderungen atomar in die Datenbank ein, wenn es keine Probleme gibt. Andernfalls wird die Transaktion rückgängig gemacht. Darüber hinaus ermöglicht Ihnen die Klasse, den Datensatz order_id abzurufen, den Sie gerade durch Zugriff auf die öffentliche Eigenschaft last_insert_id erstellt haben.

      Die Klasse DBTransaction ist nun bereit, von jedem PHP-Code, den Sie als Nächstes erstellen, aufgerufen und verwendet zu werden.

      Schritt 3 – Erstellen eines PHP-Skripts zur Verwendung der Klasse DBTransaction

      Sie erstellen ein PHP-Skript, das die Klasse DBTransaction implementiert und eine Gruppe von SQL-Befehlen in die MySQL-Datenbank sendet. Sie imitieren den Workflow einer Kundenbestellung in einem Online-Einkaufswagen.

      Diese SQL-Abfragen wirken sich auf die Tabellen orders und orders_products aus. Ihre Klasse DBTransaction sollte nur Änderungen in der Datenbank zulassen, wenn alle Abfragen fehlerfrei ausgeführt werden. Andernfalls erhalten Sie einen Fehler zurück, und alle Änderungsversuche werden rückgängig gemacht.

      Sie erstellen eine einzelne Bestellung für den mit customer_id 1 identifizierten Kunden JOHN DOE. Die Bestellung des Kunden enthält drei verschiedene Artikel mit unterschiedlichen Mengen aus der Tabelle products. Ihr PHP-Skript nimmt die Bestelldaten des Kunden und übergibt sie an die Klasse DBTransaction.

      Erstellen Sie die Datei orders.php:

      • sudo nano /var/www/html/orders.php

      Fügen Sie dann den folgenden Code in die Datei ein:

      /var/www/html/orders.php

      <?php
      
      require("DBTransaction.php");
      
      $db_host = "database_host";
      $db_name = "database_name";
      $db_user = "database_user";
      $db_password = "PASSWORD";
      
      $customer_id = 2;
      
      $products[] = [
        'product_id' => 1,
        'price' => 25.50,
        'quantity' => 1
      ];
      
      $products[] = [
        'product_id' => 2,
        'price' => 13.90,
        'quantity' => 3
      ];
      
      $products[] = [
        'product_id' => 3,
        'price' => 45.30,
        'quantity' => 2
      ];
      
      $transaction = new DBTransaction($db_host, $db_user, $db_password, $db_name);
      

      Sie haben ein PHP-Skript erstellt, das eine Instanz der Klasse DBTransaction initialisiert, die Sie in Schritt 2 erstellt haben.

      In diesem Skript fügen Sie die Datei DBTransaction.php ein und initialisieren die Klasse DBTransaction. Als Nächstes bereiten Sie ein mehrdimensionales Array aller Produkte vor, die der Kunde im Geschäft bestellt. Sie rufen auch die Methode startTransaction() auf, um eine Transaktion zu starten.

      Anschließend fügen Sie den folgenden Code hinzu, um Ihr Skript orders.php zu vervollständigen:

      /var/www/html/orders.php

      . . .
      $order_query = "insert into orders (order_id, customer_id, order_date, order_total) values(:order_id, :customer_id, :order_date, :order_total)";
      $product_query = "insert into orders_products (order_id, product_id, price, quantity) values(:order_id, :product_id, :price, :quantity)";
      
      $transaction->insertQuery($order_query, [
        'customer_id' => $customer_id,
        'order_date' => "2020-01-11",
        'order_total' => 157.8
      ]);
      
      $order_id = $transaction->last_insert_id;
      
      foreach ($products as $product) {
        $transaction->insertQuery($product_query, [
          'order_id' => $order_id,
          'product_id' => $product['product_id'],
          'price' => $product['price'],
          'quantity' => $product['quantity']
        ]);
      }
      
      $result = $transaction->submit();
      
      if ($result) {
          echo "Records successfully submitted";
      } else {
          echo "There was an error.";
      }
      
      

      Speichern und schließen Sie die Datei, indem Sie STRG + X, Y, dann ENTER drücken.

      Bereiten Sie den Befehl vor, der über die Methode insertTransaction in die Tabelle orders eingefügt werden soll. Danach rufen Sie den Wert der öffentlichen Eigenschaft last_insert_id aus der Klasse DBTransaction ab und verwenden ihn als $order_id.

      Sobald Sie über eine $order_id verfügen, verwenden Sie die eindeutige ID, um die von dem Kunden bestellten Artikel in die Tabelle orders_products einzufügen.

      Anschließend rufen Sie die Methode submitTransaction auf, um die gesamten Bestelldetails des Kunden in die Datenbank zu übertragen, wenn es keine Probleme gibt. Andernfalls macht die Methode submitTransaction die versuchten Änderungen rückgängig.

      Jetzt führen Sie das Skript orders.php in Ihrem Browser aus. Führen Sie das Folgende aus und ersetzen Sie your-server-IP mit der öffentlichen IP-Adresse Ihres Servers:

      http://your-server-IP/orders.php

      Sie erhalten eine Bestätigung, dass die Datensätze erfolgreich übermittelt wurden.

      PHP-Ausgabe aus der MySQL-Klasse Transactions

      Ihr PHP-Skript funktioniert wie erwartet und die Bestellung zusammen mit den zugehörigen Bestellprodukten wurde atomar in die Datenbank übertragen.

      Sie haben die Datei orders.php in einem Browserfenster ausgeführt. Das Skript hat die Klasse DBTransaction aufgerufen, die ihrerseits die Details der orders in die Datenbank übermittelte. Im nächsten Schritt überprüfen Sie, ob die Datensätze in den Bezugstabellen der Datenbank gespeichert wurden.

      Schritt 4 — Bestätigen der Einträge in Ihrer Datenbank

      In diesem Schritt überprüfen Sie, ob die vom Browserfenster aus initiierte Transaktion für die Bestellung des Kunden wie erwartet in die Datenbanktabellen verbucht wurde.

      Dazu melden Sie sich erneut bei Ihrer MySQL-Datenbank an:

      • sudo mysql -u sample_user -p

      Geben Sie das Passwort für den sample_user ein und drücken Sie ENTER, um fortzufahren.

      Wechseln Sie zur Datenbank sample_store:

      Stellen Sie sicher, dass die Datenbank geändert wird, bevor Sie fortfahren, indem Sie die folgende Ausgabe bestätigen:

      Output

      Database Changed.

      Geben Sie dann den folgenden Befehl ein, um Datensätze aus der Tabelle orders abzurufen:

      Dadurch wird die folgende Ausgabe angezeigt, die die Bestellung des Kunden beschreibt:

      Output

      +----------+---------------------+-------------+-------------+ | order_id | order_date | customer_id | order_total | +----------+---------------------+-------------+-------------+ | 1 | 2020-01-11 00:00:00 | 2 | 157.8 | +----------+---------------------+-------------+-------------+ 1 row in set (0.00 sec)

      Rufen Sie dann die Datensätze aus der Tabelle orders_products ab:

      • SELECT * FROM orders_products;

      Sie sehen eine Ausgabe ähnlich der folgenden mit einer Liste von Produkten aus der Bestellung des Kunden:

      Output

      +--------+----------+------------+-------+----------+ | ref_id | order_id | product_id | price | quantity | +--------+----------+------------+-------+----------+ | 1 | 1 | 1 | 25.5 | 1 | | 2 | 1 | 2 | 13.9 | 3 | | 3 | 1 | 3 | 45.3 | 2 | +--------+----------+------------+-------+----------+ 3 rows in set (0.00 sec)

      Die Ausgabe bestätigt, dass die Transaktion in der Datenbank gespeichert wurde und Ihre Helferklasse DBTransaction wie erwartet funktioniert.

      Zusammenfassung

      In diesem Leitfaden haben Sie die PHP PDO verwendet, um mit MySQL-Transaktionen zu arbeiten. Obwohl dies kein erschöpfender Artikel über die Gestaltung einer E-Commerce-Software ist, hat er ein Beispiel für die Verwendung von MySQL-Transaktionen in Ihren Anwendungen bereitgestellt.

      Weitere Informationen über das Modell MySQL ACID finden Sie in dem Leitfaden InnoDB und das ACID-Modell auf der offiziellen MySQL-Website. Besuchen Sie unsere MySQL-Inhaltsseite für weitere verwandte Tutorials, Artikel und Fragen und Antworten.



      Source link

      Verwendung von nsh zur sicheren Ausführung von Remotebefehlen auf Ubuntu 18.04


      Einführung

      Es kann oft schwierig sein, täglich mehrere Rechner zu betreiben. Obwohl Secure Shell (SSH) eine gute Wahl für den Remote-Zugriff ist, hat die Ausführung selbst einige Nachteile, was die Bequemlichkeit und Sicherheit anbelangt.

      Beispielsweise benötigen Remote-Rechner eine öffentliche IP-Adresse und einen weitergeleiteten Port, damit auf sie zugegriffen werden kann, wodurch sie dem Internet oder zumindest einem größeren Netzwerk ausgesetzt sind. Dies ist insbesondere dann bedenklich, wenn Sie zur Authentifizierung ein Passwort anstelle eines öffentlichen und privaten Schlüsselpaares verwenden. Wenn Sie außerdem den öffentlichen Schlüssel des Remote-Rechners nicht im Voraus kennen, könnten Sie für einen “Man-in-the-Middle-Angriff” anfällig sein. Auch haben viele Remote-Rechner, auf die Sie zugreifen möchten, entweder keine öffentliche IP-Adresse oder eine dynamische IP-Adresse, die Sie möglicherweise nicht kennen.

      Außerdem benötigt SSH jeweils eine Verbindung pro Remote-Sitzung. Wenn ein Benutzer einen einzelnen Befehl über Hunderte oder sogar Tausende von Rechnern ausführen muss, muss er sich zunächst mit jedem Rechner mittels TCP-Handshake verbinden, was weniger effizient ist.

      NKN Shell oder nsh ist eine Alternative zu SSH, die eine bequeme und sichere Möglichkeit bietet, Remote-Befehle auszuführen. Nsh nutzt das globale öffentliche Netzwerk von NKN, das eine sichere und dezentrale Datenübertragung bereitstellt. Die Architektur verwendet individuelle Adressen, die einen öffentlichen Schlüssel sowohl zum Routing als auch zur End-to-End-Verschlüsselung ohne Public-Key-Infrastruktur (PKI) enthalten. Für das Netzwerk ist es auch nicht erforderlich, dass der Remote-Server eine öffentliche IP-Adresse hat. Der Remote-Server benötigt nur Zugang zum Internet und muss in der Lage sein, ausgehende HTTP- und Websocket-Verbindungen herzustellen. Dadurch sind Ihre Remote-Rechner nicht dem offenen Internet ausgesetzt.

      In diesem Tutorial nutzen Sie die Anwendungen NKN shell daemon und NKN Shell Client Xterm, um Befehle auf einem Remote-Rechner auszuführen. Dazu installieren und konfigurieren Sie den NKN Shell Daemon auf einem Remote-Rechner mit Internet-Zugang, generieren ein Schlüsselpaar und erstellen Ihre Verbindung von einem Client.

      Voraussetzungen

      Um diesem Tutorial zu folgen, benötigen Sie Folgendes:

      Schritt 1 — Installieren von NKN Shell auf einem Remote-Server

      Installieren Sie zunächst den NKN shell daemon (nsd) auf Ihrem Server. Diese Anwendung ruft nkn-multiclient auf, der sich mit dem öffentlichen Netzwerk von NKN verbindet und eine Adresse zum Routing erhält. Der Daemon empfängt dann eingehende Shell-Befehle von authentifizierten und erlaubten Clients, führt diese Befehle aus und sendet danach Ergebnisse zurück.

      Beginnen Sie mit dem Herunterladen des neuesten pre-built nshd binary from GitHub​​:

      • wget https://github.com/nknorg/nkn-shell-daemon/releases/latest/download/linux-amd64.tar.gz

      Dekomprimieren Sie die Datei:

      • tar -zxvf linux-amd64.tar.gz

      Verschieben Sie dann die Dateien in das Verzeichnis /usr/local/bin, damit sie systemweit verfügbar sind:

      • sudo mv ./linux-amd64/* /usr/local/bin/

      Als Nächstes konfigurieren Sie dies als Daemon-Prozess mit Systemd, damit es neu gestartet wird, wenn der Server zurückgesetzt wird.

      Erstellen Sie eine Datei namens nshd.service in /etc/systemd/system:

      • sudo nano /etc/systemd/system/nshd.service

      Fügen Sie der Datei folgende Dienst-Definition hinzu, um den Dienst zu konfigurieren:

      /etc/systemd/system/nshd.service

      [Unit]
      Description=NKN Shell Daemon
      After=network.target
      
      [Service]
      Type=simple
      User=root
      Group=root
      Restart=always
      ExecStart=/usr/local/bin/nshd
      
      [Install]
      WantedBy=multi-user.target
      

      Erfahren Sie mehr über Systemd Unit-Dateien in Verstehen von Systemd Units und Unit-Dateien.

      Speichern Sie die Datei und beenden Sie den Editor. Aktivieren und starten Sie dann den nshd-Dienst mit den folgenden Befehlen:

      • sudo systemctl enable nshd.service
      • sudo systemctl start nshd.service

      Führen Sie den folgenden Befehl aus, um sicherzustellen, dass der Dienst aktiv und gestartet ist:

      • sudo systemctl status nshd.service

      Sie sehen, dass der Status aktiv ist:

      Output

      ● nshd.service - NKN Shell Daemon Loaded: loaded (/etc/systemd/system/nshd.service; enabled; vendor preset: enabled) Active: active (running) since Wed 2020-02-19 19:16:02 UTC; 7s ago Main PID: 3457 (nshd) Tasks: 10 (limit: 1152) CGroup: /system.slice/nshd.service └─3457 /usr/local/bin/nshd Feb 19 19:16:02 your_hostname systemd[1]: Started NKN Shell Daemon. Feb 19 19:16:03 your_hostname nshd[3457]: Create directory /etc/nshd/ Feb 19 19:16:03 your_hostname nshd[3457]: Create password and save to file /etc/nshd/wallet.pswd Feb 19 19:16:03 your_hostname nshd[3457]: Create wallet and save to file /etc/nshd/wallet.json Feb 19 19:16:03 your_hostname nshd[3457]: Create authorized pubkeys file /etc/nshd/authorized_pubkeys Feb 19 19:16:03 your_hostname nshd[3457]: Listening at d46567b883a3070ee3fe879d9fa2d5dc55a95f79ff2797c42df36c6979e5c4Aba

      Um sich mit Ihrem Server zu verbinden, müssen Sie seine NKN-Adresse bereitstellen, die Sie im Output des vorherigen Befehls finden. Sie können auch den folgenden Befehl ausführen, um die Adresse zu erhalten:

      Ihre Adresse wird Ihnen angezeigt:

      Output

      e70ca28ede84fc0659f2869255e8a393aef35b4fa5a7e036f29127c7dba75383

      Notieren Sie sich diese Adresse, da Sie sie brauchen, um sich mit Ihrem Server zu verbinden.

      Nachdem nun der Daemon läuft und empfängt, können Sie den webbasierten Client konfigurieren, um mit dem Server zu kommunizieren.

      Schritt 2 — Konfigurieren von Berechtigungen für NKN Shell Client

      Sie benötigen einen kompatiblen Client, der sich mit dem Remote-Rechner verbinden kann. In diesem Tutorial verwenden Sie NKN Shell Client Xterm, einen webbasierten NKN Shell-Client. Es gibt einige verschiedene Möglichkeiten, um diesen auszuführen:

      • Verwenden Sie die gehostete Version unter https://nsh.nkn.org/. Beachten Sie, dass diese Webseite zwar auf einem Server gehostet wird, aber eigentlich eine rein lokale Webanwendung ist, die in Ihrem Browser läuft.
      • Nehmen Sie den Quellcode und hosten Sie ihn selbst.
      • Verwenden Sie die nShell Chrome Extension.

      In diesem Tutorial verwenden Sie die gehostete Version. Öffnen Sie den Webbrowser auf Ihrem lokalen Rechner und navigieren Sie zu https://nsh.nkn.org. Sie sehen einen Startbildschirm:

      The Shell Client

      Klicken Sie Generate New Key Pair. Ihre Schlüssel werden generiert und wie im folgenden Bild angezeigt:

      Generiertes Schlüsselpaar

      Anmerkung: Wenn Sie ein neues Schlüsselpaar generieren, sehen Sie einen Secret Seed. Verwahren Sie diesen Secret Seed sicher auf – genauso, wie Sie es mit Ihrem SSH-Schlüssel tun würden. Jeder, der über den Secret Seed verfügt, kann ihn zum Regenerieren Ihres öffentlichen Schlüssels und zum Ausführen von Befehlen auf Ihren Remote-Rechnern verwenden. Ihr Browser speichert diesen Seed, aber Sie sollten ihn an einem sicheren Ort aufbewahren, um ihn bei Bedarf erneut auf einem neuen Rechner verwenden zu können.

      Speichern Sie den Secret Seed an einem sicheren Ort. Sie können ihn später verwenden, um Ihren öffentlichen Schlüssel zu regenerieren, damit Sie sich von einem anderen Client-Rechner aus verbinden können.

      Da es sich um ein neues Schlüsselpaar handelt, müssen Sie den öffentlichen Schlüssel der Datei /etc/nshd/authorized_pubkeys auf Ihrem Server hinzufügen.

      /etc/nshd/authorized_pubkeys hat eine ähnliche Rolle wie die Datei ~/authorized_keys, die steuert, welche öffentlichen SSH-Schlüssel sich anmelden können. Die Datei authorized_pubkeys kann bestimmen, welcher Benutzer mit einem Schlüssel verbunden ist. Loggen Sie sich in diesem Tutorial aus Sicherheitsgründen als Benutzer ohne Rootberechtigung ein, sodass Sie den generierten öffentlichen Schlüssel mit dem sammy-Benutzer, den Sie in der Anleitung zum Server-Setup kreiert haben, verbinden.

      Um einen Benutzer mit dem öffentlichen Schlüssel zu verbinden, müssen Sie die User-ID (UID) und Group-ID (GID) dieses Benutzers bereitstellen. Führen Sie den folgenden Befehl auf Ihrem Server aus, während Sie als sammy-Benutzer angemeldet sind:

      Sie sehen die UID und GID des Benutzers:

      Output

      uid=1000(sammy) gid=1000(sammy) groups=1000(sammy),27(sudo)

      Öffnen Sie nun die Datei authorized_pubkeys in Ihrem Editor:

      • sudo nano /etc/nshd/authorized_pubkeys

      Fügen Sie eine einzelne Zeile hinzu, die den öffentlichen Schlüssel sowie die UID und GID enthält, die durch Leerstellen getrennt sind:

      authorized_pubkeys

      5d5367a5730427c205904a4457392051d2045dbce0186518fb6eb24dd9e41ba6 1000 1000
      

      Speichern Sie die Datei.

      Überprüfen Sie, ob die Datei den richtigen Inhalt enthält:

      • cat /etc/nshd/authorized_pubkeys

      Sie sehen Ihren Schlüssel auf dem Bildschirm:

      Output

      5d5367a5730427c205904a4457392051d2045dbce0186518fb6eb24dd9e41ba6 1000 1000

      Starten Sie dann den Daemon nshd neu, um die Änderungen anzuwenden:

      • sudo systemctl restart nshd.service

      Führen Sie nun einen Test aus, indem Sie sich mit dem Server verbinden und einen Befehl ausführen.

      Schritt 3 — Senden eines Befehls an den Remote-Rechner und Empfangen einer Antwort

      Geben Sie in NKN Shell Client Ihre Remote-nshd-Adresse aus Schritt 1 sowie einen optionalen Client-Identifikator ein:

      Die nsh-Website mit der ausgefüllten Remote-Adresse

      Klicken Sie auf Connect, um die Verbindung herzustellen.

      Sie werden mit Ihrem Remote-Rechner verbunden und bekommen eine Terminal-Eingabeaufforderung im Browser angezeigt. Sie können nun weiter vorgehen wie sonst mit SSH. Führen Sie beispielsweise den folgenden Befehl aus, um in das Verzeichnis /etc/nshd zu wechseln:

      Dann listen Sie den Inhalt auf:

      Sie sehen den Inhalt des Verzeichnisses:

      Output

      authorized_pubkeys wallet.json wallet.pswd

      Sie können sich mit der Eingabe von exit trennen. Wenn Sie sich erneut verbinden möchten, gehen Sie zurück zur Web-Oberfläche und geben Ihre Verbindungsdaten ein. Wenn Sie ein neues Schlüsselpaar generieren, müssen Sie den neuen öffentlichen Schlüssel Ihrem Server hinzufügen.

      Zusammenfassung

      In diesem Tutorial haben Sie nsh installiert und konfiguriert, um Befehle sicher und bequem zu einem Remote-Rechner zu senden. Nsh bietet eine hervorragende Möglichkeit, auf Ihre Remote-Rechner zuzugreifen, wenn Sie schnell einen Befehl ausführen müssen, um den neuesten Status eines Dienstes zu erhalten oder um einen Blick auf die Konfigurationseinstellungen zu werfen. Die Anwendung befindet sich auf dem globalen öffentlichen Netzwerk von NKN und ist kostenfrei nutzbar, sodass Sie sie heute in Ihre eigene Anwendung oder Ihren Workflow einbinden können.

      Erkunden Sie auch nkn-tunnel, das SSH und andere TCP-basierte Anwendungen unterstützt.



      Source link

      Empfohlene Schritte zur Sicherung eines DigitalOcean Kubernetes Clusters


      Der Autor wählte die Kooperation Open Sourcing Mental Illness, um eine Spende im Rahmen des Programms Write for DOnations zu erhalten.

      Einführung

      Kubernetes, die Open-Source-Container-Orchestrierungsplattform, wird immer mehr zur bevorzugten Lösung für die Automatisierung, Skalierung und Verwaltung von Hochverfügbarkeits-Clustern. Aufgrund der zunehmenden Popularität wird die Sicherheit von Kubernetes immer wichtiger.

      In Anbetracht der beweglichen Teile, die in Kubernetes involviert sind, und der Vielfalt der Einsatzszenarien kann die Sicherung von Kubernetes manchmal komplex sein. Ziel dieses Artikels ist es daher, eine solide Sicherheitsbasis für einen DigitalOcean Kubernetes (DOKS) Cluster bereitzustellen. Beachten Sie, dass dieses Tutorial grundlegende Sicherheitsmaßnahmen für Kubernetes behandelt und eher als Startpunkt und nicht als erschöpfender Leitfaden gedacht ist. Zusätzliche Schritte finden Sie in der offiziellen Kubernetes-Dokumentation.

      In diesem Tutorial nehmen Sie grundlegende Schritte zur Sicherung Ihres DigitalOcean Kubernetes Clusters vor. Sie konfigurieren eine sichere lokale Authentifizierung mit TLS/SSL-Zertifikaten, vergeben Berechtigungen an lokale Benutzer mit rollenbasierten Zugriffskontrollen (RBAC) sowie an Kubernetes-Anwendungen und -Bereitstellungen mit Service Accounts, und richten Ressourcenbegrenzungen mit den Zugriffs-Controllern ResourceQuota und LimitRange ein.

      Voraussetzungen

      Um dieses Tutorial auszuführen, benötigen Sie Folgendes:

      • Einen von DigitalOcean Kubernetes (DOKS) verwalteten Cluster mit 3 Standardknoten, die jeweils mit mindestens 2 GB RAM und 1 vCPU konfiguriert sind. Eine detaillierte Anleitung für die Erstellung eines DOKS-Clusters finden Sie in unserem Leitfaden Kubernetes Quickstart. Dieses Tutorial verwendet die DOKS-Version 1.16.2-do.1.
      • Einen für die Verwaltung des DOKS-Clusters konfigurierten lokalen Client, mit einer vom DigitalOcean Control Panel heruntergeladenen Cluster-Konfigurationsdatei, gespeichert unter ~/.kube/config. Eine detaillierte Anleitung für die Konfiguration eines auf Remote-Ebene verwalteten DOKS finden Sie im Leitfaden Verbinden mit einem DigitalOcean Kubernetes Cluster. Im Einzelnen benötigen Sie Folgendes:
        • Die auf Ihrem lokalen Rechner installierte Befehlszeilenschnittstelle kubectl. Mehr über die Installation und Konfiguration von kubectl können Sie der offiziellen Dokumentation entnehmen. Dieses Tutorial verwendet die kubectl-Version 1.17.0-00.
        • Das offizielle DigitalOcean-Befehlszeilen-Tool doctl. Anweisungen für die Installation finden Sie auf der Seite doctl GitHub Page. Dieses Tutorial verwendet die Version doctl 1.36.0.

      Schritt 1 – Aktivieren der Remotebenutzer-Authentifizierung

      Nach der Erfüllung der Voraussetzungen erhalten Sie einen Kubernetes-Superuser, der sich über ein vordefiniertes DigitalOcean-Träger-Token authentifiziert. Die gemeinschaftliche Nutzung dieses Berechtigungsnachweises ist jedoch keine gute Sicherheitspraxis, da dieses Konto umfangreiche und möglicherweise destruktive Änderungen an Ihrem Cluster verursachen kann. Um diese Möglichkeit einzugrenzen, können Sie zusätzliche Benutzer einrichten, die von ihren jeweiligen lokalen Clients authentifiziert werden.

      In diesem Abschnitt authentifizieren Sie neue Benutzer beim DOKS-Remote-Cluster von lokalen Clients aus mit sicheren SSL/TLS-Zertifikaten. Dies ist ein dreistufiger Prozess: Zuerst generieren Sie für jeden Benutzer eine Zertifikatsignierungsanforderung, die Certificate Signing Requests (CSR)​​. Danach genehmigen Sie diese Zertifikate direkt im Cluster über kubectl. Abschließend erstellen Sie für jeden Benutzer eine kubeconfig-Datei mit den entsprechenden Zertifikaten. Weitere Informationen über zusätzliche von Kubernetes unterstützte Authentifizierungsverfahren finden Sie in der Kubernetes-Authentifizierungsdokumentation.

      Erstellen von Zertifikatsignierungsanforderungen für neue Benutzer

      Prüfen Sie vor dem Start die DOKS-Clusterverbindung von dem lokalen Rechner aus, der bei den Voraussetzungen konfiguriert wurde:

      Je nach Ihrer Konfiguration sehen Sie ein ähnliches Output:

      Output

      Kubernetes master is running at https://a6616782-5b7f-4381-9c0f-91d6004217c7.k8s.ondigitalocean.com CoreDNS is running at https://a6616782-5b7f-4381-9c0f-91d6004217c7.k8s.ondigitalocean.com/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'.

      Das bedeutet, dass Sie mit dem DOKS-Cluster verbunden sind.

      Erstellen Sie als Nächstes einen lokalen Ordner für die Client-Zertifikate. In diesem Tutorial nutzen Sie zur Speicherung aller Zertifikate ~/certs.

      Sie autorisieren in diesem Tutorial einen neuen Benutzer namens sammy, um auf den Cluster zuzugreifen. Sie können dies auch auf einen Benutzer Ihrer Wahl ändern. Generieren Sie mit der SSL- und TLS-Bibliothek OpenSSL einen neuen privaten Schlüssel für Ihren Benutzer mit dem folgenden Befehl:

      • openssl genrsa -out ~/certs/sammy.key 4096

      Das -out-Flag erzeugt die Output-Datei ~/certs/sammy.key, und 4096 setzt den Schlüssel als 4096-Bit. Weitere Informationen über OpenSSL finden Sie in unserem Leitfaden OpenSSL Essentials.

      Erstellen Sie nun eine Konfigurationsdatei zur Zertifikatsignierungsanforderung. Öffnen Sie die folgende Datei mit einem Texteditor (für dieses Tutorial verwenden wir nano):

      • nano ~/certs/sammy.csr.cnf

      Fügen Sie folgenden Inhalt in die Datei sammy.csr.cnf ein, um im Subjekt den gewünschten Benutzernamen als “common name” (CN) und die Gruppe als “organization”(O) festzulegen:

      ~/certs/sammy.csr.cnf

      [ req ]
      default_bits = 2048
      prompt = no
      default_md = sha256
      distinguished_name = dn
      [ dn ]
      CN = sammy
      O = developers
      [ v3_ext ]
      authorityKeyIdentifier=keyid,issuer:always
      basicConstraints=CA:FALSE
      keyUsage=keyEncipherment,dataEncipherment
      extendedKeyUsage=serverAuth,clientAuth
      

      Die Konfigurationsdatei für die Zertifikatsignierungsanforderung enthält alle erforderlichen Informationen, die Benutzeridentität und entsprechende Verwendungsparameter für den Benutzer. Das letzte Argument extendedKeyUsage=serverAuth,clientAuth ermöglicht Benutzern die Authentifizierung ihrer lokalen Clients mit dem DOKS-Cluster mittels des Zertifikats, sobald es unterzeichnet wurde.

      Erstellen Sie als Nächstes die Zertifikatsignierungsanforderung (CSR) für sammy:

      • openssl req -config ~/certs/sammy.csr.cnf -new -key ~/certs/sammy.key -nodes -out ~/certs/sammy.csr

      Mit -config können Sie die Konfigurationsdatei für die CSR festlegen, und -new signalisiert die Erstellung einer neuen CSR für den durch -key festgelegten Schlüssel.

      Sie können Ihre Zertifikatsignierungsanforderung überprüfen, indem Sie folgenden Befehl ausführen:

      • openssl req -in ~/certs/sammy.csr -noout -text

      Hier geben Sie die CSR mit -in an und verwenden -text, um die Zertifikatsanforderung in Text auszudrucken.

      Das Output zeigt die Zertifikatsanforderung, deren Anfang folgendermaßen aussieht:

      Output

      Certificate Request: Data: Version: 1 (0x0) Subject: CN = sammy, O = developers Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (4096 bit) ...

      Wiederholen Sie den gleichen Vorgang, um CSRs für alle weiteren Benutzer zu erstellen. Wenn Sie alle Zertifikatsignierungsanforderungen in dem Ordner ~/certs des Administrators gespeichert haben, fahren Sie mit dem nächsten Schritt fort, um sie zu genehmigen.

      Verwalten von Zertifikatsignierungsanforderungen mit der Kubernetes-API

      Sie können die TLS-Zertifikate, die für die Kubernetes-API ausgegeben wurden, mit dem Befehlszeilen-Tool kubectl entweder genehmigen oder ablehnen. So können Sie sicherstellen, dass der angeforderte Zugang für den jeweiligen Benutzer geeignet ist. In diesem Abschnitt senden Sie die Zertifikatsanforderung für sammy und genehmigen sie.

      Zum Senden einer CSR an den DOKS-Cluster verwenden Sie den folgenden Befehl:

      cat <<EOF | kubectl apply -f -
      apiVersion: certificates.k8s.io/v1beta1
      kind: CertificateSigningRequest
      metadata:
        name: sammy-authentication
      spec:
        groups:
        - system:authenticated
        request: $(cat ~/certs/sammy.csr | base64 | tr -d 'n')
        usages:
        - digital signature
        - key encipherment
        - server auth
        - client auth
      EOF
      

      Dieser Befehl mit einem Bash-Heredoc nutzt cat, um die Zertifikatsanforderung an kubectl apply weiterzuleiten.

      Sehen Sie sich die Zertifikatsanforderung genauer an:

      • name: sammy-authentication erstellt einen Metadaten-Identifizierer, hier sammy-authentication genannt.
      • request: $(cat ~/certs/sammy.csr | Basis64 | tr -d 'n') sendet die Zertifikatsignierungsanforderung sammy.csr als Base64 kodiert an den Cluster.
      • server auth und client auth geben die beabsichtigte Verwendung des Zertifikats an. In diesem Fall ist der Zweck die Benutzerauthentifizierung.

      Das Output sieht ungefähr wie folgt aus:

      Output

      certificatesigningrequest.certificates.k8s.io/sammy-authentication created

      Sie können den Status der Zertifikatsignierungsanforderung mit diesem Befehl prüfen:

      Je nach Ihrer Cluster-Konfiguration sehen Sie ein ähnliches Output:

      Output

      NAME AGE REQUESTOR CONDITION sammy-authentication 37s your_DO_email Pending

      Als Nächstes genehmigen Sie die CSR durch Verwendung des Befehls:

      • kubectl certificate approve sammy-authentication

      Sie erhalten eine Nachricht, die die Operation bestätigt:

      Output

      certificatesigningrequest.certificates.k8s.io/sammy-authentication approved

      Anmerkung: Als Administrator können Sie eine CSR auch mit dem Befehl kubectl certificate deny sammy-authentication ablehnen. Weitere Informationen über die Verwaltung von TLS-Zertifikaten finden Sie in der offiziellen Dokumentation von Kubernetes.

      Nachdem die CSR nun genehmigt wurde, können Sie sie auf den lokalen Rechner herunterladen, indem Sie Folgendes ausführen:

      • kubectl get csr sammy-authentication -o jsonpath='{.status.certificate}' | base64 --decode > ~/certs/sammy.crt

      Dieser Befehl dekodiert das Base64-Zertifikat für eine richtige Anwendung durch kubectl und speichert es dann als ~/certs/sammy.crt.

      Mit dem signierten sammy-Zertifikat können Sie nun die kubeconfig-Datei des Benutzers erstellen.

      Erstellen der Remotebenutzer-Kubeconfig

      Als Nächstes erstellen Sie eine spezifische kubeconfig-Datei für den sammy-Benutzer. Dadurch erhalten Sie mehr Kontrolle über den Zugriff des Benutzers auf Ihren Cluster.

      Der erste Schritt beim Erstellen einer neuen kubeconfig ist die Erstellung einer Kopie der aktuellen kubeconfig-Datei. In diesem Tutorial benennen Sie die neue kubeconfig-Datei config-sammy:

      • cp ~/.kube/config ~/.kube/config-sammy

      Als Nächstes editieren Sie die neue Datei:

      • nano ~/.kube/config-sammy

      Behalten Sie die ersten acht Zeilen dieser Datei, da sie die erforderlichen Informationen für die SSL/TLS-Verbindung mit dem Cluster enthalten. Ersetzen Sie dann ausgehend vom Benutzerparameter den Text durch die folgenden hervorgehobenen Zeilen, damit die Datei ähnlich wie Folgendes aussieht:

      config-sammy

      apiVersion: v1
      clusters:
      - cluster:
          certificate-authority-data: certificate_data
        name: do-nyc1-do-cluster
      contexts:
      - context:
          cluster: do-nyc1-do-cluster
          user: sammy
        name: do-nyc1-do-cluster
      current-context: do-nyc1-do-cluster
      kind: Config
      preferences: {}
      users:
      - name: sammy
        user:
          client-certificate: /home/your_local_user/certs/sammy.crt
          client-key: /home/your_local_user/certs/sammy.key
      

      Anmerkung: Sowohl für client-certificate als auch für client-key verwenden Sie den absoluten Pfad zu deren entsprechenden Zertifikatsstandorten. Andernfalls erzeugt kubectl einen Fehler.

      Speichern und schließen Sie die Datei.

      Sie können die neue Benutzerverbindung mit kubectl cluster-info testen:

      • kubectl --kubeconfig=/home/your_local_user/.kube/config-sammy cluster-info

      Sie erhalten einen ähnlichen Fehler:

      Output

      To further debug and diagnose cluster problems, use 'kubectl cluster-info dump'. Error from server (Forbidden): services is forbidden: User "sammy" cannot list resource "services" in API group "" in the namespace "kube-system"

      Dieser Fehler wird erwartet, weil der Benutzer sammy noch keine Berechtigung hat, eine Ressource auf dem Cluster zu listen. Die Erteilung von Berechtigungen an Benutzer wird im nächsten Schritt gezeigt. Das jetzige Output bestätigt, dass die SSL/TLS-Verbindung erfolgreich war und die Authentifizierungsangaben von sammy von der Kubernetes-API akzeptiert wurden.

      Schritt 2 – Autorisieren von Benutzern über die Rollenbasierte Zugriffskontrolle (RBAC)

      Nach der Authentifizierung eines Benutzers bestimmt die API seine Berechtigungen mit dem in Kubernetes integrierten Modell der Role Based Access Control (RBAC), der rollenbasierten Zugriffskontrolle. RBAC ist eine wirksame Methode zur Beschränkung von Benutzerrechten auf der Grundlage der jeweils zugewiesenen Rolle. Aus sicherheitstechnischer Sicht erlaubt RBAC das Festlegen genau definierter Berechtigungen und limitiert damit die Möglichkeiten von Benutzern, auf vertrauliche Daten zuzugreifen oder Befehle auf Superuser-Ebene auszuführen. Weitere detaillierte Informationen über Benutzerrollen finden Sie in der Kubernetes-RBAC-Dokumentation.

      In diesem Schritt verwenden Sie kubectl, um die vordefinierte Rolle edit dem Benutzer sammy im default-Namensraum zuzuweisen. In einer Produktivumgebung können Sie auch benutzerdefinierte Rollen und/oder benutzerdefinierte Rollenbindungen verwenden.

      Berechtigungen erteilen

      In Kubernetes bedeutet die Erteilung von Berechtigungen, einem Benutzer die gewünschte Rolle zuzuweisen. Erteilen Sie edit-Berechtigungen an den Benutzer sammy im default-Namensraum mit diesem Befehl:

      • kubectl create rolebinding sammy-edit-role --clusterrole=edit --user=sammy --namespace=default

      Sie erhalten ein Output, das dem Folgenden ähnelt:

      Output

      rolebinding.rbac.authorization.k8s.io/sammy-edit-role created

      Im Einzelnen setzt sich der Befehl wie folgt zusammen:

      • create rolebinding sammy-edit-role erstellt eine neue Rollenbindung, in diesem Fall namens sammy-edit-role.
      • --clusterrole=edit wendet die vordefinierte Rolle edit auf einen globalen Gültigkeitsbereich (Cluster-Rolle) an.
      • --user=sammy gibt an, an welchen Benutzer die Rolle gebunden wird.
      • --namespace=default erteilt die Rollenberechtigung des Benutzers im angegebenen Namensraum, in diesem Fall default.

      Als Nächstes verifizieren Sie die Benutzerberechtigung, indem Sie Pods in dem default-Namensraum auflisten. Sie können erkennen, ob die RBAC-Autorisierung wie erwartet funktioniert, wenn keine Fehler angezeigt werden.

      • kubectl --kubeconfig=/home/your_local_user/.kube/config-sammy auth can-i get pods

      Sie erhalten folgendes Output:

      Output

      yes

      Sie haben sammy nun Berechtigungen zugewiesen. Im nächsten Abschnitt sehen Sie, wie Sie diese Berechtigungen wieder entziehen.

      Berechtigungen entziehen

      In Kubernetes entziehen Sie Berechtigungen, indem Sie die Rollenbindung des Benutzers entfernen.

      In diesem Tutorial löschen Sie die edit-Rolle des Benutzers sammy, indem Sie folgenden Befehl ausführen:

      • kubectl delete rolebinding sammy-edit-role

      Sie erhalten folgendes Output:

      Output

      rolebinding.rbac.authorization.k8s.io "sammy-edit-role" deleted

      Überprüfen Sie, ob die Berechtigungen wie erwartet entzogen wurden, indem Sie Pods im default-Namensraum auflisten:

      • kubectl --kubeconfig=/home/localuser/.kube/config-sammy --namespace=default get pods

      Sie erhalten die folgende Fehlermeldung:

      Output

      Error from server (Forbidden): pods is forbidden: User "sammy" cannot list resource "pods" in API group "" in the namespace "default"

      Diese zeigt, dass die Autorisierung entzogen wurde.

      Aus sicherheitstechnischer Sicht gibt das Kubernetes-Autorisierungsmodell Cluster-Administratoren die Flexibilität, die Benutzerrechte bei Bedarf zu ändern. Außerdem ist die rollenbasierte Zugriffskontrolle nicht auf einen physischen Benutzer beschränkt; Sie können auch Cluster-Diensten Berechtigungen erteilen und entziehen. Mehr dazu erfahren Sie im nächsten Abschnitt.

      Weitere Informationen über die RBAC-Autorisierung und wie Sie benutzerdefinierte Rollen erstellen, finden Sie in der offiziellen Dokumentation.

      Schritt 3 – Verwalten von Anwendungsberechtigungen mit Service Accounts

      Wie im vorherigen Abschnitt erwähnt, erstrecken sich die RBAC-Autorisierungsmechanismen nicht nur auf menschliche Benutzer. Nicht-menschliche Cluster-Benutzer wie Anwendungen, Dienste und Prozesse, die in Pods ausgeführt werden, authentifizieren sich mit dem API-Server über Dienstkonten, die in Kubernetes Service Accounts (SAs) genannt werden. Wenn ein Pod in einem Namensraum erstellt wird, können Sie hierfür entweder das default-Service-Account verwenden oder ein Service Account Ihrer Wahl definieren. Die Möglichkeit, Anwendungen und Prozessen individuelle SAs zuzuordnen, gibt Administratoren die Freiheit, Berechtigungen nach Bedarf zu erteilen oder zu entziehen. Außerdem wird die Zuordnung bestimmter SAs zu produktionskritischen Anwendungen als beste Sicherheitspraxis angesehen. Da Service Accounts für die Authentifizierung und damit für die RBAC-Autorisierungsprüfungen verwendet werden, können Cluster-Administratoren Sicherheitsbedrohungen eindämmen, indem sie die Zugriffsrechte für Service Accounts ändern und den fehlerhaften Prozess isolieren.

      Zur Veranschaulichung von Service Accounts verwendet dieses Tutorial einen Nginx-Webserver als Beispielanwendung.

      Bevor Sie Ihrer Anwendung ein bestimmtes SA zuweisen, müssen Sie dieses SA erstellen. Erstellen Sie ein neues Service Account namens nginx-sa im default-Namensraum:

      • kubectl create sa nginx-sa

      Sie erhalten Folgendes:

      Output

      serviceaccount/nginx-sa created

      Überprüfen Sie, ob das Service Account erstellt wurde, indem Sie Folgendes ausführen:

      Dadurch erhalten Sie eine Liste Ihrer Service Accounts:

      Output

      NAME SECRETS AGE default 1 22h nginx-sa 1 80s

      Nun erteilen Sie dem Service Account nginx-sa eine Rolle. In diesem Beispiel erteilen Sie nginx-sa dieselben Berechtigungen wie dem Benutzer sammy:

      • kubectl create rolebinding nginx-sa-edit
      • --clusterrole=edit
      • --serviceaccount=default:nginx-sa
      • --namespace=default

      Nach der Ausführung erhalten Sie Folgendes:

      Output

      rolebinding.rbac.authorization.k8s.io/nginx-sa-edit created

      Dieser Befehl verwendet das gleiche Format wie für den Benutzer sammy. Ausgenommen hierbei ist das Flag --serviceaccount=default:nginx-sa, wo Sie das Service Account nginx-sa dem default-Namensraum zuweisen.

      Überprüfen Sie mit diesem Befehl, ob die Rollenbindung erfolgreich war:

      Dadurch erhalten Sie folgendes Output:

      Output

      NAME AGE nginx-sa-edit 23s

      Nachdem Sie bestätigt haben, dass die Rollenbindung für das Service Account erfolgreich konfiguriert wurde, können Sie das Service Account einer Anwendung zuweisen. Wenn Sie einer Anwendung ein bestimmtes Service Account zuweisen, können Sie dessen Zugriffsrechte in Echtzeit verwalten und somit die Sicherheit des Clusters erhöhen.

      In diesem Tutorial dient ein nginx-Pod als Beispielanwendung. Erstellen Sie den neuen Pod und bestimmen Sie das Service Account nginx-sa mit dem folgenden Befehl:

      • kubectl run nginx --image=nginx --port 80 --serviceaccount="nginx-sa"

      Der erste Teil des Befehls erstellt einen neuen Pod, der einen nginx-Webserver auf Port :80 ausführt. Der zweite Teil --serviceaccount="nginx-sa" zeigt an, dass dieser Pod das nginx-sa-Service-Account und nicht das default-Service-Account verwenden soll.

      Sie erhalten ein Output, das dem Folgenden ähnelt:

      Output

      deployment.apps/nginx created

      Überprüfen Sie mit kubectl describe, ob die neue Anwendung das Service Account verwendet:

      • kubectl describe deployment nginx

      Dadurch erhalten Sie eine längere Beschreibung der Bereitstellungsparameter. Unter dem Abschnitt Pod Template sehen Sie ein Output, das diesem ähnelt:

      Output

      ... Pod Template: Labels: run=nginx Service Account: nginx-sa ...

      In diesem Abschnitt haben Sie das Service Account nginx-sa im default-Namensraum erstellt und dem nginx-Webserver zugewiesen. Sie können nun die nginx-Berechtigungen in Echtzeit steuern, indem Sie seine Rolle nach Bedarf ändern. Sie können Anwendungen auch gruppieren, indem Sie jeder dasselbe Service Account zuweisen und dann Mehrfachänderungen (bulk changes) an Berechtigungen vornehmen. Sie können zudem kritische Anwendungen isolieren, indem Sie diesen ein individuelles SA zuweisen.

      Die Idee hinter der Zuweisung von Rollen an Ihre Anwendungen/Bereitstellungen besteht auch darin, eine Feinabstimmung der Berechtigungen vornehmen zu können. In realen Produktivumgebungen haben Sie möglicherweise mehrere Bereitstellungen, die unterschiedliche Berechtigungen erfordern, die von reinen Leseberechtigungen bis hin zu vollen Administratorrechten reichen. Mit RBAC können Sie den Zugriff auf den Cluster flexibel nach Bedarf einschränken.

      Als Nächstes richten Sie Zugangs-Controller ein, um Ressourcen zu kontrollieren und gegen übermäßigen Ressourcenverbrauch zu schützen.

      Schritt 4 – Einrichten von Zugriffs-Controllern

      Die Zugriffs-Controller von Kubernetes sind optionale Plugins, die in das kube-apiserver-Binärprogramm kompiliert werden, um die Sicherheitsoptionen zu erweitern. Zugriffs-Controller fangen Anfragen ab, nachdem diese die Authentifizierungs- und Autorisierungsphase durchlaufen haben. Nach dem Abfangen der Anfrage führen die Zugriffs-Controller den jeweils festgelegten Code aus, bevor die Anfrage ausgeführt wird.

      Während das Ergebnis einer Authentifizierungs- oder Autorisierungsprüfung ein boolescher Wahr-oder-Falsch-Wert ist, der die Anfrage entweder erlaubt oder ablehnt, können Zugriffs-Controller wesentlich vielfältiger agieren. Zugriffs-Controller können Anfragen in gleicher Weise wie eine Authentifizierung validieren. Sie können jedoch auch zusätzlich Anfragen mutieren und ändern sowie Objekte modifizieren, bevor diese zugelassen werden.

      In diesem Schritt verwenden Sie die Zugriffs-Controller ResourceQuota und LimitRange. Diese schützen Ihren Cluster, indem Sie Anfragen, die zu einem übermäßigen Ressourcenverbrauch oder einem Denial-of-Service-Angriff führen können, mutieren. Der Zugriffs-Controller ResourceQuota ermöglicht Administratoren, die Rechnerressourcen, die Speicherressourcen und die Quantität jedes Objekts in einem Namensraum zu beschränken. Der Zugriffs-Controller LimitRange beschränkt die Anzahl der von Containern verwendeten Ressourcen. Die gleichzeitige Verwendung dieser beiden Zugriffs-Controller schützt Ihren Cluster vor Angriffen, durch die Ihre Ressourcen nicht mehr verfügbar wären.

      Um zu zeigen, wie ResourceQuota funktioniert, implementieren Sie nun einige Beschränkungen im default-Namensraum. Beginnen Sie mit dem Erstellen einer neuen ResourceQuota-Objektdatei:

      • nano resource-quota-default.yaml

      Fügen Sie die folgende Objektdefinition hinzu, um Beschränkungen für den Ressourcenverbrauch im default-Namensraum festzulegen. Sie können die Werte entsprechend der physischen Ressourcen Ihrer Knoten nach Bedarf anpassen:

      resource-quota-default.yaml

      apiVersion: v1
      kind: ResourceQuota
      metadata:
        name: resource-quota-default
      spec:
        hard:
          pods: "2"
          requests.cpu: "500m"
          requests.memory: 1Gi
          limits.cpu: "1000m"
          limits.memory: 2Gi
          configmaps: "5"
          persistentvolumeclaims: "2"
          replicationcontrollers: "10"
          secrets: "3"
          services: "4"
          services.loadbalancers: "2"
      

      Diese Definition verwendet das Schlüsselwort hard, um harte Beschränkungen wie z. B. die maximale Anzahl von pods, configmaps, PersistentVolumeClaims, ReplicationControllers, secrets, services und loadbalancers festzulegen. Sie setzt auch Beschränkungen für Rechnerressourcen wie:

      • requests.cpu, das den maximalen CPU-Wert von Anfragen in milliCPU oder einem Tausendstel eines CPU-Kerns festlegt.
      • requests.memory, das den maximalen Speicherwert von Anfragen in Bytes festlegt.
      • limits.cpu, das den maximalen CPU-Wert von Limits in milliCPUs festlegt.
      • limits.memory, das den maximalen Speicherwert von Limits in Bytes festlegt.

      Speichern und schließen Sie die Datei.

      Erstellen Sie nun mit dem folgenden Befehl das Objekt im Namensraum:

      • kubectl create -f resource-quota-default.yaml --namespace=default

      Dadurch erhalten Sie:

      Output

      resourcequota/resource-quota-default created

      Beachten Sie, dass Sie das Flag -f verwenden, um Kubernetes den Speicherort der ResourceQuota-Datei anzuzeigen, sowie das Flag --namespace, um anzugeben, welcher Namensraum aktualisiert wird.

      Nach dem Erstellen des Objekts ist Ihr ResourceQuota aktiv. Sie können die default-Namensraumquoten mit describe quota prüfen:

      • kubectl describe quota --namespace=default

      Das Output sieht dem Folgenden ähnlich; mit den harten Limits, die Sie in der Datei resource-quota-default.yaml festgelegt haben:

      Output

      Name: resource-quota-default Namespace: default Resource Used Hard -------- ---- ---- configmaps 0 5 limits.cpu 0 1 limits.memory 0 2Gi persistentvolumeclaims 0 2 pods 1 2 replicationcontrollers 0 10 requests.cpu 0 500m requests.memory 0 1Gi secrets 2 3 services 1 4 services.loadbalancers 0 2

      ResourceQuotas werden in absoluten Einheiten ausgedrückt. Daher erhöht das Hinzufügen zusätzlicher Knoten nicht automatisch die hier definierten Werte. Wenn weitere Knoten hinzugefügt werden, müssen Sie die Werte hier manuell bearbeiten, um die Ressourcen zu proportionieren. ResourceQuotas können so oft wie nötig modifiziert werden. Sie können jedoch nicht entfernt werden, es sei denn, der gesamte Namensraum wird entfernt.

      Wenn Sie einen bestimmten ResourceQuota-Controller ändern möchten, aktualisieren Sie die entsprechende .yaml-Datei und wenden Sie die Änderungen mit dem folgenden Befehl an:

      • kubectl apply -f resource-quota-default.yaml --namespace=default

      Weitere Informationen über den Zugriffs-Controller ResourceQuota finden Sie in der offiziellen Dokumentation.

      Nachdem Ihr ResourceQuota nun eingerichtet ist, fahren Sie mit der Konfiguration des Zugriffs-Controllers LimitRange fort. Ähnlich wie der Controller ResourceQuota Limits für Namensräume umsetzt, setzt der Controller LimitRange die durch Validierung und Mutation von Containern deklarierten Beschränkungen durch.

      Wie zuvor beginnen Sie mit dem Erstellen der Objektdatei:

      • nano limit-range-default.yaml

      Nun können Sie mit dem LimitRange-Objekt den Ressourcenverbrauch nach Bedarf beschränken. Fügen Sie folgenden Inhalt als Beispiel eines typischen Anwendungsfalls hinzu:

      limit-ranges-default.yaml

      apiVersion: v1
      kind: LimitRange
      metadata:
        name: limit-range-default
      spec:
        limits:
        - max:
            cpu: "400m"
            memory: "1Gi"
          min:
            cpu: "100m"
            memory: "100Mi"
          default:
            cpu: "250m"
            memory: "800Mi"
          defaultRequest:
            cpu: "150m"
            memory: "256Mi"
          type: Container
      

      Die in limit-ranges-default.yaml verwendeten Beispielwerte beschränken den Containerspeicher auf ein Maximum von 1Gi und die CPU-Nutzung auf ein Maximum von 400 m, was einer Kubernetes-Metrik von 400 milliCPU entspricht und bedeutet, dass der Container auf nahezu die Hälfte seines Kerns beschränkt ist.

      Als Nächstes stellen Sie das Objekt mit dem folgenden Befehl für den API-Server bereit:

      • kubectl create -f limit-range-default.yaml --namespace=default

      Dadurch erhalten Sie folgendes Output:

      Output

      limitrange/limit-range-default created

      Nun können Sie die neuen Limits mit dem folgenden Befehl überprüfen:

      • kubectl describe limits --namespace=default

      Das Output sieht ungefähr wie folgt aus:

      Output

      Name: limit-range-default Namespace: default Type Resource Min Max Default Request Default Limit Max Limit/Request Ratio ---- -------- --- --- --------------- ------------- ----------------------- Container cpu 100m 400m 150m 250m - Container memory 100Mi 1Gi 256Mi 800Mi -

      Um den LimitRanger arbeiten zu sehen, stellen Sie einen Standard-nginx-Container mit dem folgenden Befehl bereit:

      • kubectl run nginx --image=nginx --port=80 --restart=Never

      Dadurch erhalten Sie folgendes Output:

      Output

      pod/nginx created

      Überprüfen Sie, wie der Zugriffs-Controller den Container mutiert hat, indem Sie folgenden Befehl ausführen:

      • kubectl get pod nginx -o yaml

      Dadurch erhalten Sie viele Zeilen an Output. Im Abschnitt der Container-Spezifizierung finden Sie die im LimitRange angegebenen Ressourcenlimits:

      Output

      ... spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: nginx ports: - containerPort: 80 protocol: TCP resources: limits: cpu: 250m memory: 800Mi requests: cpu: 150m memory: 256Mi ...

      Dies wäre genauso, wenn Sie die Angaben für resources und requests manuell in der Container-Spezifikation festlegen würden.

      In diesem Schritt haben Sie die Zugriffs-Controller ResourceQuota und LimitRange verwendet, um die Ressourcen Ihres Clusters vor schädlichen Angriffen zu schützen. Weitere Informationen über den Zugriffs-Controller LimitRange finden Sie in der offiziellen Dokumentation.

      Zusammenfassung

      In diesem Tutorial haben Sie eine grundlegende Kubernetes-Sicherheitsvorlage konfiguriert. Dadurch wurden die Benutzerauthentifizierung und -autorisierung, die Anwendungsberechtigungen und der Schutz der Cluster-Ressourcen bestimmt. Wenn Sie alle in diesem Tutorial behandelten Vorschläge kombinieren, haben Sie eine solide Grundlage für den Einsatz eines Kubernetes-Clusters in einer produktiven Umgebung. Nun können Sie beginnen, individuelle Aspekte Ihres Clusters je nach Ihrer Vorstellung zu härten.

      Wenn Sie mehr über Kubernetes erfahren möchten, besuchen Sie unsere Kubernetes-Ressourcenseite oder folgen Sie dem selbstgeführten Kurs Kubernetes für umfassende Entwickler.



      Source link