mirror of
https://github.com/dalbodeule/sshchat.git
synced 2025-12-08 07:05:44 +09:00
ssh HostKey added
This commit is contained in:
7
go.sum
7
go.sum
@@ -2,14 +2,9 @@ github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFI
|
|||||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
|
||||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||||
golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U=
|
|
||||||
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
|
|
||||||
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
|
||||||
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
|
||||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
|
||||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
|
||||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
|
||||||
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q=
|
||||||
|
golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss=
|
||||||
|
|||||||
32
main.go
32
main.go
@@ -4,13 +4,37 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
|
"sshchat/utils"
|
||||||
|
|
||||||
"github.com/gliderlabs/ssh"
|
"github.com/gliderlabs/ssh"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
ssh.Handle(func(s ssh.Session) {
|
keys, err := utils.CheckHostKey()
|
||||||
io.WriteString(s, "Hello World\n")
|
if err != nil {
|
||||||
})
|
log.Print("Failed to check SSH keys: generate one.\n", err)
|
||||||
|
err = utils.GenerateHostKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
log.Fatal(ssh.ListenAndServe(":2222", nil))
|
keys, err = utils.CheckHostKey()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sessionHandler := func(s ssh.Session) {
|
||||||
|
_, _ = io.WriteString(s, "Hello World\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
s := &ssh.Server{
|
||||||
|
Addr: ":2222",
|
||||||
|
Handler: sessionHandler,
|
||||||
|
}
|
||||||
|
for _, key := range keys {
|
||||||
|
s.AddHostKey(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Fatal(s.ListenAndServe())
|
||||||
}
|
}
|
||||||
|
|||||||
131
utils/hostkey.go
Normal file
131
utils/hostkey.go
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/crypto/ssh"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateHostKey는 'keys' 디렉토리를 생성하고, RSA, ECDSA, Ed25519 호스트 개인 키를 생성하여 저장합니다.
|
||||||
|
// 개인 키는 OpenSSH 형식으로 암호화되어 저장됩니다.
|
||||||
|
func GenerateHostKey() error {
|
||||||
|
const keyDir = "./keys"
|
||||||
|
|
||||||
|
// 1. 키 디렉토리 생성
|
||||||
|
if err := os.MkdirAll(keyDir, 0700); err != nil {
|
||||||
|
return fmt.Errorf("failed to create keys directory: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 사용할 키 파일 경로 및 암호화에 사용할 비밀번호 (실제 사용 시 환경 변수 등 안전한 방법으로 관리해야 함)
|
||||||
|
// OpenSSH 형식에서는 암호화에 비밀번호(passphrase)를 사용합니다.
|
||||||
|
// 여기서는 예시로 "securepassphrase"를 사용하지만, 실제 서버 환경에서는 강력하고 안전하게 보관되는 패스프레이즈를 사용해야 합니다.
|
||||||
|
keysToGenerate := []struct {
|
||||||
|
path string
|
||||||
|
keyType string
|
||||||
|
}{
|
||||||
|
{path: keyDir + "/id_rsa", keyType: "rsa"},
|
||||||
|
{path: keyDir + "/id_ecdsa", keyType: "ecdsa"},
|
||||||
|
{path: keyDir + "/id_ed25519", keyType: "ed25519"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, key := range keysToGenerate {
|
||||||
|
if err := generateAndSaveKey(key.path, key.keyType); err != nil {
|
||||||
|
return fmt.Errorf("failed to generate %s key: %v", key.keyType, err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Successfully generated and encrypted %s key: %s\n", key.keyType, key.path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateAndSaveKey는 지정된 유형의 개인 키를 생성하고 OpenSSH 형식으로 암호화하여 파일에 저장합니다.
|
||||||
|
func generateAndSaveKey(path string, keyType string) error {
|
||||||
|
var privateKey interface{}
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// 2. 개인 키 생성
|
||||||
|
switch keyType {
|
||||||
|
case "rsa":
|
||||||
|
// RSA 키 생성 (4096 비트 권장)
|
||||||
|
privateKey, err = rsa.GenerateKey(rand.Reader, 4096)
|
||||||
|
case "ecdsa":
|
||||||
|
// ECDSA 키 생성 (NIST P-521 곡선 사용 권장)
|
||||||
|
privateKey, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||||
|
case "ed25519":
|
||||||
|
// Ed25519 키 생성
|
||||||
|
_, privateKey, err = ed25519.GenerateKey(rand.Reader)
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unsupported key type: %s", keyType)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("key generation failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. OpenSSH 형식으로 개인 키 마샬링 (암호화 포함)
|
||||||
|
// MarshalPrivateKeyWithPassphrase는 OpenSSH 형식으로 키를 암호화합니다.
|
||||||
|
privatePEM, err := ssh.MarshalPrivateKey(
|
||||||
|
privateKey,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal private key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 개인 키 파일 저장
|
||||||
|
// 0600 권한은 소유자에게만 읽기/쓰기 권한을 부여하여 개인 키를 보호합니다.
|
||||||
|
privateFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open private key file for writing: %v", err)
|
||||||
|
}
|
||||||
|
if err := pem.Encode(privateFile, privatePEM); err != nil {
|
||||||
|
return fmt.Errorf("failed to write private key to file: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 선택 사항: 공개 키도 저장
|
||||||
|
signer, err := ssh.NewSignerFromKey(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create signer for public key: %v", err)
|
||||||
|
}
|
||||||
|
publicPEM := ssh.MarshalAuthorizedKey(signer.PublicKey())
|
||||||
|
pubPath := path + ".pub"
|
||||||
|
if err := os.WriteFile(pubPath, publicPEM, 0644); err != nil {
|
||||||
|
return fmt.Errorf("failed to write public key to file: %v", err)
|
||||||
|
}
|
||||||
|
fmt.Printf("Generated public key: %s\n", pubPath)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckHostKey() ([]ssh.Signer, error) {
|
||||||
|
keyFiles := []string{"./keys/id_rsa", "./keys/id_ecdsa", "./keys/id_ed25519"}
|
||||||
|
|
||||||
|
for _, keyFile := range keyFiles {
|
||||||
|
if _, err := os.Stat(keyFile); os.IsNotExist(err) {
|
||||||
|
return nil, fmt.Errorf("key file %s does not exist", keyFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var keys = make([]ssh.Signer, 0)
|
||||||
|
for _, keyFile := range keyFiles {
|
||||||
|
keyBytes, err := os.ReadFile(keyFile)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to read key file %s: %v", keyFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
signer, err := ssh.ParsePrivateKey(keyBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse private key %s: %v", keyFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
keys = append(keys, signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
return keys, nil
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user