[ English | Indonesia | Deutsch | 日本語 ]

Anpassen der Middleware für die Objektspeicherung (Swift)

OpenStack Object Storage, bekannt als swift beim Lesen des Codes, basiert auf dem Python Paste Framework. Die beste Einführung in seine Architektur ist auf Read The Docs. Aufgrund der schnellen Nutzung dieses Frameworks durch das Projekt können Sie einem Projekt Funktionen hinzufügen, indem Sie benutzerdefinierten Code in die Pipeline eines Projekts einfügen, ohne den Kerncode ändern zu müssen.

Stellen Sie sich ein Szenario vor, in dem Sie öffentlichen Zugriff auf einen Ihrer Container haben, aber was Sie wirklich wollen, ist, den Zugriff darauf auf eine Reihe von IPs basierend auf einer Whitelist zu beschränken. In diesem Beispiel erstellen wir eine Middleware für Swift, die den Zugriff auf einen Container nur von einer Reihe von IP-Adressen aus ermöglicht, wie sie durch die Metadatenelemente des Containers bestimmt werden. Nur die IP-Adressen, die Sie explizit mit den Metadaten des Containers auf die Whitelist setzen, können auf den Container zugreifen.

Warnung

Dieses Beispiel dient nur zur Veranschaulichung. Es sollte nicht als Container-IP-Whitelist-Lösung ohne Weiterentwicklung und umfangreiche Sicherheitstests verwendet werden.

Wenn Sie der Bildschirmsitzung beitreten, die stack.sh mit screen -r stack beginnt, sehen Sie einen Bildschirm für jeden laufenden Dienst, der einige oder mehrere sein kann, je nachdem, wie viele Dienste Sie DevStack zum Ausführen konfiguriert haben.

Das Sternchen * zeigt an, welches Bildschirmfenster Sie gerade betrachten. Dieses Beispiel zeigt, dass wir das Key (für Keystone) Bildschirmfenster betrachten:

0$ shell  1$ key*  2$ horizon  3$ s-proxy  4$ s-object  5$ s-container  6$ s-account

Die Bildschirmfenster haben folgenden Zweck:

shell

Eine Shell, in der Sie etwas Arbeit erledigen können

key*

Der Keystone-Service

horizon

Die Webanwendung des Horizon Dashboards

s-{name}

Die swift Dienste

Um die Middleware zu erstellen und über die Konfiguration Einfügen einzubinden:

Der gesamte Code für OpenStack lebt in /opt/stack. Gehen Sie in das Swift-Verzeichnis im shell Bildschirm und bearbeiten Sie Ihr Middleware-Modul.

  1. Wechseln Sie in das Verzeichnis, in dem der Objektspeicher installiert ist:

    $ cd /opt/stack/swift
    
  2. Erstellen Sie die ip_whitelist.py Python-Quelltextdatei:

    $ vim swift/common/middleware/ip_whitelist.py
    
  3. Kopieren Sie den Code wie unten gezeigt in ip_whitelist.py. Der folgende Code ist ein Middleware-Beispiel, das den Zugriff auf einen Container basierend auf der IP-Adresse einschränkt, wie am Anfang des Abschnitts erläutert. Die Middleware leitet die Anforderung an eine andere Anwendung weiter. In diesem Beispiel wird die swift „swob“-Bibliothek verwendet, um Web Server Gateway Interface (WSGI)-Anfragen und -Antworten in Objekte zu wickeln, mit denen swift interagieren kann. Wenn Sie fertig sind, speichern und schließen Sie die Datei.

    # vim: tabstop=4 shiftwidth=4 softtabstop=4
    # Copyright (c) 2014 OpenStack Foundation
    # All Rights Reserved.
    #
    #    Licensed under the Apache License, Version 2.0 (the "License"); you may
    #    not use this file except in compliance with the License. You may obtain
    #    a copy of the License at
    #
    #         http://www.apache.org/licenses/LICENSE-2.0
    #
    #    Unless required by applicable law or agreed to in writing, software
    #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
    #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
    #    License for the specific language governing permissions and limitations
    #    under the License.
    
    import socket
    
    from swift.common.utils import get_logger
    from swift.proxy.controllers.base import get_container_info
    from swift.common.swob import Request, Response
    
    class IPWhitelistMiddleware(object):
        """
        IP Whitelist Middleware
    
        Middleware that allows access to a container from only a set of IP
        addresses as determined by the container's metadata items that start
        with the prefix 'allow'. E.G. allow-dev=192.168.0.20
        """
    
        def __init__(self, app, conf, logger=None):
            self.app = app
    
            if logger:
                self.logger = logger
            else:
                self.logger = get_logger(conf, log_route='ip_whitelist')
    
            self.deny_message = conf.get('deny_message', "IP Denied")
            self.local_ip = socket.gethostbyname(socket.gethostname())
    
        def __call__(self, env, start_response):
            """
            WSGI entry point.
            Wraps env in swob.Request object and passes it down.
    
            :param env: WSGI environment dictionary
            :param start_response: WSGI callable
            """
            req = Request(env)
    
            try:
                version, account, container, obj = req.split_path(1, 4, True)
            except ValueError:
                return self.app(env, start_response)
    
            container_info = get_container_info(
                req.environ, self.app, swift_source='IPWhitelistMiddleware')
    
            remote_ip = env['REMOTE_ADDR']
            self.logger.debug("Remote IP: %(remote_ip)s",
                              {'remote_ip': remote_ip})
    
            meta = container_info['meta']
            allow = {k:v for k,v in meta.iteritems() if k.startswith('allow')}
            allow_ips = set(allow.values())
            allow_ips.add(self.local_ip)
            self.logger.debug("Allow IPs: %(allow_ips)s",
                              {'allow_ips': allow_ips})
    
            if remote_ip in allow_ips:
                return self.app(env, start_response)
            else:
                self.logger.debug(
                    "IP %(remote_ip)s denied access to Account=%(account)s "
                    "Container=%(container)s. Not in %(allow_ips)s", locals())
                return Response(
                    status=403,
                    body=self.deny_message,
                    request=req)(env, start_response)
    
    
    def filter_factory(global_conf, **local_conf):
        """
        paste.deploy app factory for creating WSGI proxy apps.
        """
        conf = global_conf.copy()
        conf.update(local_conf)
    
        def ip_whitelist(app):
            return IPWhitelistMiddleware(app, conf)
        return ip_whitelist
    

    Es gibt viele nützliche Informationen in env und conf`, mit denen Sie entscheiden können, was mit der Anfrage geschehen soll. Um mehr darüber zu erfahren, welche Eigenschaften verfügbar sind, können Sie die folgende Log-Anweisung in die Methode __init__ einfügen:

    self.logger.debug("conf = %(conf)s", locals())
    

    und die folgende Log-Anweisung in das __call__ Verfahren:

    self.logger.debug("env = %(env)s", locals())
    
  4. Um diese Middleware in die swift Paste Pipeline einzubinden, bearbeiten Sie eine Konfigurationsdatei, /etc/swift/proxy-server.conf:

    $ vim /etc/swift/proxy-server.conf
    
  5. Suchen Sie den Abschnitt [filter:ratelimit] in /etc/swift/proxy-server.conf, und kopieren Sie ihn danach in den folgenden Konfigurationsabschnitt:

    [filter:ip_whitelist]
    paste.filter_factory = swift.common.middleware.ip_whitelist:filter_factory
    # You can override the default log routing for this filter here:
    # set log_name = ratelimit
    # set log_facility = LOG_LOCAL0
    # set log_level = INFO
    # set log_headers = False
    # set log_address = /dev/log
    deny_message = You shall not pass!
    
  6. Suchen Sie den Abschnitt [pipeline:main] in /etc/swift/proxy-server.conf, und fügen Sie ip_whitelist nach Tarifbeschränkung der Liste so hinzu. Wenn Sie fertig sind, speichern und schließen Sie die Datei:

    [pipeline:main]
    pipeline = catch_errors gatekeeper healthcheck proxy-logging cache bulk tempurl ratelimit ip_whitelist ...
    
  7. Starten Sie den Dienst swift proxy neu, um Ihre Middleware schnell zu nutzen. Wechseln Sie zunächst zum Bildschirm „swift-proxy“:

    1. Drücken Sie Strg+A gefolgt von 3.

    2. Drücken Sie Strg+C, um den Dienst zu beenden.

    3. Drücken Sie Pfeil nach oben, um den letzten Befehl aufzurufen.

    4. Drücken Sie die Eingabetaste, um es auszuführen.

  8. Testen Sie Ihre Middleware mit der swift CLI. Beginnen Sie, indem Sie zum Shell-Bildschirm wechseln und beenden Sie ihn, indem Sie zum swift-proxy Bildschirm zurückkehren, um die Protokollausgabe zu überprüfen:

    1. Drücken Sie Strg+A gefolgt von 0.

    2. Stellen Sie sicher, dass Sie sich im Verzeichnis devstack` befinden:

      $ cd /root/devstack
      
    3. Quelle openrc, um Ihre Umgebungsvariablen für die CLI einzurichten:

      $ . openrc
      
    4. Erstellen Sie einen Container mit dem Namen Middleware-Test:

      $ swift post middleware-test
      
    5. Drücken Sie Ctrl+A gefolgt von 3**, um die Protokollausgabe zu überprüfen.

  9. Unter den Protokollanweisungen sehen Sie die Zeilen:

    proxy-server Remote IP: my.instance.ip.address (txn: ...)
    proxy-server Allow IPs: set(['my.instance.ip.address']) (txn: ...)
    

    Diese beiden Anweisungen werden von unserer Middleware erzeugt und zeigen, dass die Anfrage von unserer DevStack-Instanz gesendet wurde und erlaubt war.

  10. Testen Sie die Middleware von außerhalb von DevStack auf einem entfernten Computer, der Zugriff auf Ihre DevStack-Instanz hat:

    1. Installieren Sie die Clients keystone und swift auf Ihrem lokalen Rechner:

      # pip install python-keystoneclient python-swiftclient
      
    2. Versuchen Sie, die Objekte im Container Middleware-Test aufzulisten:

      $ swift --os-auth-url=http://my.instance.ip.address:5000/v2.0/ \
        --os-region-name=RegionOne --os-username=demo:demo \
        --os-password=devstack list middleware-test
      Container GET failed: http://my.instance.ip.address:8080/v1/AUTH_.../
          middleware-test?format=json 403 Forbidden   You shall not pass!
      
  11. Drücken Sie Ctrl+A gefolgt von 3**, um die Protokollausgabe zu überprüfen. Betrachten Sie die Swift-Log-Anweisungen noch einmal, und unter den Log-Anweisungen sehen Sie die Zeilen:

    proxy-server Authorizing from an overriding middleware (i.e: tempurl) (txn: ...)
    proxy-server ... IPWhitelistMiddleware
    proxy-server Remote IP: my.local.ip.address (txn: ...)
    proxy-server Allow IPs: set(['my.instance.ip.address']) (txn: ...)
    proxy-server IP my.local.ip.address denied access to Account=AUTH_... \
       Container=None. Not in set(['my.instance.ip.address']) (txn: ...)
    

    Hier sehen wir, dass die Anfrage abgelehnt wurde, weil die entfernte IP-Adresse nicht im Satz der erlaubten IPs enthalten war.

  12. Fügen Sie in Ihrer DevStack-Instanz auf dem Shell-Bildschirm einige Metadaten zu Ihrem Container hinzu, um die Anforderung von der entfernten Maschine zu ermöglichen:

    1. Drücken Sie Strg+A gefolgt von 0.

    2. Fügen Sie dem Container Metadaten hinzu, um die IP zu ermöglichen:

      $ swift post --meta allow-dev:my.local.ip.address middleware-test
      
    3. Versuchen Sie nun den Befehl aus Schritt 10 erneut und er ist erfolgreich. Es gibt keine Objekte im Container, also gibt es nichts aufzulisten, aber es gibt auch keinen Fehler zu melden.

      Warnung

      Funktionstests wie dieser sind kein Ersatz für richtige Geräte- und Integrationstests, aber sie dienen dazu, Ihnen den Einstieg zu erleichtern.

Sie können ein ähnliches Muster in anderen Projekten verfolgen, die das Python Paste Framework verwenden. Erstellen Sie einfach ein Middleware-Modul und schließen Sie es über die Konfiguration an. Die Middleware läuft im Rahmen der Projektpipeline nacheinander und kann bei Bedarf weitere Dienste aufrufen. Es wird kein Projektkern-Code berührt. Suchen Sie nach einem Pipeline Wert in den Konfigurationsdateien des Projekts conf oder ini in /etc/<project>`, um Projekte zu identifizieren, die Paste verwenden.

Wenn Ihre Middleware fertig ist, empfehlen wir Ihnen, sie Open Source zu verwenden und die Community auf der OpenStack-Mailingliste darüber zu informieren. Vielleicht benötigen andere die gleiche Funktionalität. Sie können Ihren Code verwenden, Feedback geben und möglicherweise einen Beitrag leisten. Wenn es genügend Unterstützung dafür gibt, können Sie vielleicht vorschlagen, dass sie dem offiziellen Swift Middleware hinzugefügt wird.