Compare commits

..

2 Commits

Author SHA1 Message Date
dalbodeule
33ffd13663 discord addded 2025-10-14 21:26:50 +09:00
dalbodeule
bc3cd19b24 Dockerfile and some add/fix config 2025-10-14 21:21:12 +09:00
7 changed files with 76 additions and 30 deletions

35
Dockerfile Normal file
View File

@@ -0,0 +1,35 @@
# Stage 1: Build the Go application
FROM golang:1.25-alpine AS builder
WORKDIR /app
# Copy go.mod and go.sum first to leverage Docker cache
COPY go.mod go.sum ./
RUN go mod download
# Copy the rest of the application source code
COPY . .
# Build the Go application
# CGO_ENABLED=0 disables CGO, creating a statically linked binary
# -o /app/main specifies the output path and name of the executable
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /app/main .
# Stage 2: Create a minimal runtime image
FROM alpine:latest
# Install ca-certificates for HTTPS support if needed
RUN apk --no-cache add ca-certificates
WORKDIR /root/
# Copy the built executable from the builder stage
COPY --from=builder /app/main .
# Expose the port your application listens on (e.g., 2222)
EXPOSE 2222
ENV PORT=2222
ENV ROOT_PATH="/app/data"
# Command to run the application when the container starts
CMD ["./main"]

View File

@@ -1,2 +1,6 @@
# sshchat # sshchat
## community
[![Discord](https://img.shields.io/discord/1250093195870867577)
](https://discord.gg/ABDkUtgzBj)

View File

@@ -1,3 +1,4 @@
PORT=2222 PORT=2222
GEOIP_DB=./GeoLite2-City.mmdb GEOIP_DB=GeoLite2-City.mmdb
DB_DSN="postgrtesql://postgres:password@localhost/postgres" DB_DSN="postgrtesql://postgres:password@localhost/postgres"
ROOT_PATH="./"

30
main.go
View File

@@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"log" "log"
"net"
"slices" "slices"
"strings" "strings"
@@ -24,7 +25,12 @@ func sessionHandler(s ssh.Session, geoip *geoip2.Reader, pgDb *bun.DB) {
return return
} }
remote := s.RemoteAddr().String() addr := s.RemoteAddr().String()
host, _, err := net.SplitHostPort(addr)
if err != nil {
host = addr
}
remote := strings.Trim(host, "[]")
username := s.User() username := s.User()
geoStatus := utils.GetIPInfo(remote, geoip) geoStatus := utils.GetIPInfo(remote, geoip)
@@ -43,12 +49,14 @@ func sessionHandler(s ssh.Session, geoip *geoip2.Reader, pgDb *bun.DB) {
_ = s.Close() _ = s.Close()
} }
if geoStatus.Country == "ZZ" && !(strings.HasPrefix(remote, "127") || strings.HasPrefix(remote, "[::1]")) { if geoStatus.Country == "ZZ" {
log.Printf("[sshchat] unknown country blacklisted. %s", username) if strings.HasPrefix(remote, "127") || strings.HasPrefix(remote, "::1") {
_, _ = fmt.Fprintf(s, "[system] Unknown country is blacklisted. %s\n", geoStatus.Country) log.Printf("[sshchat] %s is localhost whitelisted.", username)
_ = s.Close() } else {
} else { log.Printf("[sshchat] unknown country blacklisted. %s", username)
log.Printf("[sshchat] %s is localhost whitelisted.", username) _, _ = fmt.Fprintf(s, "[system] Unknown country is blacklisted. %s\n", geoStatus.Country)
_ = s.Close()
}
} }
client := utils.NewClient(s, ptyReq.Window.Height, ptyReq.Window.Width, username, remote) client := utils.NewClient(s, ptyReq.Window.Height, ptyReq.Window.Width, username, remote)
@@ -62,7 +70,7 @@ func sessionHandler(s ssh.Session, geoip *geoip2.Reader, pgDb *bun.DB) {
} }
func main() { func main() {
geoip, err := utils.GetDB(config.Geoip) geoip, err := utils.GetDB(config.RootPath + "/" + config.Geoip)
if err != nil { if err != nil {
log.Fatalf("Geoip db is error: %v", err) log.Fatalf("Geoip db is error: %v", err)
} }
@@ -74,15 +82,15 @@ func main() {
port := config.Port port := config.Port
keys, err := utils.CheckHostKey() keys, err := utils.CheckHostKey(config.RootPath)
if err != nil { if err != nil {
log.Print("Failed to check SSH keys: generate one.\n", err) log.Print("Failed to check SSH keys: generate one.\n", err)
err = utils.GenerateHostKey() err = utils.GenerateHostKey(config.RootPath)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
keys, err = utils.CheckHostKey() keys, err = utils.CheckHostKey(config.RootPath)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }

View File

@@ -1,7 +1,6 @@
package utils package utils
import ( import (
"log"
"os" "os"
"strings" "strings"
@@ -13,23 +12,23 @@ type Config struct {
Geoip string Geoip string
CountryBlacklist []string CountryBlacklist []string
PgDsn string PgDsn string
RootPath string
} }
func GetConfig() *Config { func GetConfig() *Config {
err := godotenv.Load() _ = godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
port := os.Getenv("PORT") port := os.Getenv("PORT")
geoipDbfile := os.Getenv("GEOIP_DB") geoipDbfile := os.Getenv("GEOIP_DB")
countryBlacklist := os.Getenv("COUNTRY_BLACKLIST") countryBlacklist := os.Getenv("COUNTRY_BLACKLIST")
pgDsn := os.Getenv("DB_DSN") pgDsn := os.Getenv("DB_DSN")
rootPath := os.Getenv("ROOT_PATH")
return &Config{ return &Config{
Port: port, Port: port,
Geoip: geoipDbfile, Geoip: geoipDbfile,
CountryBlacklist: strings.Split(countryBlacklist, ","), CountryBlacklist: strings.Split(countryBlacklist, ","),
PgDsn: pgDsn, PgDsn: pgDsn,
RootPath: rootPath,
} }
} }

View File

@@ -30,10 +30,7 @@ func GetIPInfo(ip string, db *geoip2.Reader) *IpInfo {
country := func(ip net.IP) string { country := func(ip net.IP) string {
country, _ := db.Country(parsedIp) country, _ := db.Country(parsedIp)
println(country.Country.IsoCode)
if country != nil && country.Country.IsoCode != "" { if country != nil && country.Country.IsoCode != "" {
println(country.Country.IsoCode)
return country.Country.IsoCode return country.Country.IsoCode
} else { } else {
return "ZZ" return "ZZ"

View File

@@ -15,8 +15,8 @@ import (
// GenerateHostKey는 'keys' 디렉토리를 생성하고, RSA, ECDSA, Ed25519 호스트 개인 키를 생성하여 저장합니다. // GenerateHostKey는 'keys' 디렉토리를 생성하고, RSA, ECDSA, Ed25519 호스트 개인 키를 생성하여 저장합니다.
// 개인 키는 OpenSSH 형식으로 암호화되어 저장됩니다. // 개인 키는 OpenSSH 형식으로 암호화되어 저장됩니다.
func GenerateHostKey() error { func GenerateHostKey(rootPath string) error {
const keyDir = "./keys" keyDir := rootPath + "/keys"
// 1. 키 디렉토리 생성 // 1. 키 디렉토리 생성
if err := os.MkdirAll(keyDir, 0700); err != nil { if err := os.MkdirAll(keyDir, 0700); err != nil {
@@ -103,25 +103,27 @@ func generateAndSaveKey(path string, keyType string) error {
return nil return nil
} }
func CheckHostKey() ([]ssh.Signer, error) { func CheckHostKey(rootPath string) ([]ssh.Signer, error) {
keyFiles := []string{"./keys/id_rsa", "./keys/id_ecdsa", "./keys/id_ed25519"} keyFiles := []string{"keys/id_rsa", "keys/id_ecdsa", "keys/id_ed25519"}
for _, keyFile := range keyFiles { for _, keyFile := range keyFiles {
if _, err := os.Stat(keyFile); os.IsNotExist(err) { tmp := rootPath + "/" + keyFile
return nil, fmt.Errorf("key file %s does not exist", keyFile) if _, err := os.Stat(tmp); os.IsNotExist(err) {
return nil, fmt.Errorf("key file %s does not exist", tmp)
} }
} }
var keys = make([]ssh.Signer, 0) var keys = make([]ssh.Signer, 0)
for _, keyFile := range keyFiles { for _, keyFile := range keyFiles {
keyBytes, err := os.ReadFile(keyFile) tmp := rootPath + "/" + keyFile
keyBytes, err := os.ReadFile(tmp)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to read key file %s: %v", keyFile, err) return nil, fmt.Errorf("failed to read key file %s: %v", tmp, err)
} }
signer, err := ssh.ParsePrivateKey(keyBytes) signer, err := ssh.ParsePrivateKey(keyBytes)
if err != nil { if err != nil {
return nil, fmt.Errorf("failed to parse private key %s: %v", keyFile, err) return nil, fmt.Errorf("failed to parse private key %s: %v", tmp, err)
} }
keys = append(keys, signer) keys = append(keys, signer)