Anwendungen über SSH mit Instanzen verbinden

Über Dienstkonten können Ihre Anwendungen automatisierte Aufgaben ausführen und mit anderen Google Cloud APIs interagieren. Sie können Ihren Anwendungen erlauben, ihre eigenen SSH-Schlüssel zu verwalten und eine Verbindung zu Instanzen herzustellen, um die Systemverwaltungsprozesse zu automatisieren. 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.

Der gesamte Code, den Sie in dieser Anleitung finden, wird auf der GitHub-Seite GoogleCloudPlatform/python-docs-samples gehostet.

Ziele

In dieser Anleitung werden die folgenden Aufgaben behandelt:

  • 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 von Google Cloud verwendet, darunter auch Compute Engine.

Neuen Google Cloud-Nutzern steht möglicherweise eine kostenlose Testversion zur Verfügung.

Hinweise

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

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

  2. Wählen Sie in der Cloud Console auf der Projektauswahlseite ein Cloud-Projekt aus oder erstellen Sie eines.

    Zur Projektauswahl

  3. Die Abrechnung für das Google Cloud-Projekt muss aktiviert sein. So prüfen Sie, ob die Abrechnung für Ihr Projekt aktiviert ist.

  4. Rufen Sie in Ihrem persönlichen Nutzerkonto die folgenden IAM-Rollen für Ihr Projekt ab:
    • compute.instanceAdmin.v1
    • compute.networkAdmin
    • compute.osAdminLogin
    • iam.serviceAccountAdmin
    • iam.serviceAccountKeyAdmin
    • iam.serviceAccountUser
  5. Informationen zum Ausführen von Befehlen des gcloud-Befehlszeilentools mit Cloud Shell.

Dienstkonto und Beispielinstanzen erstellen und konfigurieren

Erstellen Sie ein Dienstkonto und zwei Instanzen für diese Anleitung. Verwenden Sie das Dienstkonto, um Ihrer Anwendung SSH-Zugriff zu gewähren. Diese Anwendung stellt dann über SSH eine Verbindung zur anderen Instanz her.

Führen Sie die folgenden Schritte aus, um die Testumgebung zu konfigurieren:

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

    Cloud Shell öffnen

  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 mit dem Namen ssh-account:

    gcloud iam service-accounts create ssh-account --project $PROJECT_ID \
       --display-name "ssh-account"
    
  4. Erstellen Sie ein Netzwerk mit dem Namen ssh-example für diese Anleitung:

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

    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. Verwenden Sie das Flag --metadata, um OS Login für diese bestimmte Instanz zu aktivieren. Fügen Sie die Flags --no-service-account und --no-scopes ein, 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 den SSH-Zugriff genauer zu steuern. Sie können Ihrem Dienstkonto später zusätzliche Berechtigungen erteilen, wenn Sie feststellen, dass Ihre Anwendungen Zugriff auf andere Ressourcen in Ihrem Projekt benötigen:

    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 Cloud-Clientbibliotheken für Python 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 mithilfe des gcloud-Befehlszeilentools 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 Cloud-Clientbibliotheken für Python mit den Standardanmeldedaten der Anwendung das Dienstkonto verwenden konnten, das mit der Instanz source verbunden ist. 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 Konsole auf der Seite Instanzen oder durch Ausführen 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. Verwenden Sie pip, um die Cloud-Clientbibliotheken für Python zu installieren:

      $ pip install --upgrade google-api-python-client
      
  7. Beispielanwendung herunterladen:

    $ 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.

Funktionsweise der Beispielanwendung

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. Rufen Sie die Methode create_ssh_key() auf, um einen temporären SSH-Schlüssel für das Dienstkonto auf der Instanz zu erstellen, auf der dieses Beispiel ausgeführt wird. Fügen Sie dem Dienstkonto den öffentlichen Schlüssel mit einem von Ihnen festgelegten Ablaufzeitpunkt hinzu.
  4. Rufen Sie die OS Login API-Methode getLoginProfile() auf, um den POSIX-Nutzernamen abzurufen, den das Dienstkonto verwendet.
  5. Rufen Sie die Methode run_ssh() auf, 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:

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

    Cloud Shell öffnen

  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 nach Bedarf herunterladen, ändern und ausführen.
  • In der Referenz zur Compute Engine API und der Referenz zur OS Login API erfahren Sie mehr darüber, wie Sie mit diesen APIs andere Aufgaben ausführen.
  • Erstellen Sie eigene Anwendungen.