Skip to content

Commit

Permalink
feat: Add authentication support with hashed credentials (#8)
Browse files Browse the repository at this point in the history
* Add authentication support with hashed credentials

This update adds support for authenticating users with hashed credentials. It introduces the `CREDENTIALS` environment variable to accommodate this feature. The `bcrypt` Golang library is used to compare hashed passwords, ensuring secure authentication. Instructions for generating compatible hashed credentials are included in the `README.md`.

* Update test credentials to hashed values

The test credentials in the SOCKS5 and credential tests have been updated from plain text to hashed values. This is a more accurate reflection of how credentials would be stored in a real scenario, thus making the tests more reliable.
  • Loading branch information
ryanbekhen authored Dec 28, 2023
1 parent 567bc2b commit cd2a8d0
Show file tree
Hide file tree
Showing 8 changed files with 48 additions and 8 deletions.
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand All @@ -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

Expand Down
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ 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 (
github.com/davecgh/go-spew v1.1.1 // indirect
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
)
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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=
Expand Down
16 changes: 14 additions & 2 deletions nanoproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/ryanbekhen/nanoproxy/pkg/config"
"github.com/ryanbekhen/nanoproxy/pkg/socks5"
"os"
"strings"
"time"

_ "time/tzdata"
Expand All @@ -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)
Expand Down
1 change: 1 addition & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
}
8 changes: 7 additions & 1 deletion pkg/socks5/credentials.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package socks5

import (
"golang.org/x/crypto/bcrypt"
)

type CredentialStore interface {
Valid(user, password string) bool
}
Expand All @@ -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
}
2 changes: 1 addition & 1 deletion pkg/socks5/credentials_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
2 changes: 1 addition & 1 deletion pkg/socks5/socks5_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{
Expand Down

0 comments on commit cd2a8d0

Please sign in to comment.