From 5f0b8fcb33a357b02e4349abc839c46b62a38fa4 Mon Sep 17 00:00:00 2001 From: Alexandre Nicolaie Date: Sat, 17 Feb 2024 14:13:59 +0100 Subject: [PATCH 1/6] =?UTF-8?q?=E2=9C=A8=20(yaldap):=20Add=20password=20ha?= =?UTF-8?q?sh=20mecanism?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Storing plain-text password is not a good practice at all. So, in order to improve a bit the security of this project, I add the ability to store hashed password that can be managed transparently during the LDAP BIND request. Currently, yaLDAP only manages argon2, bcrypt, pbkdf2 and scrypt hashed password. --- go.mod | 3 + go.sum | 6 ++ internal/ldap/auth/auth_test.go | 2 +- pkg/ldap/directory/common/types.go | 33 ++++++++-- pkg/ldap/directory/common/types_test.go | 63 ++++++++++++++++++- pkg/ldap/directory/types.go | 2 +- .../directory/yaml/directory_fixtures_test.go | 36 ++++++++--- pkg/ldap/directory/yaml/fixtures/basic.yaml | 2 +- pkg/ldap/mux.go | 7 ++- 9 files changed, 134 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 5c6076a7..0cc7078e 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/prometheus/common v0.47.0 github.com/puzpuzpuz/xsync/v3 v3.0.2 github.com/stretchr/testify v1.8.4 + go.pact.im/x/phcformat v0.0.6 golang.org/x/exp v0.0.0-20240205201215-2c58cdc269a3 golang.org/x/sync v0.3.0 gopkg.in/yaml.v3 v3.0.1 @@ -34,11 +35,13 @@ require ( github.com/prometheus/procfs v0.12.0 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/spf13/cast v1.3.1 // indirect + go.pact.im/x/option v0.0.6 // indirect google.golang.org/protobuf v1.32.0 // indirect ) require ( github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect + github.com/aldy505/phc-crypto v1.2.0 github.com/davecgh/go-spew v1.1.1 // indirect github.com/fatih/color v1.16.0 // indirect github.com/google/uuid v1.6.0 // indirect diff --git a/go.sum b/go.sum index 17fcdee1..31844032 100644 --- a/go.sum +++ b/go.sum @@ -6,6 +6,8 @@ github.com/Masterminds/semver/v3 v3.2.0 h1:3MEsd0SM6jqZojhjLWWeBY+Kcjy9i6MQAeY7Y github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/aldy505/phc-crypto v1.2.0 h1:IbovWYUDUqqqI1wva9U2+sMJ4HABK5L32pScoJw2tc4= +github.com/aldy505/phc-crypto v1.2.0/go.mod h1:tpy0uE4Cqb7I6LUO7BLihCdX56MxvZylsZ1LIzuRw0M= github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA= github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY= @@ -100,6 +102,10 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.pact.im/x/option v0.0.6 h1:8lam/SPqUxr/AhQV8S6pNs+2npB+Fcwgmfwqea6vrik= +go.pact.im/x/option v0.0.6/go.mod h1:mpzKc/UIz4m3X4/47trnsCXP15YoUJsAcSZTiqTkmqA= +go.pact.im/x/phcformat v0.0.6 h1:1Vww+WEVbTTxyr+SLSsHaI3zCYwOe7KmY/8RRZ4RVgU= +go.pact.im/x/phcformat v0.0.6/go.mod h1:q+HHj0v7P1fA3WObC9Ts7YvAm21Jmviw+Kep/DlJjRc= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= diff --git a/internal/ldap/auth/auth_test.go b/internal/ldap/auth/auth_test.go index 53651b20..476a846a 100644 --- a/internal/ldap/auth/auth_test.go +++ b/internal/ldap/auth/auth_test.go @@ -17,7 +17,7 @@ type mockLDAPObject map[string][]string func (o mockLDAPObject) DN() string { return "" } func (o mockLDAPObject) Attributes() ldap.Attributes { return ldap.Attributes(o) } func (o mockLDAPObject) Search(gldap.Scope, string) ([]ldap.Object, error) { return nil, nil } -func (o mockLDAPObject) Bind(string) bool { return false } +func (o mockLDAPObject) Bind(string) (bool, error) { return false, nil } func (o mockLDAPObject) CanSearchOn(string) bool { return true } func TestSessions_NewSession(t *testing.T) { diff --git a/pkg/ldap/directory/common/types.go b/pkg/ldap/directory/common/types.go index b413d611..43ba615b 100644 --- a/pkg/ldap/directory/common/types.go +++ b/pkg/ldap/directory/common/types.go @@ -5,12 +5,17 @@ import ( "sort" "strings" + "github.com/aldy505/phc-crypto/argon2" + "github.com/aldy505/phc-crypto/bcrypt" + "github.com/aldy505/phc-crypto/pbkdf2" + "github.com/aldy505/phc-crypto/scrypt" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/jimlambrt/gldap" "github.com/moznion/go-optional" ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "github.com/xunleii/yaldap/pkg/ldap/filters" + "go.pact.im/x/phcformat" ) type ( @@ -69,13 +74,33 @@ func (obj Object) Search(scope gldap.Scope, filter string) ([]ldap.Object, error } // Bind returns true if the current object is able to authenticate and the password is correct. -// It returns false if the password is wrong and optional.None if it cannot be authenticated. -func (obj Object) Bind(password string) bool { +// It returns false if the password is wrong or not set. +func (obj Object) Bind(password string) (bool, error) { if obj.BindPasswords.IsNone() { - return false + return false, nil + } + bindPassword := obj.BindPasswords.Unwrap() + + phcInfo, ok := phcformat.Parse(bindPassword) + if !ok { + // NOTE: if the password is not a valid PHC string, we assume it's a plain text password + // and we compare it directly with the stored password. + // NOT RECOMMENDED + return bindPassword == password, nil } - return obj.BindPasswords.Unwrap() == password + switch { + case strings.HasPrefix(phcInfo.ID, "argon2"): + return argon2.Verify(bindPassword, password) + case phcInfo.ID == "bcrypt": + return bcrypt.Verify(bindPassword, password) + case strings.HasPrefix(phcInfo.ID, "pbkdf2"): + return pbkdf2.Verify(bindPassword, password) + case phcInfo.ID == "scrypt": + return scrypt.Verify(bindPassword, password) + default: + return false, fmt.Errorf("unsupported PHC algorithm: %s", phcInfo.ID) + } } // CanSearchOn returns true if the current object is able to perform a search on the given DN. diff --git a/pkg/ldap/directory/common/types_test.go b/pkg/ldap/directory/common/types_test.go index 861b8770..1d3a4faf 100644 --- a/pkg/ldap/directory/common/types_test.go +++ b/pkg/ldap/directory/common/types_test.go @@ -1,3 +1,4 @@ +//nolint:goconst package common import ( @@ -218,24 +219,80 @@ func TestObjectBind(t *testing.T) { // Test with correct password password := "password123" expectedResult := true - actualResult := obj.Bind(password) + actualResult, err := obj.Bind(password) + assert.NoError(t, err) assert.Equal(t, expectedResult, actualResult) // Test with incorrect password password = "wrongpassword" expectedResult = false - actualResult = obj.Bind(password) + actualResult, err = obj.Bind(password) + assert.NoError(t, err) assert.Equal(t, expectedResult, actualResult) // Test with no bind passwords obj.BindPasswords = nil password = "password123" expectedResult = false - actualResult = obj.Bind(password) + actualResult, err = obj.Bind(password) + assert.NoError(t, err) assert.Equal(t, expectedResult, actualResult) + + // Test with unsupported PHC algorithm + obj.BindPasswords = []string{"$unknown$v=0$r=0$salt$hash"} + password = "password123" + expectedResult = false + actualResult, err = obj.Bind(password) + + assert.EqualError(t, err, "unsupported PHC algorithm: unknown") + assert.Equal(t, expectedResult, actualResult) +} + +func TestObjectBindWithHashedPassword(t *testing.T) { + tests := []struct { + Name string + HashedPassword string + ExpectedResult bool + }{ + { + Name: "Test with argon2id", + HashedPassword: "$argon2id$v=19$m=65536,t=10,p=1$fc833b1da8729366224df547834badc7914906b7add02b6e709e1ffe4de56ed3$cbfe0dce36ac1d0db8d869b60cb0f264ea44b2e50d1376cfc4ff3412d73e38c7eebc9d07674b9337297edfe2f64877769b09bbb7f80ef974dc7263eb48002b9f", // yaldap_utils hash argon2 password123 + ExpectedResult: true, + }, + { + Name: "Test with bcrypt", + HashedPassword: "$bcrypt$v=0$r=8$$243261243038244e4e78745643644d4f7a33442f6a37534e72345a7075586b772f416d58456a2f6e544856706f784b45656446547570332f41474743", // yaldap_utils hash bcrypt password123 + ExpectedResult: true, + }, + { + Name: "Test with pbkdf2", + HashedPassword: "$pbkdf2sha256$v=0$i=10$67447629899eeb0d6fdb1e4d784a8a78$b467bb3ec3a9c4d46cf0fabb8207f4da139022836168fd29f1258f85f3c4bd6f", // yaldap_utils hash pbkdf2 password123 + ExpectedResult: true, + }, + { + Name: "Test with scrypt", + HashedPassword: "$scrypt$v=0$ln=16,r=8,p=1$fca08f0f4120c4fd2b2d5e36a114d4fb$fd3db2ea0f59d547d0687ef64c7b2afcdc6f98cbd416442f3ccda41f62a66348", // yaldap_utils hash scrypt password123 + ExpectedResult: true, + }, + } + + for _, tt := range tests { + t.Run(tt.Name, func(t *testing.T) { + obj := Object{ + ImplObject: ImplObject{ + BindPasswords: []string{tt.HashedPassword}, + }, + } + + actualResult, err := obj.Bind("password123") + + assert.NoError(t, err) + assert.Equal(t, tt.ExpectedResult, actualResult) + }) + } } func TestObjectCanSearchOn(t *testing.T) { diff --git a/pkg/ldap/directory/types.go b/pkg/ldap/directory/types.go index 83b87240..509a9a11 100644 --- a/pkg/ldap/directory/types.go +++ b/pkg/ldap/directory/types.go @@ -24,7 +24,7 @@ type ( // Bind returns true if the current object is able to authenticate and the password is correct. // It returns false if the password is wrong and optional.None if it cannot be authenticated. - Bind(password string) bool + Bind(password string) (bool, error) // CanSearchOn returns true if the current object is able to perform a search on the given DN. CanSearchOn(dn string) bool } diff --git a/pkg/ldap/directory/yaml/directory_fixtures_test.go b/pkg/ldap/directory/yaml/directory_fixtures_test.go index bd200e76..3c6aa43d 100644 --- a/pkg/ldap/directory/yaml/directory_fixtures_test.go +++ b/pkg/ldap/directory/yaml/directory_fixtures_test.go @@ -150,7 +150,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("alice")) + isBind, err := obj.Bind("alice") + require.NoError(t, err) + assert.True(t, isBind) assert.True(t, obj.CanSearchOn("dc=org")) }) @@ -169,7 +171,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("bob")) + isBind, err := obj.Bind("bob") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("ou=group,dc=example,dc=org")) }) @@ -213,7 +217,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("charlie")) + isBind, err := obj.Bind("charlie") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("ou=group,dc=example,dc=org")) assert.False(t, obj.CanSearchOn("cn=owner,ou=group,dc=example,dc=org")) @@ -258,7 +264,9 @@ func TestFixture_Basic(t *testing.T) { }, obj.Attributes(), ) - assert.False(t, obj.Bind("eve")) + isBind, err := obj.Bind("eve") + require.NoError(t, err) + assert.False(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.False(t, obj.CanSearchOn("ou=group,dc=example,dc=org")) assert.False(t, obj.CanSearchOn("c=fr,dc=example,dc=org")) @@ -374,7 +382,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("bind")) + isBind, err := obj.Bind("bind") + require.NoError(t, err) + assert.True(t, isBind) assert.True(t, obj.CanSearchOn("dc=org")) }) @@ -397,7 +407,9 @@ func TestFixture_Templated(t *testing.T) { obj.Attributes(), ) - assert.True(t, obj.Bind("alice")) + isBind, err := obj.Bind("alice") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=alice,ou=people,dc=example,dc=org")) }) @@ -420,7 +432,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("bob")) + isBind, err := obj.Bind("bob") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=bob,ou=people,dc=example,dc=org")) }) @@ -443,7 +457,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("charlie")) + isBind, err := obj.Bind("charlie") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=charlie,ou=people,dc=example,dc=org")) }) @@ -466,7 +482,9 @@ func TestFixture_Templated(t *testing.T) { }, obj.Attributes(), ) - assert.True(t, obj.Bind("eve")) + isBind, err := obj.Bind("eve") + require.NoError(t, err) + assert.True(t, isBind) assert.False(t, obj.CanSearchOn("dc=org")) assert.True(t, obj.CanSearchOn("cn=eve,ou=people,dc=example,dc=org")) }) diff --git a/pkg/ldap/directory/yaml/fixtures/basic.yaml b/pkg/ldap/directory/yaml/fixtures/basic.yaml index 698182f2..7fd60536 100644 --- a/pkg/ldap/directory/yaml/fixtures/basic.yaml +++ b/pkg/ldap/directory/yaml/fixtures/basic.yaml @@ -56,7 +56,7 @@ dc:org: #dn: dc=org homeDirectory: /home/bob uidNumber: 1001 gidNumber: 1001 - userPassword: !!ldap/bind:password bob + userPassword: !!ldap/bind:password bob c:de: #dn: c=de,dc=example,dc=org objectClass: [top, country] diff --git a/pkg/ldap/mux.go b/pkg/ldap/mux.go index 96252209..2280533a 100644 --- a/pkg/ldap/mux.go +++ b/pkg/ldap/mux.go @@ -69,7 +69,12 @@ func (s *server) bind(w *gldap.ResponseWriter, req *gldap.Request) { return } - if !obj.Bind(string(msg.Password)) { + isBinded, err := obj.Bind(string(msg.Password)) + if err != nil { + log.Error("unable to bind user", slog.String("username", msg.UserName), slog.String("error", err.Error())) + } + + if !isBinded { log.Error("unable to bind user", slog.String("username", msg.UserName)) resp.SetResultCode(gldap.ResultInvalidCredentials) // NOTE: we don't want to give any information about the user existence From ad3355aa89e5e84996c0ab26cd88f18d41f447f5 Mon Sep 17 00:00:00 2001 From: Alexandre Nicolaie Date: Sat, 17 Feb 2024 14:40:29 +0100 Subject: [PATCH 2/6] =?UTF-8?q?=F0=9F=8E=89=20(yaldap):=20Add=20tools=20to?= =?UTF-8?q?=20generate=20hash=20password?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to create password hash for the LDAP configuration, I changed how the CLI works. BREAKING CHANGES: The dockerfile entrypoint has changed and we must provide the `run` command before argument --- .../merge_group,pull_request.go.test.yaml | 2 + Dockerfile | 2 +- cmd/yaldap/main.go | 18 ++- pkg/cmd/server.go | 2 +- pkg/cmd/server_test.go | 9 +- pkg/cmd/tool.go | 146 ++++++++++++++++++ 6 files changed, 171 insertions(+), 8 deletions(-) create mode 100644 pkg/cmd/tool.go diff --git a/.github/workflows/merge_group,pull_request.go.test.yaml b/.github/workflows/merge_group,pull_request.go.test.yaml index 40eacec6..f8308273 100644 --- a/.github/workflows/merge_group,pull_request.go.test.yaml +++ b/.github/workflows/merge_group,pull_request.go.test.yaml @@ -7,6 +7,8 @@ on: - go.mod - go.sum - .github/workflows/pull_request,push.go.test.yaml + push: + branches: [main] permissions: read-all diff --git a/Dockerfile b/Dockerfile index d9cd9ec7..fb35a72c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -47,7 +47,7 @@ ENV PATH=/opt/yaldap:${PATH} USER yaldap WORKDIR /opt/yaldap -ENTRYPOINT [ "yaldap" ] +ENTRYPOINT yaldap EXPOSE 389 HEALTHCHECK --interval=30s --timeout=30s --start-period=5s --retries=3 \ diff --git a/cmd/yaldap/main.go b/cmd/yaldap/main.go index 2009a0d9..8cc9af16 100644 --- a/cmd/yaldap/main.go +++ b/cmd/yaldap/main.go @@ -7,11 +7,23 @@ import ( "github.com/xunleii/yaldap/pkg/cmd" ) +// yaLDAP represents the main application struct, interpretted by kong. +type yaLDAP struct { + cmd.Base `embed:""` + + Server cmd.Server `cmd:"" name:"run" help:"Start the yaLDAP server"` + Tools cmd.Tools `cmd:"" name:"tools" help:"yaLDAP utilities"` +} + func main() { - var server cmd.Server + var yaldap yaLDAP + + yaldap.Server.Base = &yaldap.Base + yaldap.Tools.Base = &yaldap.Base + // Parse command-line arguments using kong. ctx := kong.Parse( - &server, + &yaldap, kong.Name("yaldap"), kong.Description(` yaLDAP is an LDAP server that is backed by different read-only data sources, @@ -23,7 +35,7 @@ such as YAML files. It is intended to be lightweight, secure and easy to configu ) if err := ctx.Run(); err != nil { - server.Logger().Error(err.Error()) + yaldap.Logger().Error(err.Error()) os.Exit(1) } } diff --git a/pkg/cmd/server.go b/pkg/cmd/server.go index a93c8ef3..08a0824b 100644 --- a/pkg/cmd/server.go +++ b/pkg/cmd/server.go @@ -21,7 +21,7 @@ import ( ) type Server struct { - Base `embed:""` + *Base `kong:"-"` ListenAddr string `name:"listen-address" help:"Address to listen on" default:":389"` diff --git a/pkg/cmd/server_test.go b/pkg/cmd/server_test.go index a6b59f5e..26bd22fe 100644 --- a/pkg/cmd/server_test.go +++ b/pkg/cmd/server_test.go @@ -3,7 +3,6 @@ package cmd import ( "crypto/tls" "fmt" - "log/slog" "net" "os" "testing" @@ -18,9 +17,10 @@ import ( func TestServer_Defaults(t *testing.T) { var actual, expected Server + actual.Base = &Base{} + expected.Base = &Base{} + expected.ListenAddr = ":389" - expected.Base.Log.Format = "json" - expected.Base.Log.Level = LogLevel(slog.LevelInfo) expected.Backend.Name = "yaml" expected.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" //nolint:goconst expected.SessionTTL = 168 * time.Hour @@ -34,6 +34,7 @@ func TestServer_Defaults(t *testing.T) { func TestServer_YAML_Simple(t *testing.T) { server := Server{ListenAddr: fmt.Sprintf("localhost:%d", freePort(t))} + server.Base = &Base{} server.Base.Log.Format = "test" server.Backend.Name = "yaml" server.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" @@ -63,6 +64,7 @@ func TestServer_YAML_WithTLS(t *testing.T) { require.NoError(t, err) server := Server{ListenAddr: fmt.Sprintf("localhost:%d", freePort(t))} + server.Base = &Base{} server.Base.Log.Format = "test" server.Backend.Name = "yaml" server.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" @@ -98,6 +100,7 @@ func TestServer_YAML_WithMutualTLS(t *testing.T) { require.NoError(t, err) server := Server{ListenAddr: fmt.Sprintf("localhost:%d", freePort(t))} + server.Base = &Base{} server.Base.Log.Format = "test" server.Backend.Name = "yaml" server.Backend.URL = "file://../ldap/directory/yaml/fixtures/basic.yaml" diff --git a/pkg/cmd/tool.go b/pkg/cmd/tool.go new file mode 100644 index 00000000..ebb95984 --- /dev/null +++ b/pkg/cmd/tool.go @@ -0,0 +1,146 @@ +package cmd + +import ( + allow_fmt "fmt" + + "github.com/aldy505/phc-crypto/argon2" + "github.com/aldy505/phc-crypto/bcrypt" + "github.com/aldy505/phc-crypto/pbkdf2" + "github.com/aldy505/phc-crypto/scrypt" +) + +type ( + Tools struct { + *Base `kong:"-"` + + Hash Hash `cmd:"" help:"Hashing tool"` + } + + Hash struct { + Argon2 Argon2 `cmd:"" name:"argon2" help:"Argon2 hashing algorithm"` + Scrypt Scrypt `cmd:"" name:"scrypt" help:"Scrypt hashing algorithm"` + Bcrypt Bcrypt `cmd:"" name:"bcrypt" help:"Bcrypt hashing algorithm"` + PBKDF2 PBKDF2 `cmd:"" name:"pbkdf2" help:"PBKDF2 hashing algorithm"` + } + + HashCommon struct { + Password string `arg:"" name:"password" help:"Password to hash" required:""` + } + + Argon2 struct { + HashCommon + + Variant string `name:"variant" enum:"i, id" help:"Variant of argon2 to use" default:"id"` + Iterations int `name:"iterations" help:"Number of iterations to use" default:"10"` + Memory int `name:"memory" help:"Memory to use in kibibytes" default:"64"` + Parallelism int `name:"parallelism" help:" Degree of parallelism to use" default:"1"` + } + + Scrypt struct { + HashCommon + + Blocksize int `name:"block-size" help:"Amount of memory to use in kibibytes" default:"8"` + Cost int `name:"cost" help:"CPU/memory cost of the scrypt algorithm" default:"16"` + Parallelism int `name:"parallelism" help:"Degree of parallelism to use" default:"1"` + } + + Bcrypt struct { + HashCommon + + Rounds int `name:"rounds" help:"Number of iterations to use as 2^rounds" default:"8"` + } + + PBKDF2 struct { + HashCommon + + Iterations int `name:"iterations" help:"Number of iterations to use" default:"10"` + Digest string `name:"digest" enum:"md5, sha1, sha256, sha224, sha384, sha512" help:"Digest to use when applying the key derivation function" default:"sha256"` + } +) + +func (h *HashCommon) prepare() { + // Handle password from stdin (- argument) + if h.Password == "-" { + allow_fmt.Scanln(&h.Password) + } +} + +func (a *Argon2) Run() error { + a.HashCommon.prepare() + config := argon2.Config{ + Memory: a.Memory * 1024, + Parallelism: a.Parallelism, + Time: a.Iterations, + } + switch a.Variant { + case "i": + config.Variant = argon2.I + case "id": + config.Variant = argon2.ID + } + + hash, err := argon2.Hash(a.Password, config) + if err != nil { + return err + } + + allow_fmt.Println(hash) + return nil +} + +func (s *Scrypt) Run() error { + s.HashCommon.prepare() + config := scrypt.Config{ + Cost: s.Cost, + Parallelism: s.Parallelism, + Rounds: s.Blocksize, + } + + hash, err := scrypt.Hash(s.Password, config) + if err != nil { + return err + } + + allow_fmt.Println(hash) + return nil +} + +func (b *Bcrypt) Run() error { + b.HashCommon.prepare() + config := bcrypt.Config{Rounds: b.Rounds} + + hash, err := bcrypt.Hash(b.Password, config) + if err != nil { + return err + } + + allow_fmt.Println(hash) + return nil +} + +func (p *PBKDF2) Run() error { + p.HashCommon.prepare() + config := pbkdf2.Config{Rounds: p.Iterations} + switch p.Digest { + case "md5": + config.HashFunc = pbkdf2.MD5 + case "sha1": + config.HashFunc = pbkdf2.SHA1 + case "sha256": + config.HashFunc = pbkdf2.SHA256 + case "sha224": + config.HashFunc = pbkdf2.SHA224 + case "sha384": + config.HashFunc = pbkdf2.SHA384 + case "sha512": + config.HashFunc = pbkdf2.SHA512 + } + + hash, err := pbkdf2.Hash(p.Password, config) + if err != nil { + return err + } + + allow_fmt.Println(hash) + return nil +} From 2b5830df0f2d102320e6e5643ad731a098a8347a Mon Sep 17 00:00:00 2001 From: Alexandre Nicolaie Date: Sat, 17 Feb 2024 14:46:13 +0100 Subject: [PATCH 3/6] =?UTF-8?q?=F0=9F=9A=9A=20(yaldap):=20Update=20all=20r?= =?UTF-8?q?eferences=20to=20xunleii=20owner?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The yaLDAP owner has been changed without updating their references --- Dockerfile | 6 +++--- README.md | 10 +++++----- cmd/yaldap/main.go | 4 ++-- go.mod | 2 +- internal/ldap/auth/auth.go | 2 +- internal/ldap/auth/auth_test.go | 2 +- pkg/cmd/common.go | 2 +- pkg/cmd/common_test.go | 4 ++-- pkg/cmd/server.go | 10 +++++----- pkg/ldap/directory/common/types.go | 4 ++-- pkg/ldap/directory/common/types_test.go | 2 +- pkg/ldap/directory/yaml/directory.go | 4 ++-- pkg/ldap/directory/yaml/directory_fixtures_test.go | 4 ++-- pkg/ldap/directory/yaml/directory_test.go | 4 ++-- pkg/ldap/directory/yaml/parse.go | 4 ++-- pkg/ldap/directory/yaml/parse_tags.go | 2 +- pkg/ldap/directory/yaml/parse_tags_test.go | 2 +- pkg/ldap/directory/yaml/parse_test.go | 4 ++-- pkg/ldap/filters/boolean_resolvers.go | 2 +- pkg/ldap/filters/boolean_resolvers_test.go | 2 +- pkg/ldap/filters/comparator_resolvers.go | 2 +- pkg/ldap/filters/comparator_resolvers_test.go | 2 +- pkg/ldap/filters/match_resolver.go | 2 +- pkg/ldap/filters/match_resolver_test.go | 2 +- pkg/ldap/filters/present_resolver.go | 2 +- pkg/ldap/filters/present_resolver_test.go | 2 +- pkg/ldap/filters/resolvers.go | 2 +- pkg/ldap/filters/resolvers_test.go | 6 +++--- pkg/ldap/filters/substring_resolver.go | 2 +- pkg/ldap/filters/substring_resolver_test.go | 2 +- pkg/ldap/mux.go | 6 +++--- pkg/ldap/mux_test.go | 6 +++--- 32 files changed, 56 insertions(+), 56 deletions(-) diff --git a/Dockerfile b/Dockerfile index fb35a72c..eaebf2ea 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,7 +28,7 @@ RUN set -eux; \ # └───────────────────────────────────────────────────────────────────────────┘ FROM docker.io/library/alpine:3.19.1 -# renovate: datasource=github-tags depName=xunleii/yaldap versioning=semver +# renovate: datasource=github-tags depName=chezmoi-sh/yaldap versioning=semver ARG YALDAP_VERSION="v0.1.2" # renovate: datasource=repology depName=alpine_3_19/ca-certificates versioning=loose @@ -59,10 +59,10 @@ LABEL \ org.opencontainers.image.authors="xunleii " \ org.opencontainers.image.created="01/01/1970T00:00:00.000" \ org.opencontainers.image.description="Your identity, your rules." \ - org.opencontainers.image.documentation="https://github.com/xunleii/yaldap" \ + org.opencontainers.image.documentation="https://github.com/chezmoi-sh/yaldap" \ org.opencontainers.image.licenses="AGPL-3.0" \ org.opencontainers.image.revision="" \ org.opencontainers.image.source="" \ org.opencontainers.image.title="yaldap" \ - org.opencontainers.image.url="https://github.com/xunleii/yaldap" \ + org.opencontainers.image.url="https://github.com/chezmoi-sh/yaldap" \ org.opencontainers.image.version=${YALDAP_VERSION} diff --git a/README.md b/README.md index ca5c29af..9844e1e0 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,11 @@ yaLDAP is an easy-to-use LDAP server using YAML file as directory definition. -![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/xunleii/yaldap) -[![Go](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push.go.test.yaml/badge.svg)](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push.go.test.yaml) -[![CodeQL](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml/badge.svg)](https://github.com/xunleii/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml) -[![codecov](https://codecov.io/gh/xunleii/yaldap/branch/main/graph/badge.svg?token=20J4XPYH1H)](https://codecov.io/gh/xunleii/yaldap) -[![Go Report Card](https://goreportcard.com/badge/github.com/xunleii/yaldap)](https://goreportcard.com/report/github.com/xunleii/yaldap) +![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/chezmoi-sh/yaldap) +[![Test code (Go)](https://github.com/chezmoi-sh/yaldap/actions/workflows/merge_group,pull_request.go.test.yaml/badge.svg?event=push)](https://github.com/chezmoi-sh/yaldap/actions/workflows/merge_group,pull_request.go.test.yaml) +[![CodeQL](https://github.com/chezmoi-sh/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml/badge.svg)](https://github.com/chezmoi-sh/yaldap/actions/workflows/pull_request,push,schedule.codeql.yaml) +[![codecov](https://codecov.io/gh/chezmoi-sh/yaldap/branch/main/graph/badge.svg?token=20J4XPYH1H)](https://codecov.io/gh/chezmoi-sh/yaldap) +[![Go Report Card](https://goreportcard.com/badge/github.com/chezmoi-sh/yaldap)](https://goreportcard.com/report/github.com/chezmoi-sh/yaldap) _Sometimes, we just need a simple LDAP compatible server to store user/group information and other information. For this purpose, many simple LDAP server exists and manage user/group in a better way than yaLDAP. However, no one can diff --git a/cmd/yaldap/main.go b/cmd/yaldap/main.go index 8cc9af16..8559294e 100644 --- a/cmd/yaldap/main.go +++ b/cmd/yaldap/main.go @@ -4,7 +4,7 @@ import ( "os" "github.com/alecthomas/kong" - "github.com/xunleii/yaldap/pkg/cmd" + "github.com/chezmoi-sh/yaldap/pkg/cmd" ) // yaLDAP represents the main application struct, interpretted by kong. @@ -29,7 +29,7 @@ func main() { yaLDAP is an LDAP server that is backed by different read-only data sources, such as YAML files. It is intended to be lightweight, secure and easy to configure. - See https://github.com/xunleii/yaldap for more information. + See https://github.com/chezmoi-sh/yaldap for more information. `), kong.UsageOnError(), ) diff --git a/go.mod b/go.mod index 0cc7078e..efa7e9a6 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/xunleii/yaldap +module github.com/chezmoi-sh/yaldap go 1.21 diff --git a/internal/ldap/auth/auth.go b/internal/ldap/auth/auth.go index d68b0acd..a3838443 100644 --- a/internal/ldap/auth/auth.go +++ b/internal/ldap/auth/auth.go @@ -6,8 +6,8 @@ import ( "sync" "time" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" xsync "github.com/puzpuzpuz/xsync/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) type ( diff --git a/internal/ldap/auth/auth_test.go b/internal/ldap/auth/auth_test.go index 476a846a..4baf6a90 100644 --- a/internal/ldap/auth/auth_test.go +++ b/internal/ldap/auth/auth_test.go @@ -6,10 +6,10 @@ import ( "testing" "time" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" "github.com/jimlambrt/gldap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) type mockLDAPObject map[string][]string diff --git a/pkg/cmd/common.go b/pkg/cmd/common.go index 455a9871..5a6e9907 100644 --- a/pkg/cmd/common.go +++ b/pkg/cmd/common.go @@ -8,8 +8,8 @@ import ( "slices" "github.com/alecthomas/kong" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/prometheus/common/version" - "github.com/xunleii/yaldap/pkg/utils" ) type ( diff --git a/pkg/cmd/common_test.go b/pkg/cmd/common_test.go index 994019ad..9d05d766 100644 --- a/pkg/cmd/common_test.go +++ b/pkg/cmd/common_test.go @@ -6,9 +6,9 @@ import ( "testing" "github.com/alecthomas/kong" + "github.com/chezmoi-sh/yaldap/pkg/cmd" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/stretchr/testify/assert" - "github.com/xunleii/yaldap/pkg/cmd" - "github.com/xunleii/yaldap/pkg/utils" ) func TestLogger_Format(t *testing.T) { diff --git a/pkg/cmd/server.go b/pkg/cmd/server.go index 08a0824b..6cd9929a 100644 --- a/pkg/cmd/server.go +++ b/pkg/cmd/server.go @@ -11,12 +11,12 @@ import ( "time" "github.com/alecthomas/kong" + "github.com/chezmoi-sh/yaldap/internal/ldap/auth" + "github.com/chezmoi-sh/yaldap/pkg/ldap" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + yamldir "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/yaml" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/jimlambrt/gldap" - "github.com/xunleii/yaldap/internal/ldap/auth" - "github.com/xunleii/yaldap/pkg/ldap" - "github.com/xunleii/yaldap/pkg/ldap/directory" - yamldir "github.com/xunleii/yaldap/pkg/ldap/directory/yaml" - "github.com/xunleii/yaldap/pkg/utils" "golang.org/x/sync/errgroup" ) diff --git a/pkg/ldap/directory/common/types.go b/pkg/ldap/directory/common/types.go index 43ba615b..6e7a12d3 100644 --- a/pkg/ldap/directory/common/types.go +++ b/pkg/ldap/directory/common/types.go @@ -9,12 +9,12 @@ import ( "github.com/aldy505/phc-crypto/bcrypt" "github.com/aldy505/phc-crypto/pbkdf2" "github.com/aldy505/phc-crypto/scrypt" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/jimlambrt/gldap" "github.com/moznion/go-optional" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/filters" "go.pact.im/x/phcformat" ) diff --git a/pkg/ldap/directory/common/types_test.go b/pkg/ldap/directory/common/types_test.go index 1d3a4faf..ac381b15 100644 --- a/pkg/ldap/directory/common/types_test.go +++ b/pkg/ldap/directory/common/types_test.go @@ -4,9 +4,9 @@ package common import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" "github.com/jimlambrt/gldap" "github.com/stretchr/testify/assert" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) func TestObjectDN(t *testing.T) { diff --git a/pkg/ldap/directory/yaml/directory.go b/pkg/ldap/directory/yaml/directory.go index 81d70892..cd0632cf 100644 --- a/pkg/ldap/directory/yaml/directory.go +++ b/pkg/ldap/directory/yaml/directory.go @@ -8,8 +8,8 @@ import ( "os" "strings" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/directory_fixtures_test.go b/pkg/ldap/directory/yaml/directory_fixtures_test.go index 3c6aa43d..c37e85cb 100644 --- a/pkg/ldap/directory/yaml/directory_fixtures_test.go +++ b/pkg/ldap/directory/yaml/directory_fixtures_test.go @@ -3,10 +3,10 @@ package yamldir_test import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + yamldir "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/yaml" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - yamldir "github.com/xunleii/yaldap/pkg/ldap/directory/yaml" ) func TestFixture_Basic(t *testing.T) { diff --git a/pkg/ldap/directory/yaml/directory_test.go b/pkg/ldap/directory/yaml/directory_test.go index ac1d752a..bbb35656 100644 --- a/pkg/ldap/directory/yaml/directory_test.go +++ b/pkg/ldap/directory/yaml/directory_test.go @@ -3,9 +3,9 @@ package yamldir import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/stretchr/testify/assert" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" ) func TestNewDirectory_NoFile(t *testing.T) { diff --git a/pkg/ldap/directory/yaml/parse.go b/pkg/ldap/directory/yaml/parse.go index 9ad2fdd5..414e1615 100644 --- a/pkg/ldap/directory/yaml/parse.go +++ b/pkg/ldap/directory/yaml/parse.go @@ -5,8 +5,8 @@ import ( "slices" "strings" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - common "github.com/xunleii/yaldap/pkg/ldap/directory/common" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + common "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/parse_tags.go b/pkg/ldap/directory/yaml/parse_tags.go index 5933fda1..85cdec19 100644 --- a/pkg/ldap/directory/yaml/parse_tags.go +++ b/pkg/ldap/directory/yaml/parse_tags.go @@ -3,8 +3,8 @@ package yamldir import ( "fmt" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/moznion/go-optional" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/parse_tags_test.go b/pkg/ldap/directory/yaml/parse_tags_test.go index c5d05f9b..f19b1016 100644 --- a/pkg/ldap/directory/yaml/parse_tags_test.go +++ b/pkg/ldap/directory/yaml/parse_tags_test.go @@ -3,9 +3,9 @@ package yamldir import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/moznion/go-optional" "github.com/stretchr/testify/assert" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/directory/yaml/parse_test.go b/pkg/ldap/directory/yaml/parse_test.go index 00996576..05772d1b 100644 --- a/pkg/ldap/directory/yaml/parse_test.go +++ b/pkg/ldap/directory/yaml/parse_test.go @@ -3,10 +3,10 @@ package yamldir import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" "gopkg.in/yaml.v3" ) diff --git a/pkg/ldap/filters/boolean_resolvers.go b/pkg/ldap/filters/boolean_resolvers.go index 80b265a2..9a4d0e43 100644 --- a/pkg/ldap/filters/boolean_resolvers.go +++ b/pkg/ldap/filters/boolean_resolvers.go @@ -3,9 +3,9 @@ package filters import ( "fmt" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) //nolint:gochecknoinits diff --git a/pkg/ldap/filters/boolean_resolvers_test.go b/pkg/ldap/filters/boolean_resolvers_test.go index 966f3de7..61ec5421 100644 --- a/pkg/ldap/filters/boolean_resolvers_test.go +++ b/pkg/ldap/filters/boolean_resolvers_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestAndResolver(t *testing.T) { diff --git a/pkg/ldap/filters/comparator_resolvers.go b/pkg/ldap/filters/comparator_resolvers.go index b03266ce..d466fe18 100644 --- a/pkg/ldap/filters/comparator_resolvers.go +++ b/pkg/ldap/filters/comparator_resolvers.go @@ -5,9 +5,9 @@ import ( "strconv" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/filters/comparator_resolvers_test.go b/pkg/ldap/filters/comparator_resolvers_test.go index 3d34d1bd..29570ae2 100644 --- a/pkg/ldap/filters/comparator_resolvers_test.go +++ b/pkg/ldap/filters/comparator_resolvers_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestGreaterOrEqualResolver(t *testing.T) { diff --git a/pkg/ldap/filters/match_resolver.go b/pkg/ldap/filters/match_resolver.go index 1cbfdb90..f3e874e2 100644 --- a/pkg/ldap/filters/match_resolver.go +++ b/pkg/ldap/filters/match_resolver.go @@ -4,10 +4,10 @@ import ( "strconv" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" phonetics "github.com/go-dedup/metaphone" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/filters/match_resolver_test.go b/pkg/ldap/filters/match_resolver_test.go index fb97b634..3d534b2c 100644 --- a/pkg/ldap/filters/match_resolver_test.go +++ b/pkg/ldap/filters/match_resolver_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestApproxResolver(t *testing.T) { diff --git a/pkg/ldap/filters/present_resolver.go b/pkg/ldap/filters/present_resolver.go index 9871a051..62cc7dae 100644 --- a/pkg/ldap/filters/present_resolver.go +++ b/pkg/ldap/filters/present_resolver.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) //nolint:gochecknoinits diff --git a/pkg/ldap/filters/present_resolver_test.go b/pkg/ldap/filters/present_resolver_test.go index 7fbf2cc7..80f226ca 100644 --- a/pkg/ldap/filters/present_resolver_test.go +++ b/pkg/ldap/filters/present_resolver_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestPresentResolver(t *testing.T) { diff --git a/pkg/ldap/filters/resolvers.go b/pkg/ldap/filters/resolvers.go index 34748cd3..e6084548 100644 --- a/pkg/ldap/filters/resolvers.go +++ b/pkg/ldap/filters/resolvers.go @@ -3,9 +3,9 @@ package filters import ( "fmt" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" ) //nolint:gochecknoinits diff --git a/pkg/ldap/filters/resolvers_test.go b/pkg/ldap/filters/resolvers_test.go index 3dc82866..b2525f4a 100644 --- a/pkg/ldap/filters/resolvers_test.go +++ b/pkg/ldap/filters/resolvers_test.go @@ -3,13 +3,13 @@ package filters_test import ( "testing" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/common" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/ldap/directory/common" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) var object = common.Object{ diff --git a/pkg/ldap/filters/substring_resolver.go b/pkg/ldap/filters/substring_resolver.go index 2e9c89c3..97089269 100644 --- a/pkg/ldap/filters/substring_resolver.go +++ b/pkg/ldap/filters/substring_resolver.go @@ -5,9 +5,9 @@ import ( "regexp" "strings" + ldap "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" - ldap "github.com/xunleii/yaldap/pkg/ldap/directory" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/filters/substring_resolver_test.go b/pkg/ldap/filters/substring_resolver_test.go index c2edc75a..c67805e5 100644 --- a/pkg/ldap/filters/substring_resolver_test.go +++ b/pkg/ldap/filters/substring_resolver_test.go @@ -3,11 +3,11 @@ package filters_test import ( "testing" + "github.com/chezmoi-sh/yaldap/pkg/ldap/filters" ber "github.com/go-asn1-ber/asn1-ber" goldap "github.com/go-ldap/ldap/v3" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" - "github.com/xunleii/yaldap/pkg/ldap/filters" ) func TestSubstringResolver(t *testing.T) { diff --git a/pkg/ldap/mux.go b/pkg/ldap/mux.go index 2280533a..36c88d92 100644 --- a/pkg/ldap/mux.go +++ b/pkg/ldap/mux.go @@ -5,10 +5,10 @@ import ( "log/slog" "strings" + "github.com/chezmoi-sh/yaldap/internal/ldap/auth" + "github.com/chezmoi-sh/yaldap/pkg/ldap/directory" + "github.com/chezmoi-sh/yaldap/pkg/utils" "github.com/jimlambrt/gldap" - "github.com/xunleii/yaldap/internal/ldap/auth" - "github.com/xunleii/yaldap/pkg/ldap/directory" - "github.com/xunleii/yaldap/pkg/utils" "golang.org/x/exp/slices" ) diff --git a/pkg/ldap/mux_test.go b/pkg/ldap/mux_test.go index 4cc4e7eb..57718d32 100644 --- a/pkg/ldap/mux_test.go +++ b/pkg/ldap/mux_test.go @@ -7,14 +7,14 @@ import ( "testing" "time" + "github.com/chezmoi-sh/yaldap/internal/ldap/auth" + "github.com/chezmoi-sh/yaldap/pkg/ldap" + yamldir "github.com/chezmoi-sh/yaldap/pkg/ldap/directory/yaml" goldap "github.com/go-ldap/ldap/v3" "github.com/jimlambrt/gldap" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/stretchr/testify/suite" - "github.com/xunleii/yaldap/internal/ldap/auth" - "github.com/xunleii/yaldap/pkg/ldap" - yamldir "github.com/xunleii/yaldap/pkg/ldap/directory/yaml" ) type ( From 4d987bc42c4692603e3388519cf325e762b1818f Mon Sep 17 00:00:00 2001 From: Alexandre Nicolaie Date: Sat, 17 Feb 2024 15:00:46 +0100 Subject: [PATCH 4/6] =?UTF-8?q?=F0=9F=93=9D=20Update=20READMEs=20to=20talk?= =?UTF-8?q?=20about=20the=20hash=20feature?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++++++++++++++++++ pkg/ldap/directory/yaml/README.md | 4 ++++ 2 files changed, 22 insertions(+) diff --git a/README.md b/README.md index 9844e1e0..af51af12 100644 --- a/README.md +++ b/README.md @@ -103,4 +103,22 @@ dc:org: #dn: dc=org userPassword: eve ``` +### Hashed passwords + +In order to avoid storing clear text passwords in the YAML file, yaLDAP supports hashed passwords. +Currently, only `argon2`, `bcrypt`, `pbkdf2` and `scrypt` are supported. + +#### How to hash a password + +```sh +echo -n "" | yaldap tools hash [] - +``` + +For example, to hash a password using `bcrypt` and a cost of 10: + +```sh +$ echo -n "password" | yaldap tools hash bcrypt --rounds 10 - +$bcrypt$v=0$r=10$$243261243130247935525748646434736f52794a2e474f3162714856755331496c616e54384b4d387346494a746c6b3141776e7a6c36736f377a6471 +``` + ## Contribution diff --git a/pkg/ldap/directory/yaml/README.md b/pkg/ldap/directory/yaml/README.md index 36b39251..99ac821e 100644 --- a/pkg/ldap/directory/yaml/README.md +++ b/pkg/ldap/directory/yaml/README.md @@ -48,6 +48,10 @@ dc:org: #dn: dc=org - Can be a scalar (one) or a sequence (several) node - **These values are not stored inside the attribute** +> [!NOTE] +> The `!!ldap/bind:password` handle hashed password during the `bind` operation. +> Currently, only `argon2`, `bcrypt`, `pbkdf2` and `scrypt` are supported. See [README.md](../../../../README.md) for more details. + ### Extension: `go` template To extend the `YAML` syntax _(injecting secrets for example)_, the `YAML` parser will use the `text/template` package to parse the `YAML` file. From 32980d7adac048b94e27c82c7e8ddb07149c3696 Mon Sep 17 00:00:00 2001 From: Alexandre Nicolaie Date: Sat, 17 Feb 2024 16:02:44 +0100 Subject: [PATCH 5/6] =?UTF-8?q?=F0=9F=92=9A=20(workflows/trunk):=20Add=20m?= =?UTF-8?q?issing=20requirements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/merge_group,pull_request.all.lint.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.github/workflows/merge_group,pull_request.all.lint.yaml b/.github/workflows/merge_group,pull_request.all.lint.yaml index 086b816b..9c7bbfb6 100644 --- a/.github/workflows/merge_group,pull_request.all.lint.yaml +++ b/.github/workflows/merge_group,pull_request.all.lint.yaml @@ -14,5 +14,12 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/setup-go@0c52d547c9bc32b1aa3301fd7a9cb496313a4491 # v5.0.0 + with: + go-version: 1.21 + - uses: golangci/golangci-lint-action@3cfe3a4abbb849e10058ce4af15d205b6da42804 # v4.0.0 + with: + version: latest + args: --help # Just force the action to download the latest version of golangci-lint + - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - uses: trunk-io/trunk-action@97ecd21fe6c743bf7a606791584b683a7995c70e # v1.1.9 From 8e34a2823140ea09cc8b58c25b1e6fd280185d2d Mon Sep 17 00:00:00 2001 From: Alexandre Nicolaie Date: Sat, 17 Feb 2024 16:59:29 +0100 Subject: [PATCH 6/6] =?UTF-8?q?=E2=9C=85=20(pkg/cmd):=20Add=20missing=20te?= =?UTF-8?q?sts=20for=20tools=20CLI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pkg/cmd/tool.go | 18 +++++++-- pkg/cmd/tool_test.go | 90 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 pkg/cmd/tool_test.go diff --git a/pkg/cmd/tool.go b/pkg/cmd/tool.go index ebb95984..88780cfb 100644 --- a/pkg/cmd/tool.go +++ b/pkg/cmd/tool.go @@ -2,6 +2,8 @@ package cmd import ( allow_fmt "fmt" + "io" + "os" "github.com/aldy505/phc-crypto/argon2" "github.com/aldy505/phc-crypto/bcrypt" @@ -25,6 +27,9 @@ type ( HashCommon struct { Password string `arg:"" name:"password" help:"Password to hash" required:""` + + // This is a workaround to allow fmt.Println to be mocked in tests. + writer io.Writer `kong:"-"` } Argon2 struct { @@ -63,6 +68,11 @@ func (h *HashCommon) prepare() { if h.Password == "-" { allow_fmt.Scanln(&h.Password) } + + // Set writer to stdout if not set (should be set in tests only) + if h.writer == nil { + h.writer = os.Stdout + } } func (a *Argon2) Run() error { @@ -84,7 +94,7 @@ func (a *Argon2) Run() error { return err } - allow_fmt.Println(hash) + allow_fmt.Fprintln(a.writer, hash) return nil } @@ -101,7 +111,7 @@ func (s *Scrypt) Run() error { return err } - allow_fmt.Println(hash) + allow_fmt.Fprintln(s.writer, hash) return nil } @@ -114,7 +124,7 @@ func (b *Bcrypt) Run() error { return err } - allow_fmt.Println(hash) + allow_fmt.Fprintln(b.writer, hash) return nil } @@ -141,6 +151,6 @@ func (p *PBKDF2) Run() error { return err } - allow_fmt.Println(hash) + allow_fmt.Fprintln(p.writer, hash) return nil } diff --git a/pkg/cmd/tool_test.go b/pkg/cmd/tool_test.go new file mode 100644 index 00000000..ee3627db --- /dev/null +++ b/pkg/cmd/tool_test.go @@ -0,0 +1,90 @@ +package cmd + +import ( + "bytes" + "strings" + "testing" + + "github.com/aldy505/phc-crypto/argon2" + "github.com/aldy505/phc-crypto/bcrypt" + "github.com/aldy505/phc-crypto/pbkdf2" + "github.com/aldy505/phc-crypto/scrypt" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestArgon2(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := Argon2{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Iterations: 1, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := argon2.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} + +func TestScrypt(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := Scrypt{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Cost: 2, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := scrypt.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} + +func TestBcrypt(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := Bcrypt{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Rounds: 1, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := bcrypt.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +} + +func TestPBKDF2(t *testing.T) { + buff := bytes.NewBuffer(nil) + tool := PBKDF2{ + HashCommon: HashCommon{ + Password: "password", + writer: buff, + }, + Iterations: 1, + } + + err := tool.Run() + require.NoError(t, err) + + hash := strings.TrimSpace(buff.String()) + valid, err := pbkdf2.Verify(hash, "password") + require.NoError(t, err) + assert.True(t, valid) +}