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
## community
[![Discord](https://img.shields.io/discord/1250093195870867577)
](https://discord.gg/ABDkUtgzBj)

View File

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

30
main.go
View File

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

View File

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

View File

@@ -15,8 +15,8 @@ import (
// GenerateHostKey는 'keys' 디렉토리를 생성하고, RSA, ECDSA, Ed25519 호스트 개인 키를 생성하여 저장합니다.
// 개인 키는 OpenSSH 형식으로 암호화되어 저장됩니다.
func GenerateHostKey() error {
const keyDir = "./keys"
func GenerateHostKey(rootPath string) error {
keyDir := rootPath + "/keys"
// 1. 키 디렉토리 생성
if err := os.MkdirAll(keyDir, 0700); err != nil {
@@ -103,25 +103,27 @@ func generateAndSaveKey(path string, keyType string) error {
return nil
}
func CheckHostKey() ([]ssh.Signer, error) {
keyFiles := []string{"./keys/id_rsa", "./keys/id_ecdsa", "./keys/id_ed25519"}
func CheckHostKey(rootPath string) ([]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)
tmp := rootPath + "/" + 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)
for _, keyFile := range keyFiles {
keyBytes, err := os.ReadFile(keyFile)
tmp := rootPath + "/" + keyFile
keyBytes, err := os.ReadFile(tmp)
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)
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)