いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-95 (go で SSH接続メモ (3))

概要

以下、自分用のメモです。忘れない内にメモメモ。

前回の記事は以下です。

devlights.hatenablog.com

devlights.hatenablog.com

上記のサンプル作っている際に知ったことなんですが、

if err = sess.Run(command); err != nil {
        output.Stderrl("[sess.Run]", err)
        return err
}

として、コマンド実行した後に

defer func() {
        e := sess.Close()
        if e != nil {
            output.Stderrl("[sess.Close]", e)
        }
}()

として、セッションを Close しようとすると、 io.EOF が返ってきます。

何で?ってなって、調べてみると以下の情報を発見。

stackoverflow.com

なるほど。。sess.Run() すると内部で実行後のセッションをCloseしているので、その後に更に Close 呼んでいることになります。

なので、io.EOF が返ってきていたということでした。

このエラーは返ってくるけど、無害なので無視してもオッケイですね。

以下のようにしました。

defer func() {
        // see: https://stackoverflow.com/questions/60879023/getting-eof-as-error-in-golang-ssh-session-close
        //
        // sess.Run() は、コマンドを実行後にセッションをcloseしている.
        // そのため、sess.Run() 後に sess.Close() を呼ぶと io.EOF が返却される.
        // 既にセッションは sess.Run() で適切にクローズされているため、このエラーは無視しても良い
        e := sess.Close()
        if e != nil && e != io.EOF {
            output.Stderrl("[sess.Close]", e)
        }
}()

サンプル

以下のサンプルですが、処理に必要な情報を環境変数から取得するようにしています。

動かす場合は以下を設定しておいてください。

  • $SSH_USER
    • SSH接続するユーザ名
  • $SSH_PASS
    • 秘密鍵のパスワード
  • $SSH_HOST
    • 接続先を xxx.xxx.xxx.xxx:ポート番号 の形式で。

SSH接続したら、cat /etc/os-release | head -n 2を実行して終わります。

package network

import (
    "fmt"
    "io"
    "os"

    "github.com/devlights/gomy/output"
    "golang.org/x/crypto/ssh"
)

// SSHSessionCloseAfterRun -- ssh.Run() を呼んだ後に ssh.Close() を呼ぶと io.EOF が返却されることを確認するサンプルです.
//
// REFERENCES:
//   - https://stackoverflow.com/questions/60879023/getting-eof-as-error-in-golang-ssh-session-close
func SSHSessionCloseAfterRun() error {
    // -----------------------------------------------------------------------------------------
    // ssh.Run() した後に ssh.Close() すると io.EOF が返却される件
    //
    // 理由としては、ssh.Run() を実行すると内部でセッションを Close しているため。
    // 適切に セッション は閉じられているので、このエラーは無視しても良い。
    // -----------------------------------------------------------------------------------------
    var (
        sshUser = os.ExpandEnv("${SSH_USER}")
        sshPass = os.ExpandEnv("${SSH_PASS}")
        sshHost = os.ExpandEnv("${SSH_HOST}")
    )

    if sshUser == "" {
        return fmt.Errorf("[error] %s が設定されていません", "${SSH_USER}")
    }

    if sshPass == "" {
        return fmt.Errorf("[error] %s が設定されていません", "${SSH_PASS}")
    }

    if sshHost == "" {
        return fmt.Errorf("[error] %s が設定されていません", "${SSH_HOST}")
    }

    config := &ssh.ClientConfig{
        User: sshUser,
        Auth: []ssh.AuthMethod{
            ssh.Password(sshPass),
        },
        HostKeyCallback: ssh.InsecureIgnoreHostKey(),
    }

    conn, err := ssh.Dial("tcp", sshHost, config)
    if err != nil {
        output.Stderrl("[ssh.Dial]", err)
        return err
    }

    sess, err := conn.NewSession()
    if err != nil {
        output.Stderrl("[conn.NewSession]", err)
        return err
    }

    defer func() {
        // see: https://stackoverflow.com/questions/60879023/getting-eof-as-error-in-golang-ssh-session-close
        //
        // sess.Run() は、コマンドを実行後にセッションをcloseしている.
        // そのため、sess.Run() 後に sess.Close() を呼ぶと io.EOF が返却される.
        // 既にセッションは sess.Run() で適切にクローズされているため、このエラーは無視しても良い
        e := sess.Close()
        if e != nil && e != io.EOF {
            output.Stderrl("[sess.Close]", e)
        }
    }()

    // リモートサーバでのコマンド実行結果をローカルの標準出力と標準エラーへ流す
    sess.Stdout = os.Stdout
    sess.Stderr = os.Stderr

    if err = sess.Run(command); err != nil {
        output.Stderrl("[sess.Run]", err)
        return err
    }

    return nil
}

実行すると以下のようになります。

$ go run .
ENTER EXAMPLE NAME: ssh_close_after_run
[Name] "ssh_close_after_run"
NAME="Ubuntu"
VERSION="20.04 LTS (Focal Fossa)"


[Elapsed] 305.0718ms

参考情報

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

pkg.go.dev

zaiste.net

medium.com

stackoverflow.com


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com