diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..c28dce0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,32 @@ +.git/ +.github/ +.gitea/ +.gitignore + +.idea/ +*.swp +*.swo +*~ + +tmp/ +*.log + +.env +.env.* + +certs/ +id_rsa* +*.pub + +README.md +LICENSE.md +*.md + +renovate.json +renovate-config.js + +*_test.go +testdata/ + +app + diff --git a/.gitignore b/.gitignore index bfc3046..dc40a4f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ id_rsa* .env tmp certs +app \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 484427a..83d9b84 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,20 +1,48 @@ FROM golang:1.25.5-alpine AS go_builder +RUN apk update && apk upgrade && \ + apk add --no-cache ca-certificates tzdata git && \ + update-ca-certificates + WORKDIR /src + +COPY go.mod go.sum ./ + +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + go mod download && go mod verify + COPY . . -RUN apk update && apk upgrade && apk add --no-cache ca-certificates tzdata -RUN update-ca-certificates -RUN go build -o ./tmp/main +RUN --mount=type=cache,target=/go/pkg/mod \ + --mount=type=cache,target=/root/.cache/go-build \ + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ + go build -trimpath \ + -ldflags="-w -s" \ + -o /app/tunnel_pls \ + . + +RUN adduser -D -u 10001 -g '' appuser && \ + mkdir -p /app/certs/ssh /app/certs/tls && \ + chown -R appuser:appuser /app FROM scratch -WORKDIR /src - COPY --from=go_builder /usr/share/zoneinfo /usr/share/zoneinfo COPY --from=go_builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ -COPY --from=go_builder /src/tmp/main /src +COPY --from=go_builder /etc/passwd /etc/passwd +COPY --from=go_builder /etc/group /etc/group +COPY --from=go_builder --chown=appuser:appuser /app /app -ENV TZ Asia/Jakarta +WORKDIR /app -ENTRYPOINT ["./main"] \ No newline at end of file +USER appuser + +ENV TZ=Asia/Jakarta + +EXPOSE 2200 8080 8443 + +LABEL org.opencontainers.image.title="Tunnel Please" \ + org.opencontainers.image.description="SSH-based tunnel server" + +ENTRYPOINT ["/app/tunnel_pls"] diff --git a/README.md b/README.md index 7a52b04..c7ddf33 100644 --- a/README.md +++ b/README.md @@ -22,15 +22,13 @@ The following environment variables can be configured in the `.env` file: |----------|-------------|---------|----------| | `DOMAIN` | Domain name for subdomain routing | `localhost` | No | | `PORT` | SSH server port | `2200` | No | +| `HTTP_PORT` | HTTP server port | `8080` | No | +| `HTTPS_PORT` | HTTPS server port | `8443` | No | | `TLS_ENABLED` | Enable TLS/HTTPS | `false` | No | | `TLS_REDIRECT` | Redirect HTTP to HTTPS | `false` | No | -| `CERT_LOC` | Path to TLS certificate | `certs/cert.pem` | No | -| `KEY_LOC` | Path to TLS private key | `certs/privkey.pem` | No | -| `CERT_STORAGE_PATH` | Path for CertMagic certificate storage | `certs/certmagic` | No | | `ACME_EMAIL` | Email for Let's Encrypt registration | `admin@` | No | | `CF_API_TOKEN` | Cloudflare API token for DNS-01 challenge | - | Yes (if auto-cert) | | `ACME_STAGING` | Use Let's Encrypt staging server | `false` | No | -| `SSH_PRIVATE_KEY` | Path to SSH private key (auto-generated if missing) | `certs/id_rsa` | No | | `CORS_LIST` | Comma-separated list of allowed CORS origins | - | No | | `ALLOWED_PORTS` | Port range for TCP tunnels (e.g., 40000-41000) | `40000-41000` | No | | `BUFFER_SIZE` | Buffer size for io.Copy operations in bytes (4096-1048576) | `32768` | No | @@ -43,8 +41,14 @@ The following environment variables can be configured in the `.env` file: The server supports automatic TLS certificate generation and renewal using [CertMagic](https://github.com/caddyserver/certmagic) with Cloudflare DNS-01 challenge. This is required for wildcard certificate support (`*.yourdomain.com`). +**Certificate Storage:** +- TLS certificates are stored in `certs/tls/` (relative to application directory) +- User-provided certificates: `certs/tls/cert.pem` and `certs/tls/privkey.pem` +- CertMagic automatic certificates: `certs/tls/certmagic/` +- SSH keys are stored separately in `certs/ssh/` + **How it works:** -1. If user-provided certificates (`CERT_LOC`, `KEY_LOC`) exist and cover both `DOMAIN` and `*.DOMAIN`, they will be used +1. If user-provided certificates exist at `certs/tls/cert.pem` and `certs/tls/privkey.pem` and cover both `DOMAIN` and `*.DOMAIN`, they will be used 2. If certificates are missing, expired, expiring within 30 days, or don't cover the required domains, CertMagic will automatically obtain new certificates from Let's Encrypt 3. Certificates are automatically renewed before expiration 4. User-provided certificates support hot-reload (changes detected every 30 seconds) @@ -71,7 +75,7 @@ ACME_EMAIL=admin@example.com ### SSH Key Auto-Generation -If the SSH private key specified in `SSH_PRIVATE_KEY` doesn't exist, the application will automatically generate a new 4096-bit RSA key pair at the specified location. This makes it easier to get started without manually creating SSH keys. +The application will automatically generate a new 4096-bit RSA key pair at `certs/ssh/id_rsa` if it doesn't exist. This makes it easier to get started without manually creating SSH keys. SSH keys are stored separately from TLS certificates. ### Memory Optimization diff --git a/go.sum b/go.sum index faee6f1..fb988d1 100644 --- a/go.sum +++ b/go.sum @@ -2,6 +2,10 @@ github.com/caddyserver/certmagic v0.25.0 h1:VMleO/XA48gEWes5l+Fh6tRWo9bHkhwAEhx6 github.com/caddyserver/certmagic v0.25.0/go.mod h1:m9yB7Mud24OQbPHOiipAoyKPn9pKHhpSJxXR1jydBxA= github.com/caddyserver/zerossl v0.1.3 h1:onS+pxp3M8HnHpN5MMbOMyNjmTheJyWRaZYwn+YTAyA= github.com/caddyserver/zerossl v0.1.3/go.mod h1:CxA0acn7oEGO6//4rtrRjYgEoa4MFw/XofZnrYwGqG4= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= @@ -14,44 +18,39 @@ github.com/mholt/acmez/v3 v3.1.3 h1:gUl789rjbJSuM5hYzOFnNaGgWPV1xVfnOs59o0dZEcc= github.com/mholt/acmez/v3 v3.1.3/go.mod h1:L1wOU06KKvq7tswuMDwKdcHeKpFFgkppZy/y0DFxagQ= github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA= github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/zeebo/assert v1.1.0 h1:hU1L1vLTHsnO8x8c9KAR5GmM5QscxHg5RNU5z5qbUWY= +github.com/zeebo/assert v1.1.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0= github.com/zeebo/blake3 v0.2.4 h1:KYQPkhpRtcqh0ssGYcKLG1JYvddkEA8QwCM/yBqhaZI= github.com/zeebo/blake3 v0.2.4/go.mod h1:7eeQ6d2iXWRGF6npfaxl2CU+xy2Fjo2gxeyZGCRUjcE= +github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo= +github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.uber.org/zap/exp v0.3.0 h1:6JYzdifzYkGmTdRR59oYH+Ng7k49H9qVpWwNSsGJj3U= go.uber.org/zap/exp v0.3.0/go.mod h1:5I384qq7XGxYyByIhHm6jg5CHkGY0nsTfbDLgDDlgJQ= -golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= -golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= -golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= -golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk= golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= -golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= -golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= -golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU= -golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= -golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= -golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek= -golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ= golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/internal/port/port.go b/internal/port/port.go index 6512f40..68e185a 100644 --- a/internal/port/port.go +++ b/internal/port/port.go @@ -9,36 +9,47 @@ import ( "tunnel_pls/utils" ) -type PortManager struct { +type Manager interface { + AddPortRange(startPort, endPort uint16) error + GetUnassignedPort() (uint16, bool) + SetPortStatus(port uint16, assigned bool) error + GetPortStatus(port uint16) (bool, bool) +} + +type manager struct { mu sync.RWMutex ports map[uint16]bool sortedPorts []uint16 } -var Manager = PortManager{ +var Default Manager = &manager{ ports: make(map[uint16]bool), sortedPorts: []uint16{}, } func init() { - rawRange := utils.Getenv("ALLOWED_PORTS", "40000-41000") + rawRange := utils.Getenv("ALLOWED_PORTS", "") + if rawRange == "" { + return + } + splitRange := strings.Split(rawRange, "-") if len(splitRange) != 2 { - Manager.AddPortRange(30000, 31000) - } else { - start, err := strconv.ParseUint(splitRange[0], 10, 16) - if err != nil { - start = 30000 - } - end, err := strconv.ParseUint(splitRange[1], 10, 16) - if err != nil { - end = 31000 - } - Manager.AddPortRange(uint16(start), uint16(end)) + return } + + start, err := strconv.ParseUint(splitRange[0], 10, 16) + if err != nil { + return + } + end, err := strconv.ParseUint(splitRange[1], 10, 16) + if err != nil { + return + } + _ = Default.AddPortRange(uint16(start), uint16(end)) } -func (pm *PortManager) AddPortRange(startPort, endPort uint16) error { +func (pm *manager) AddPortRange(startPort, endPort uint16) error { pm.mu.Lock() defer pm.mu.Unlock() @@ -57,7 +68,7 @@ func (pm *PortManager) AddPortRange(startPort, endPort uint16) error { return nil } -func (pm *PortManager) GetUnassignedPort() (uint16, bool) { +func (pm *manager) GetUnassignedPort() (uint16, bool) { pm.mu.Lock() defer pm.mu.Unlock() @@ -70,7 +81,7 @@ func (pm *PortManager) GetUnassignedPort() (uint16, bool) { return 0, false } -func (pm *PortManager) SetPortStatus(port uint16, assigned bool) error { +func (pm *manager) SetPortStatus(port uint16, assigned bool) error { pm.mu.Lock() defer pm.mu.Unlock() @@ -78,7 +89,7 @@ func (pm *PortManager) SetPortStatus(port uint16, assigned bool) error { return nil } -func (pm *PortManager) GetPortStatus(port uint16) (bool, bool) { +func (pm *manager) GetPortStatus(port uint16) (bool, bool) { pm.mu.RLock() defer pm.mu.RUnlock() diff --git a/main.go b/main.go index 65fb3ca..0d90318 100644 --- a/main.go +++ b/main.go @@ -33,7 +33,7 @@ func main() { ServerVersion: "SSH-2.0-TunnlPls-1.0", } - sshKeyPath := utils.Getenv("SSH_PRIVATE_KEY", "certs/id_rsa") + sshKeyPath := "certs/ssh/id_rsa" if err := utils.GenerateSSHKeyIfNotExist(sshKeyPath); err != nil { log.Fatalf("Failed to generate SSH key: %s", err) } diff --git a/server/http.go b/server/http.go index 6c716d1..0ca4e23 100644 --- a/server/http.go +++ b/server/http.go @@ -193,7 +193,8 @@ func (cw *CustomWriter) AddInteraction(interaction Interaction) { var redirectTLS = false func NewHTTPServer() error { - listener, err := net.Listen("tcp", ":80") + httpPort := utils.Getenv("HTTP_PORT", "8080") + listener, err := net.Listen("tcp", ":"+httpPort) if err != nil { return errors.New("Error listening: " + err.Error()) } diff --git a/server/https.go b/server/https.go index 2964d4f..fc08424 100644 --- a/server/https.go +++ b/server/https.go @@ -14,13 +14,14 @@ import ( func NewHTTPSServer() error { domain := utils.Getenv("DOMAIN", "localhost") + httpsPort := utils.Getenv("HTTPS_PORT", "8443") tlsConfig, err := NewTLSConfig(domain) if err != nil { return fmt.Errorf("failed to initialize TLS config: %w", err) } - ln, err := tls.Listen("tcp", ":443", tlsConfig) + ln, err := tls.Listen("tcp", ":"+httpsPort, tlsConfig) if err != nil { return err } diff --git a/server/tls.go b/server/tls.go index e5b3105..1eb6ac8 100644 --- a/server/tls.go +++ b/server/tls.go @@ -37,9 +37,9 @@ func NewTLSConfig(domain string) (*tls.Config, error) { var initErr error tlsManagerOnce.Do(func() { - certPath := utils.Getenv("CERT_LOC", "certs/cert.pem") - keyPath := utils.Getenv("KEY_LOC", "certs/privkey.pem") - storagePath := utils.Getenv("CERT_STORAGE_PATH", "certs/certmagic") + certPath := "certs/tls/cert.pem" + keyPath := "certs/tls/privkey.pem" + storagePath := "certs/tls/certmagic" tm := &TLSManager{ domain: domain, diff --git a/session/handler.go b/session/handler.go index eb61cfd..d536b51 100644 --- a/session/handler.go +++ b/session/handler.go @@ -107,7 +107,7 @@ func (s *SSHSession) HandleTCPIPForward(req *ssh.Request) { return } else { if portToBind == 0 { - unassign, success := portUtil.Manager.GetUnassignedPort() + unassign, success := portUtil.Default.GetUnassignedPort() portToBind = unassign if !success { s.Interaction.SendMessage("No available port\r\n") @@ -122,7 +122,7 @@ func (s *SSHSession) HandleTCPIPForward(req *ssh.Request) { } return } - } else if isUse, isExist := portUtil.Manager.GetPortStatus(portToBind); isExist && isUse { + } else if isUse, isExist := portUtil.Default.GetPortStatus(portToBind); isExist && isUse { s.Interaction.SendMessage(fmt.Sprintf("Port %d is already in use or restricted. Please choose a different port. (03)\r\n", portToBind)) err := req.Reply(false, nil) if err != nil { @@ -135,7 +135,7 @@ func (s *SSHSession) HandleTCPIPForward(req *ssh.Request) { } return } - err := portUtil.Manager.SetPortStatus(portToBind, true) + err := portUtil.Default.SetPortStatus(portToBind, true) if err != nil { log.Println("Failed to set port status:", err) return @@ -208,7 +208,7 @@ func (s *SSHSession) HandleTCPForward(req *ssh.Request, addr string, portToBind listener, err := net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", portToBind)) if err != nil { s.Interaction.SendMessage(fmt.Sprintf("Port %d is already in use or restricted. Please choose a different port.\r\n", portToBind)) - if setErr := portUtil.Manager.SetPortStatus(portToBind, false); setErr != nil { + if setErr := portUtil.Default.SetPortStatus(portToBind, false); setErr != nil { log.Printf("Failed to reset port status: %v", setErr) } err = req.Reply(false, nil) @@ -227,7 +227,7 @@ func (s *SSHSession) HandleTCPForward(req *ssh.Request, addr string, portToBind err = binary.Write(buf, binary.BigEndian, uint32(portToBind)) if err != nil { log.Println("Failed to write port to buffer:", err) - if setErr := portUtil.Manager.SetPortStatus(portToBind, false); setErr != nil { + if setErr := portUtil.Default.SetPortStatus(portToBind, false); setErr != nil { log.Printf("Failed to reset port status: %v", setErr) } err = listener.Close() @@ -242,7 +242,7 @@ func (s *SSHSession) HandleTCPForward(req *ssh.Request, addr string, portToBind err = req.Reply(true, buf.Bytes()) if err != nil { log.Println("Failed to reply to request:", err) - if setErr := portUtil.Manager.SetPortStatus(portToBind, false); setErr != nil { + if setErr := portUtil.Default.SetPortStatus(portToBind, false); setErr != nil { log.Printf("Failed to reset port status: %v", setErr) } err = listener.Close() diff --git a/session/lifecycle/lifecycle.go b/session/lifecycle/lifecycle.go index ecfc206..11106f8 100644 --- a/session/lifecycle/lifecycle.go +++ b/session/lifecycle/lifecycle.go @@ -85,7 +85,7 @@ func (l *Lifecycle) Close() error { } if l.Forwarder.GetTunnelType() == types.TCP { - err := portUtil.Manager.SetPortStatus(l.Forwarder.GetForwardedPort(), false) + err := portUtil.Default.SetPortStatus(l.Forwarder.GetForwardedPort(), false) if err != nil { return err } diff --git a/utils/utils.go b/utils/utils.go index d2087d1..52637be 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -11,22 +11,18 @@ import ( "path/filepath" "strconv" "strings" - "sync" "time" "github.com/joho/godotenv" "golang.org/x/crypto/ssh" ) -type Env struct { - value map[string]string - mu sync.Mutex -} - -var env *Env - func init() { - env = &Env{value: map[string]string{}} + if _, err := os.Stat(".env"); err == nil { + if err := godotenv.Load(".env"); err != nil { + log.Printf("Warning: Failed to load .env file: %s", err) + } + } } func GenerateRandomString(length int) string { @@ -41,24 +37,10 @@ func GenerateRandomString(length int) string { } func Getenv(key, defaultValue string) string { - env.mu.Lock() - defer env.mu.Unlock() - if val, ok := env.value[key]; ok { - return val - } - - if os.Getenv("HOSTNAME") == "" { - err := godotenv.Load(".env") - if err != nil { - log.Fatalf("Error loading .env file: %s", err) - } - } - val := os.Getenv(key) if val == "" { val = defaultValue } - env.value[key] = val return val }