diff --git a/README.md b/README.md index 8ba69b1..052c49e 100644 --- a/README.md +++ b/README.md @@ -129,8 +129,25 @@ NETWORK=tcp TZ=Asia/Jakarta CLIENT_TIMEOUT=10s DNS_TIMEOUT=10s +CREDENTIALS=username:passwordHash ``` +For the creation of the password hash, you can use the `htpasswd -nB username` command, but you need to install the +`apache2-utils` package first. To install the package, run the following command: + +```shell +sudo apt install apache2-utils +``` + +Then, you can use the `htpasswd` command to generate the password hash: + +```shell +htpasswd -nB username +``` + +This will prompt you to enter the password. After entering the password, the command will output the username and the +password hash. You can then use the output to set the `CREDENTIALS` environment variable. + The following table lists the available configuration options: | Name | Description | Default Value | @@ -140,6 +157,7 @@ The following table lists the available configuration options: | TZ | The timezone to use. | `Local` | | CLIENT_TIMEOUT | The timeout for connecting to the destination server. | `10s` | | DNS_TIMEOUT | The timeout for DNS resolution. | `10s` | +| CREDENTIALS | The credentials to use for authentication. | `""` | ## Logging diff --git a/go.mod b/go.mod index b25cad1..097ac05 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/caarlos0/env/v10 v10.0.0 github.com/rs/zerolog v1.31.0 github.com/stretchr/testify v1.8.4 + golang.org/x/crypto v0.17.0 ) require ( @@ -13,6 +14,6 @@ require ( github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/sys v0.14.0 // indirect + golang.org/x/sys v0.15.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 40acf82..a724049 100644 --- a/go.sum +++ b/go.sum @@ -18,11 +18,13 @@ github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A= github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/nanoproxy.go b/nanoproxy.go index 53f7496..ddaff7c 100644 --- a/nanoproxy.go +++ b/nanoproxy.go @@ -6,6 +6,7 @@ import ( "github.com/ryanbekhen/nanoproxy/pkg/config" "github.com/ryanbekhen/nanoproxy/pkg/socks5" "os" + "strings" "time" _ "time/tzdata" @@ -26,11 +27,22 @@ func main() { socks5Config := socks5.Config{ Logger: &logger, - Resolver: &socks5.DNSResolver{}, - ClientConnTimeout: cfg.ClientTimeout, DestConnTimeout: cfg.DestTimeout, + ClientConnTimeout: cfg.ClientTimeout, + Resolver: &socks5.DNSResolver{}, } + credentials := socks5.StaticCredentialStore{} + for _, cred := range cfg.Credentials { + credArr := strings.Split(cred, ":") + if len(credArr) != 2 { + logger.Fatal().Msgf("Invalid credential: %s", cred) + } + credentials[credArr[0]] = credArr[1] + } + if len(credentials) > 0 { + socks5Config.Credentials = credentials + } sock5Server := socks5.New(&socks5Config) logger.Info().Msgf("Starting socks5 server on %s://%s", cfg.Network, cfg.ADDR) diff --git a/pkg/config/config.go b/pkg/config/config.go index 69596d4..33a3230 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -6,6 +6,7 @@ type Config struct { Timezone string `env:"TZ" envDefault:"Local"` Network string `env:"NETWORK" envDefault:"tcp"` ADDR string `env:"ADDR" envDefault:":1080"` + Credentials []string `env:"CREDENTIALS" envSeparator:","` ClientTimeout time.Duration `env:"CLIENT_TIMEOUT" envDefault:"15s"` DestTimeout time.Duration `env:"DEST_TIMEOUT" envDefault:"15s"` } diff --git a/pkg/socks5/credentials.go b/pkg/socks5/credentials.go index 1f32368..e222e3b 100644 --- a/pkg/socks5/credentials.go +++ b/pkg/socks5/credentials.go @@ -1,5 +1,9 @@ package socks5 +import ( + "golang.org/x/crypto/bcrypt" +) + type CredentialStore interface { Valid(user, password string) bool } @@ -11,5 +15,7 @@ func (s StaticCredentialStore) Valid(user, password string) bool { if !ok { return false } - return password == pass + + err := bcrypt.CompareHashAndPassword([]byte(pass), []byte(password)) + return err == nil } diff --git a/pkg/socks5/credentials_test.go b/pkg/socks5/credentials_test.go index 0c5471f..a8490a2 100644 --- a/pkg/socks5/credentials_test.go +++ b/pkg/socks5/credentials_test.go @@ -8,7 +8,7 @@ import ( func Test_CredentialStore_Valid(t *testing.T) { var s CredentialStore s = StaticCredentialStore{ - "foo": "bar", + "foo": "$2y$05$Xr4Vj6wbsCuf70.Fif2guuX8Ez97GB0VysyCTRL2EMkIikCpY/ugi", } assert.True(t, s.Valid("foo", "bar")) assert.False(t, s.Valid("foo", "baz")) diff --git a/pkg/socks5/socks5_test.go b/pkg/socks5/socks5_test.go index fa18b58..2871f31 100644 --- a/pkg/socks5/socks5_test.go +++ b/pkg/socks5/socks5_test.go @@ -47,7 +47,7 @@ func TestListenAndServe(t *testing.T) { lAddr := l.Addr().(*net.TCPAddr) credentials := StaticCredentialStore{ - "foo": "bar", + "foo": "$2y$05$Xr4Vj6wbsCuf70.Fif2guuX8Ez97GB0VysyCTRL2EMkIikCpY/ugi", // foo:bar } auth := &UserPassAuthenticator{Credentials: credentials} conf := &Config{