Как включить id_ed25519-cert.pub в Go SSH-клиент? - PullRequest
0 голосов
/ 01 сентября 2018

Я могу использовать SSH (используя openssh клиент) на моем сервере, используя два файла: ~/.ssh/id_ed25519{,-cert.pub}

debug1: Trying private key: /home/xavier/.ssh/id_ed25519                        
debug1: Authentications that can continue: publickey,keyboard-interactive      
debug1: Offering ED25519-CERT public key: /home/xavier/.ssh/id_ed25519          
debug1: Server accepts key: pkalg ssh-ed25519-cert-v01@openssh.com blen 441    
debug1: sign_and_send_pubkey: no separate private key for certificate "/home/xavier/.ssh/id_ed25519"
debug1: Authentication succeeded (publickey).

Я бы хотел, чтобы go-клиент делал то же самое, но я не знаю, как включить файл id_ed25519-cert.pub в пример на https://godoc.org/golang.org/x/crypto/ssh#example-PublicKeys

key, err := ioutil.ReadFile("/home/xavier/.ssh/id_ed25519")
if err != nil {
    log.Fatalf("unable to read private key: %v", err)
}

// Create the Signer for this private key.
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
    log.Fatalf("unable to parse private key: %v", err)
}

config := &ssh.ClientConfig{
    User: "user",
    Auth: []ssh.AuthMethod{
        // Use the PublicKeys method for remote authentication.
        ssh.PublicKeys(signer),
    },
}

// Connect to the remote server and perform the SSH handshake.
client, err := ssh.Dial("tcp", "host.com:22", config)
if err != nil {
    log.Fatalf("unable to connect: %v", err)
}
defer client.Close()

Отчасти проблема в том, что я не знаю, что это за файл (PublicKey? Certificate?), Отчасти, даже если я знаю, что не понимаю, для чего он нужен в этом обмене.

Я подтвердил, что этот файл необходим: его удаление приводит к сбою интерфейса командной строки ssh.

1 Ответ

0 голосов
/ 02 сентября 2018

Это файл сертификата SSH, используемый для реализации аутентификации пользователя на основе сертификата SSH . Это проверяет подлинность пользователя при входе в систему путем проверки действительной подписи доверенного центра сертификации в иерархии открытых ключей. Этот подход предлагает различные преимущества по сравнению со стандартной аутентификацией на основе ключей SSH (с файлами authorized_keys), например:

  • контроль над выдачей файлов ключей (кто-то, имеющий доступ к главному ключу ЦС, должен подписывать новые сертификаты, а не пользователи, выдающие свои собственные с ssh-keygen)
  • срок действия файла ключа
  • сокращение накладных расходов на администрирование при добавлении или ротации сертификатов, поскольку для проверки сертификата требуется только открытый ключ ЦС; больше нет необходимости заполнять файл authorized_keys для каждого пользователя на каждом хосте
  • упрощенная поддержка отзыва сертификатов при изменении отношений с пользователем

Предполагая, что вы используете встроенную библиотеку golang.org/x/crypto/ssh, вы можете реализовать это следующим образом:

  • чтение и анализ вашего подписанного сертификата открытого ключа вместе с закрытым ключом
  • создание подписанта из закрытого ключа
  • создание лица, подписывающего сертификат, с использованием открытого ключа и соответствующего лица, подписывающего закрытый ключ

Указанный формат сертификатов открытого ключа OpenSSH аналогичен файлу authorized_keys. Функция ParseAuthorizedKeys библиотеки Go будет анализировать этот файл и возвращать соответствующий ключ как экземпляр интерфейса ssh.PublicKey; для сертификатов это конкретно экземпляр структуры ssh.Certificate.

См. Пример кода (примечание: я добавил HostKeyCallback к вашему ClientConfig, чтобы установить соединение с тестовой коробкой - однако он использует проверку InsecureIgnoreHostKey, которую я не рекомендую в производстве!).

package main

import (
    "bytes"
    "io/ioutil"
    "log"

    "golang.org/x/crypto/ssh"
)

func main() {
    key, err := ioutil.ReadFile("/tmp/mycert")
    if err != nil {
        log.Fatalf("unable to read private key: %v", err)
    }

    // Create the Signer for this private key.
    signer, err := ssh.ParsePrivateKey(key)
    if err != nil {
        log.Fatalf("unable to parse private key: %v", err)
    }

    // Load the certificate
    cert, err := ioutil.ReadFile("/tmp/mycert-cert.pub")
    if err != nil {
        log.Fatalf("unable to read certificate file: %v", err)
    }

    pk, _, _, _, err := ssh.ParseAuthorizedKey(cert)
    if err != nil {
        log.Fatalf("unable to parse public key: %v", err)
    }

    certSigner, err := ssh.NewCertSigner(pk.(*ssh.Certificate), signer)
    if err != nil {
        log.Fatalf("failed to create cert signer: %v", err)
    }

    config := &ssh.ClientConfig{
        User: "user",
        Auth: []ssh.AuthMethod{
            // Use the PublicKeys method for remote authentication.
            ssh.PublicKeys(certSigner),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }

    // Connect to the remote server and perform the SSH handshake.
    client, err := ssh.Dial("tcp", "host.com:22", config)
    if err != nil {
        log.Fatalf("unable to connect: %v", err)
    }
    defer client.Close()
}

Если вы хотите написать более общий клиент подключения, который поддерживает сертификаты и несертификаты, вам, очевидно, потребуется дополнительная логика для обработки других типов открытого ключа. Как было написано, я ожидаю, что утверждение типа pk.(*ssh.Certificate) завершится неудачно для файлов открытого ключа без сертификата! (Действительно, для соединений без сертификатов вам, вероятно, не нужно для чтения открытого ключа вообще.)

...