Anwendungen über SSH mit Instanzen verbinden

Wenn Sie mit Dienstkonten auf Ihren Instanzen automatisierte Aufgaben ausführen und andere Google Cloud Platform APIs interaktiv nutzen, kann für das Dienstkonto auch SSH-Zugriff auf andere Compute Engine-Instanzen erforderlich sein. In dieser Anleitung wird gezeigt, wie Sie Anwendungen für den Zugriff auf Ihre Instanzen über SSH-Verbindungen konfigurieren. In der Beispielanwendung dieser Anleitung werden ein Dienstkonto und OS Login für die SSH-Schlüsselverwaltung verwendet.

Wenn Sie diese Übung überspringen und sich das vollständige Codebeispiel ansehen möchten, rufen Sie die GitHub-Seite GoogleCloudPlatform/python-docs-samples auf.

Lernziele

In dieser Anleitung erfahren Sie, wie Sie folgende Aufgaben ausführen:

  • Dienstkonto erstellen und konfigurieren, um SSH-Zugriff von OS Login für Anwendungen bereitzustellen, die eine Verbindung zu Ihren Instanzen herstellen
  • Instanz erstellen, die mit Ihrem Dienstkonto verbunden ist
  • Beispielanwendung auf der Instanz konfigurieren, um das Dienstkonto zur Verwaltung seiner eigenen SSH-Schlüssel und zum Herstellen von SSH-Verbindungen zu verwenden
  • Anwendung auf einer Instanz ausführen, die mit dem Dienstkonto verbunden ist
  • Anwendung außerhalb von Compute Engine ausführen und dabei den Dienstkontoschlüssel manuell eingeben sowie zusätzliche SSH-Parameter festlegen

Kosten

In dieser Anleitung werden kostenpflichtige Komponenten der Cloud Platform verwendet, einschließlich Google Compute Engine.

Neuen Nutzern der Cloud Platform steht möglicherweise eine kostenlose Testversion zur Verfügung.

Vorbereitung

  1. Melden Sie sich in Ihrem Google-Konto an.

    Wenn Sie noch kein Konto haben, registrieren Sie sich hier für ein neues Konto.

  2. Wählen Sie ein Google Cloud Platform-Projekt aus oder erstellen Sie eines.

    Zur Seite "Ressourcen verwalten"

  3. Prüfen Sie, ob die Abrechnung für Ihr Google Cloud Platform-Projekt aktiviert ist.

    Informationen zum Aktivieren der Abrechnung

  4. Ihr Nutzerkonto muss die Berechtigung zum Erstellen, Löschen und Ändern mehrerer Compute Engine-Ressourcen haben. In dieser Anleitung wird davon ausgegangen, dass Ihnen die folgenden IAM-Rollen für Ihr Projekt zugewiesen sind:
    • compute.instanceAdmin.v1
    • compute.networkAdmin
    • compute.osAdminLogin
    • iam.serviceAccountAdmin
    • iam.serviceAccountKeyAdmin
    • iam.serviceAccountUser
  5. In dieser Anleitung wird außerdem davon ausgegangen, dass Sie Cloud Shell zur Ausführung von gcloud-Befehlen verwenden.

Dienstkonto und Beispielinstanzen erstellen und konfigurieren

In dieser Anleitung wird mit einem Dienstkonto und zwei Instanzen gezeigt, wie Ihre Anwendungen SSH-Befehle auf Remoteinstanzen ausführen können.

Konfigurieren Sie mit den folgenden Befehlen die Testumgebung:

  1. Rufen Sie Cloud Shell in der GCP Console auf:

    Cloud Shell aufrufen

  2. Exportieren Sie eine Umgebungsvariable, um Ihre Projekt-ID für zukünftige Befehle festzulegen:

    export PROJECT_ID='[PROJECT_ID]'
    
  3. Erstellen Sie ein neues Dienstkonto in Ihrem Projekt. Legen Sie für dieses Beispiel ein Dienstkonto mit dem Namen ssh-account an:

    gcloud iam service-accounts create ssh-account --project $PROJECT_ID \
       --display-name "ssh-account"
    
  4. Erstellen Sie ein temporäres Netzwerk namens ssh-example, das nur für dieses Beispiel verwendet wird:

    gcloud compute networks create ssh-example --project $PROJECT_ID
    
  5. Erstellen Sie eine Firewallregel, die alle SSH-Verbindungen zu Instanzen im Netzwerk ssh-example zulässt:

    gcloud compute firewall-rules create ssh-all --project $PROJECT_ID \
       --network ssh-example --allow tcp:22
    
  6. Erstellen Sie in us-central1-f eine Instanz namens target. Diese Instanz dient als Remoteinstanz, zu der Ihr Dienstkonto eine SSH-Verbindung herstellt. Für die Instanz muss OS Login entweder auf Projektebene oder auf Instanzebene aktiviert sein. In diesem Beispiel wird gezeigt, wie Sie mit dem Flag --metadata OS Login für diese spezielle Instanz aktivieren. Fügen Sie die Flags --no-service-account und --no-scopes hinzu, da die Instanz in diesem Beispiel keine API-Anfragen ausführen muss:

    gcloud compute instances create target --project $PROJECT_ID \
       --zone us-central1-f --network ssh-example \
       --no-service-account --no-scopes \
       --machine-type f1-micro --metadata=enable-oslogin=TRUE
    
  7. Weisen Sie dem Dienstkonto die IAM-Rolle compute.osAdminLogin zu, damit es SSH-Verbindungen speziell für die Instanz mit dem Namen target herstellen kann. Die Rolle compute.osAdminLogin gewährt Ihrem Dienstkonto auch Superuser-Berechtigungen für die Instanz. Sie können grundsätzlich diese Rolle auf Projektebene zuweisen, sodass sie dann für alle Instanzen in Ihrem Projekt gilt. Weisen Sie hier aber die Rolle nur auf Instanzebene zu, um die Berechtigungen in diesem Beispiel zu begrenzen:

    gcloud compute instances add-iam-policy-binding target \
       --project $PROJECT_ID --zone us-central1-f \
       --member serviceAccount:ssh-account@$PROJECT_ID.iam.gserviceaccount.com \
       --role roles/compute.osAdminLogin
    
  8. Erstellen Sie in us-central1-f eine Instanz namens source. Verbinden Sie die Instanz mit dem Dienstkonto ssh-account. Geben Sie außerdem den Bereich cloud-platform an. Dieser ist Voraussetzung dafür, dass das Dienstkonto API-Anfragen für diese Instanz ausführen kann.

    gcloud compute instances create source \
       --project $PROJECT_ID --zone us-central1-f \
       --service-account ssh-account@$PROJECT_ID.iam.gserviceaccount.com  \
       --scopes https://www.googleapis.com/auth/cloud-platform \
       --network ssh-example --machine-type f1-micro
    

Das Dienstkonto kann jetzt seine eigenen SSH-Schlüsselpaare verwalten und über SSH eine Verbindung zur Instanz target herstellen. Da die Instanz source mit dem Dienstkonto ssh-account verbunden ist, das Sie erstellt haben, kann sich die Python-Clientbibliothek mithilfe der Standardanmeldedaten der Anwendung als Dienstkonto authentifizieren und die dem Dienstkonto zuvor zugewiesenen Rollen nutzen.

Als Nächstes konfigurieren Sie eine Anwendung, die eine SSH-Verbindung von einer Instanz zu einer anderen herstellen kann, und führen diese aus.

SSH-Anwendung auf einer Instanz ausführen

Wenn Anwendungen, die auf Ihren Instanzen ausgeführt werden, SSH-Zugriff auf andere Instanzen benötigen, können Sie die SSH-Schlüsselpaare für Ihr Dienstkonto verwalten und SSH-Befehle programmatisch ausführen. Führen Sie für dieses Beispiel eine Beispielanwendung mit den folgenden Schritten aus:

  1. Stellen Sie mit dem gcloud-Befehlszeilentool eine Verbindung zur Instanz source her:

    gcloud compute ssh source --project $PROJECT_ID --zone us-central1-f
    
  2. Installieren Sie auf der source-Instanz pip sowie die Python-Clientbibliothek:

    my-username@source:~$ sudo apt update && sudo apt install python-pip -y && pip install --upgrade google-api-python-client
    
  3. Laden Sie die Beispielanwendung service_account_ssh.py von GoogleCloudPlatform/python-docs-samples herunter:

    my-username@source:~$ curl -O https://raw.githubusercontent.com/GoogleCloudPlatform/python-docs-samples/master/compute/oslogin/service_account_ssh.py
    
  4. Führen Sie die Beispielanwendung aus, die mit argparse Variablen von der Befehlszeile übernimmt. In diesem Beispiel wird die Anwendung angewiesen, cowsay auf der Instanz target zu installieren und auszuführen. Fügen Sie diesem Befehl Ihre Projekt-ID manuell hinzu:

    my-username@source:~$ python service_account_ssh.py \
        --cmd 'sudo apt install cowsay -y && cowsay "It works!"' \
        --project [PROJECT_ID] --zone us-central1-f --instance target
    
    ⋮
    ___________
      It works!
    -----------
           \   ^__^
            \  (oo)\_______
               (__)\       )\/\
                   ||----w |
                   ||     ||
    
    

Wenn die Anwendung ordnungsgemäß ausgeführt wird, erhalten Sie die Ausgabe von der Anwendung cowsay. Sie können mit dem Flag --cmd jeden gewünschten Befehl festlegen. Alternativ haben Sie die Möglichkeit, eine eigene Anwendung zu schreiben, die service_account_ssh.py importiert und direkt aufruft.

Geben Sie exit ein, um die Verbindung zur Instanz source zu trennen und zu Cloud Shell zurückzukehren.

SSH-Anwendung außerhalb von Compute Engine ausführen

Im vorherigen Beispiel wurde die Anwendung auf einer Compute Engine-Instanz ausgeführt, wobei die Python-Clientbibliothek mit den Standardanmeldedaten der Anwendung das Dienstkonto verwenden konnte, das mit der Instanz source verbunden war. Wenn Sie diese Anwendung außerhalb einer Compute Engine-Instanz ausführen, kann die Clientbibliothek erst einmal nicht auf das Dienstkonto und seine Berechtigungen zugreifen. Dafür müssen Sie manuell den Dienstkontoschlüssel angeben.

  1. Rufen Sie die externe IP-Adresse für die Instanz target ab, die Sie zuvor in dieser Anleitung erstellt haben. Diese Adresse erhalten Sie entweder in der GCP Console auf der Seite Instanzen oder durch Ausführung des folgenden Befehls mit dem gcloud-Befehlszeilentool:

    gcloud compute instances describe target \
       --project $PROJECT_ID --zone us-central1-f
    
  2. Erstellen Sie einen Dienstkontoschlüssel für das Dienstkonto ssh-account, das Sie im vorherigen Beispiel verwendet haben, und laden Sie die Schlüsseldatei auf Ihre lokale Workstation herunter.

  3. Kopieren Sie den Dienstkontoschlüssel auf das System, auf dem Sie dieses Beispiel ausführen möchten.

  4. Öffnen Sie ein Terminal auf dem System, auf dem Sie dieses Beispiel ausführen möchten.

  5. Legen Sie die Umgebungsvariable GOOGLE_APPLICATION_CREDENTIALS so fest, dass sie auf den Pfad verweist, in dem sich die .json-Datei Ihres Dienstkontoschlüssels befindet. Wenn Ihr Schlüssel im Ordner Downloads enthalten ist, können Sie die Umgebungsvariable etwa so festlegen:

    $ export GOOGLE_APPLICATION_CREDENTIALS="/home/user/Downloads/key.json"
    
  6. Installieren Sie die erforderlichen Elemente auf diesem System:

    1. Installieren Sie Python und Pip. Auf Debian-basierten Systemen können Sie diesen Schritt mit apt ausführen:

      $ sudo apt update && sudo apt install python python-pip -y
      
    2. Installieren Sie mit pip die Python-Clientbibliothek:

      $ pip install --upgrade google-api-python-client
      
  7. Laden Sie die Beispielanwendung herunter:

    $ curl -O https://raw.githubusercontent.com/GoogleCloudPlatform/python-docs-samples/master/compute/oslogin/service_account_ssh.py
    
  8. Führen Sie die Beispielanwendung aus. Wenn die Anwendung außerhalb von Compute Engine ausgeführt wird, ist der Metadatenserver nicht verfügbar. Sie müssen deshalb die E-Mail-Adresse des Dienstkontos manuell angeben. Außerdem müssen Sie die externe IP-Adresse für die Instanz target angeben, die Sie zuvor abgerufen haben.

    $ python service_account_ssh.py \
        --cmd 'sudo apt install cowsay -y && cowsay "It works!"' \
        --account ssh-account@[PROJECT_ID].iam.gserviceaccount.com \
        --project [PROJECT_ID] --hostname [TARGET_EXTERNAL_IP]
    
    ⋮
    ___________
      It works!
    -----------
           \   ^__^
            \  (oo)\_______
               (__)\       )\/\
                   ||----w |
                   ||     ||
    
    

Wenn die Anwendung ordnungsgemäß ausgeführt wird, erhalten Sie die Ausgabe von der Anwendung cowsay.

So funktioniert das Beispiel

Die Beispielanwendung service_account_ssh.py führt folgende Schritte aus:

  1. Initialisieren des OS Login API-Objekts.
  2. Wenn die E-Mail-Adresse des Dienstkontos nicht manuell angegeben wurde, ermittelt die Anwendung aus den Instanzmetadaten das mit der Instanz verbundene Dienstkonto. Wenn diese Anwendung außerhalb von Compute Engine ausgeführt wird, ist die manuelle Angabe der E-Mail-Adresse des Dienstkontos erforderlich.
  3. Aufrufen der Methode create_ssh_key(), um einen temporären SSH-Schlüssel für das Dienstkonto auf der Instanz zu erstellen, auf der dieses Beispiel ausgeführt wird. Dem Dienstkonto muss den öffentlichen Schlüssel mit einem von Ihnen festgelegten Ablaufzeitpunkt hinzugefügt werden.
  4. Aufrufen der OS Login API-Methode getLoginProfile(), um den POSIX-Nutzernamen abzurufen, den das Dienstkonto verwendet.
  5. Aufrufen der Methode run_ssh(), um einen Remote-SSH-Befehl als Dienstkonto auszuführen.
  6. Ausgabe der Antwort des Remote-SSH-Befehls.
  7. Entfernen der temporären SSH-Schlüsseldateien.
  8. OS Login entfernt die öffentlichen Schlüsseldateien automatisch, wenn der Ablaufzeitpunkt überschritten ist.
def main(cmd, project, instance=None, zone=None,
         oslogin=None, account=None, hostname=None):
    """Run a command on a remote system."""

    # Create the OS Login API object.
    oslogin = oslogin or googleapiclient.discovery.build('oslogin', 'v1')

    # Identify the service account ID if it is not already provided.
    account = account or requests.get(
        SERVICE_ACCOUNT_METADATA_URL, headers=HEADERS).text
    if not account.startswith('users/'):
        account = 'users/' + account

    # Create a new SSH key pair and associate it with the service account.
    private_key_file = create_ssh_key(oslogin, account)

    # Using the OS Login API, get the POSIX user name from the login profile
    # for the service account.
    profile = oslogin.users().getLoginProfile(name=account).execute()
    username = profile.get('posixAccounts')[0].get('username')

    # Create the hostname of the target instance using the instance name,
    # the zone where the instance is located, and the project that owns the
    # instance.
    hostname = hostname or '{instance}.{zone}.c.{project}.internal'.format(
        instance=instance, zone=zone, project=project)

    # Run a command on the remote instance over SSH.
    result = run_ssh(cmd, private_key_file, username, hostname)

    # Print the command line output from the remote instance.
    # Use .rstrip() rather than end='' for Python 2 compatability.
    for line in result:
        print(line.decode('utf-8').rstrip('\n\r'))

    # Shred the private key and delete the pair.
    execute(['shred', private_key_file])
    execute(['rm', private_key_file])
    execute(['rm', private_key_file + '.pub'])

if __name__ == '__main__':

    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    parser.add_argument(
        '--cmd', default='uname -a',
        help='The command to run on the remote instance.')
    parser.add_argument(
        '--project',
        help='Your Google Cloud project ID.')
    parser.add_argument(
        '--zone',
        help='The zone where the target instance is locted.')
    parser.add_argument(
        '--instance',
        help='The target instance for the ssh command.')
    parser.add_argument(
        '--account',
        help='The service account email.')
    parser.add_argument(
        '--hostname',
        help='The external IP address or hostname for the target instance.')
    args = parser.parse_args()

    main(args.cmd, args.project, instance=args.instance, zone=args.zone,
         account=args.account, hostname=args.hostname)

Die Methode create_ssh_key() generiert ein neues SSH-Schlüsselpaar. Anschließend wird durch diese Methode aus der OS Login API users().importSshPublicKey() aufgerufen, um den öffentlichen Schlüssel mit dem Dienstkonto zu verbinden. Für die Methode users().importSshPublicKey() kann auch ein Ablaufzeitpunkt angegeben werden, der festlegt, wie lange der öffentliche Schlüssel gültig ist.

def create_ssh_key(oslogin, account, private_key_file=None, expire_time=300):
    """Generate an SSH key pair and apply it to the specified account."""
    private_key_file = private_key_file or '/tmp/key-' + str(uuid.uuid4())
    execute(['ssh-keygen', '-t', 'rsa', '-N', '', '-f', private_key_file])

    with open(private_key_file + '.pub', 'r') as original:
        public_key = original.read().strip()

    # Expiration time is in microseconds.
    expiration = int((time.time() + expire_time) * 1000000)

    body = {
        'key': public_key,
        'expirationTimeUsec': expiration,
    }
    oslogin.users().importSshPublicKey(parent=account, body=body).execute()
    return private_key_file

Konfigurieren Sie als Best Practice Ihre Dienstkonten für eine regelmäßige Erstellung eigener neuer Schlüsselpaare. In diesem Beispiel wird vom Dienstkonto für jede SSH-Verbindung, die es herstellt, ein neues Schlüsselpaar erstellt. Sie können dafür einen eigenen Zeitplan festlegen, wenn für Ihre Anwendung andere Anforderungen gelten.

Der Anfragetext für users().importSshPublicKey() enthält einen Wert für expirationTimeUsec, der für OS Login festlegt, wann der Schlüssel abläuft. Jedes Konto kann nur SSH-Schlüsseldaten bis zu 32 KB enthalten. Daher ist es am besten, wenn Sie Ihre öffentlichen SSH-Schlüssel so konfigurieren, dass sie ablaufen, wenn die Vorgänge für Ihr Dienstkonto abgeschlossen sind.

Nach der Konfiguration seiner SSH-Schlüssel kann das Dienstkonto Remotebefehle ausführen. In diesem Beispiel verwendet die Anwendung die Methode run_ssh(), um einen Befehl auf einer Remoteinstanz auszuführen und die Befehlsausgabe zurückzugeben.

def run_ssh(cmd, private_key_file, username, hostname):
    """Run a command on a remote system."""
    ssh_command = [
        'ssh', '-i', private_key_file, '-o', 'StrictHostKeyChecking=no',
        '{username}@{hostname}'.format(username=username, hostname=hostname),
        cmd,
    ]
    ssh = subprocess.Popen(
        ssh_command, shell=False, stdout=subprocess.PIPE,
        stderr=subprocess.PIPE)
    result = ssh.stdout.readlines()
    return result if result else ssh.stderr.readlines()

Bereinigen

So vermeiden Sie, dass Ihrem Google Cloud Platform-Konto die in dieser Anleitung verwendeten Ressourcen in Rechnung gestellt werden:

Verwenden Sie die folgenden Befehle, um die Ressourcen in Ihrer Testumgebung zu bereinigen:

  1. Rufen Sie Cloud Shell in der GCP Console auf:

    Cloud Shell aufrufen

  2. Löschen Sie die Instanz source:

    gcloud compute instances delete source \
       --project $PROJECT_ID --zone us-central1-f
    
  3. Löschen Sie die Instanz target:

    gcloud compute instances delete target \
       --project $PROJECT_ID --zone us-central1-f
    
  4. Löschen Sie das Dienstkonto ssh-account:

    gcloud iam service-accounts delete ssh-account --project $PROJECT_ID
    
  5. Löschen Sie das Netzwerk ssh-example:

    gcloud compute networks delete ssh-example --project $PROJECT_ID
    

Weitere Informationen

  • Laden Sie das vollständige Codebeispiel herunter und sehen Sie es sich an. Es enthält ein kleines Beispiel für die gemeinsame Verwendung dieser Methoden. Sie können es herunterladen, nach Bedarf ändern und ausführen.
  • Erfahren Sie in der Compute Engine API-Referenz und der OS Login API-Referenz mehr darüber, wie Sie mit diesen APIs andere Aufgaben ausführen.
  • Erstellen Sie eigene Anwendungen!
Hat Ihnen diese Seite weitergeholfen? Teilen Sie uns Ihr Feedback mit:

Feedback geben zu...

Compute Engine-Dokumentation