Ldap authentication #369

commit 878646310b
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Jul 7 19:16:27 2023 -0400

    fix unreachable code

    Signed-off-by: Jerome Doucerain <jerome.doucerain@bell.ca>

commit 3a6f13c097
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Jul 7 17:22:12 2023 -0400

    update README.md with features from master upstream branch

    Signed-off-by: Jerome Doucerain <jerome.doucerain@bell.ca>

commit 3f92a77b5d
Merge: 6b87e3f ce307e5
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Jul 7 16:45:51 2023 -0400

    Merge branch 'master' of https://github.com/jpillora/chisel into LDAP-authentication

commit 6b87e3fe49
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:52:56 2022 -0500

    MD again

commit b66e317f5b
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:50:20 2022 -0500

    markdown adjustments

commit 62920afd7d
Merge: 3e05ec5 8f96052
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:34:44 2022 -0500

    Merge branch 'LDAP-authentication' of https://github.com/jdoucerain/chisel into LDAP-authentication

commit 3e05ec57fa
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:14:53 2022 -0500

    markdown adjustments

commit 659c06d3d1
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:10:52 2022 -0500

    markdown adjustments

commit cfb1d00000
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 15:58:32 2022 -0500

    Changes as recommended by J.Pillora

commit 69a8ce0fd5
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Oct 19 12:04:33 2022 -0400

    a bit improved version of the password validation keeping the ability to both rely on local or LDAP authentication

commit 40da15740a
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Tue Oct 18 10:59:02 2022 -0400

    allow combination of both LDAP and local authentication and give ldap-config example

commit 4708fe182b
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 19:33:27 2022 -0400

    ca file debug

commit 92b20ba512
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:51:14 2022 -0400

    add user in debugging log when too many entries

commit ba54e8d759
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:43:24 2022 -0400

    remove comments in ldap.go

commit 505ba024c3
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:39:23 2022 -0400

    new --ldap-config in README.md

commit 111b545fc7
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:33:57 2022 -0400

    ldap authentication

    Signed-off-by: Jerome Doucerain <jerome.doucerain@bell.ca>

commit 8f9605217b
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:14:53 2022 -0500

    markdown adjustments

commit 2d7c977825
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 16:10:52 2022 -0500

    markdown adjustments

commit 7c0e7414cb
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Fri Nov 25 15:58:32 2022 -0500

    Changes as recommended by J.Pillora

commit f7cdb96cb8
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Oct 19 12:04:33 2022 -0400

    a bit improved version of the password validation keeping the ability to both rely on local or LDAP authentication

commit 00836b1e61
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Tue Oct 18 10:59:02 2022 -0400

    allow combination of both LDAP and local authentication and give ldap-config example

commit 6f7bc5db73
Merge: 79c0a67 bea4540
Author: jdoucerain <jerome.doucerain@bell.ca>
Date:   Tue Oct 18 09:53:06 2022 -0400

    Merge branch 'jpillora:master' into LDAP-authentication

commit 79c0a676c5
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 19:33:27 2022 -0400

    ca file debug

commit 5b77520719
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:51:14 2022 -0400

    add user in debugging log when too many entries

commit 6c5081371d
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:43:24 2022 -0400

    remove comments in ldap.go

commit 4bc9fc4766
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:39:23 2022 -0400

    new --ldap-config in README.md

commit 6c1f26968c
Author: Jerome Doucerain <jerome.doucerain@bell.ca>
Date:   Wed Jun 29 18:33:57 2022 -0400

    ldap authentication

    Signed-off-by: Jerome Doucerain <jerome.doucerain@bell.ca>
This commit is contained in:
Jaime Pillora
2023-07-09 21:47:40 +10:00
parent 79a5e78015
commit 465a92cb8e
12 changed files with 323 additions and 51 deletions
+21
View File
@@ -179,6 +179,24 @@ $ chisel server --help
validate client connections. The provided CA certificates will be used
instead of the system roots. This is commonly used to implement mutual-TLS.
--ldap-config, a path to a JSON configuration file, which defines settings used to
connect to a remote LDAP server for authenticating users. once configured, user
passwords will be validated against the configured LDAP server.
here is an example of an ldap-config file: {
"bindDN": "CN=ldapUser,OU=Users,OU=example,DC=EXAMPLE,DC=COM",
"bindPassword": "ldapUserPassword",
"url": "example.com:636",
"baseDN": "OU=Users,OU=example,DC=EXAMPLE,DC=COM",
"filter": "(&(objectClass=person)(objectClass=user))",
"idMapTo": "sAMAccountName",
"ca": "",
"insecure": true
}
note, ldap is only used to validate password, a user or a set of users must still
be whitelisted with the --auth or --authfile flags.
--pid Generate pid file in current working directory
-v, Enable verbose logging
@@ -300,6 +318,9 @@ $ chisel client --help
--hostname, Optionally set the 'Host' header (defaults to the host
found in the server url).
--sni, Override the ServerName when using TLS (defaults to the
hostname).
--tls-ca, An optional root certificate bundle used to verify the
chisel server. Only valid when connecting to the server with
"https" or "wss". By default, the operating system CAs will be used.
+10 -10
View File
@@ -29,7 +29,7 @@ import (
"golang.org/x/sync/errgroup"
)
//Config represents a client configuration
// Config represents a client configuration
type Config struct {
Fingerprint string
Auth string
@@ -45,7 +45,7 @@ type Config struct {
Verbose bool
}
//TLSConfig for a Client
// TLSConfig for a Client
type TLSConfig struct {
SkipVerify bool
CA string
@@ -54,7 +54,7 @@ type TLSConfig struct {
ServerName string
}
//Client represents a client instance
// Client represents a client instance
type Client struct {
*cio.Logger
config *Config
@@ -69,7 +69,7 @@ type Client struct {
tunnel *tunnel.Tunnel
}
//NewClient creates a new client instance
// NewClient creates a new client instance
func NewClient(c *Config) (*Client, error) {
//apply default scheme
if !strings.HasPrefix(c.Server, "http") {
@@ -105,7 +105,7 @@ func NewClient(c *Config) (*Client, error) {
tlsConfig: nil,
}
//set default log level
client.Logger.Info = c.Verbose
client.Logger.Info = true
//configure tls
if u.Scheme == "wss" {
tc := &tls.Config{}
@@ -190,7 +190,7 @@ func NewClient(c *Config) (*Client, error) {
return client, nil
}
//Run starts client and blocks while connected
// Run starts client and blocks while connected
func (c *Client) Run() error {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@@ -221,7 +221,7 @@ func (c *Client) verifyServer(hostname string, remote net.Addr, key ssh.PublicKe
return nil
}
//verifyLegacyFingerprint calculates and compares legacy MD5 fingerprints
// verifyLegacyFingerprint calculates and compares legacy MD5 fingerprints
func (c *Client) verifyLegacyFingerprint(key ssh.PublicKey) error {
bytes := md5.Sum(key.Marshal())
strbytes := make([]string, len(bytes))
@@ -236,7 +236,7 @@ func (c *Client) verifyLegacyFingerprint(key ssh.PublicKey) error {
return nil
}
//Start client and does not block
// Start client and does not block
func (c *Client) Start(ctx context.Context) error {
ctx, cancel := context.WithCancel(ctx)
c.stop = cancel
@@ -293,12 +293,12 @@ func (c *Client) setProxy(u *url.URL, d *websocket.Dialer) error {
return nil
}
//Wait blocks while the client is running.
// Wait blocks while the client is running.
func (c *Client) Wait() error {
return c.eg.Wait()
}
//Close manually stops the client
// Close manually stops the client
func (c *Client) Close() error {
if c.stop != nil {
c.stop()
+3
View File
@@ -5,6 +5,7 @@ go 1.20
require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/fsnotify/fsnotify v1.6.0
github.com/go-ldap/ldap/v3 v3.4.5
github.com/gorilla/websocket v1.5.0
github.com/jpillora/backoff v1.0.0
github.com/jpillora/requestlog v1.0.0
@@ -15,7 +16,9 @@ require (
)
require (
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.4 // indirect
github.com/jpillora/ansi v1.0.3 // indirect
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
golang.org/x/sys v0.10.0 // indirect
+57
View File
@@ -1,9 +1,20 @@
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3UuJRqlA3JxYxBZEqCeOmATOvrbT4p9RA=
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 h1:axBiC50cNZOs7ygH5BgQp4N+aYrZ2DNpWZ1KG3VOSOM=
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2/go.mod h1:jnzFpU88PccN/tPPhCpnNU8mZphvKxYM9lLNkd8e+os=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/go-asn1-ber/asn1-ber v1.5.4 h1:vXT6d/FNDiELJnLb6hGNa309LMsrCoYFvpwHDF0+Y1A=
github.com/go-asn1-ber/asn1-ber v1.5.4/go.mod h1:hEBeB/ic+5LoWskz+yKT7vGhhPYkProFKoKdwZRWMe0=
github.com/go-ldap/ldap/v3 v3.4.5 h1:ekEKmaDrpvR2yf5Nc/DClsGG9lAmdDixe44mLzlW5r8=
github.com/go-ldap/ldap/v3 v3.4.5/go.mod h1:bMGIq3AGbytbaMwf8wdv5Phdxz0FWHTIYMSzyrYgnQs=
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/jpillora/ansi v1.0.3 h1:nn4Jzti0EmRfDxm7JtEs5LzCbNwd5sv+0aE+LdS9/ZQ=
@@ -14,17 +25,63 @@ github.com/jpillora/requestlog v1.0.0 h1:bg++eJ74T7DYL3DlIpiwknrtfdUA9oP/M4fL+Pp
github.com/jpillora/requestlog v1.0.0/go.mod h1:HTWQb7QfDc2jtHnWe2XEIEeJB7gJPnVdpNn52HXPvy8=
github.com/jpillora/sizestr v1.0.0 h1:4tr0FLxs1Mtq3TnsLDV+GYUWG7Q26a6s+tV5Zfw2ygw=
github.com/jpillora/sizestr v1.0.0/go.mod h1:bUhLv4ctkknatr6gR42qPxirmd5+ds1u7mzD+MZ33f0=
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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc=
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
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.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.12.0 h1:cfawfvKITfUsFCeJIHJrbSxpeu/E81khclypR0GVT50=
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+30
View File
@@ -16,6 +16,7 @@ import (
chserver "github.com/jpillora/chisel/server"
chshare "github.com/jpillora/chisel/share"
"github.com/jpillora/chisel/share/cos"
"github.com/jpillora/chisel/share/settings"
)
var help = `
@@ -162,6 +163,24 @@ var serverHelp = `
holding multiple PEM encode CA certificate bundle files, which is used to
validate client connections. The provided CA certificates will be used
instead of the system roots. This is commonly used to implement mutual-TLS.
--ldap-config, a path to a JSON configuration file, which defines settings used to
connect to a remote LDAP server for authenticating users. once configured, user
passwords will be validated against the configured LDAP server.
here is an example of an ldap-config file: {
"bindDN": "CN=ldapUser,OU=Users,OU=example,DC=EXAMPLE,DC=COM",
"bindPassword": "ldapUserPassword",
"url": "example.com:636",
"baseDN": "OU=Users,OU=example,DC=EXAMPLE,DC=COM",
"filter": "(&(objectClass=person)(objectClass=user))",
"idMapTo": "sAMAccountName",
"ca": "",
"insecure": true
}
note, ldap is only used to validate password, a user or a set of users must still
be whitelisted with the --auth or --authfile flags, TODO
` + commonHelp
func server(args []string) {
@@ -181,6 +200,7 @@ func server(args []string) {
flags.StringVar(&config.TLS.Cert, "tls-cert", "", "")
flags.Var(multiFlag{&config.TLS.Domains}, "tls-domain", "")
flags.StringVar(&config.TLS.CA, "tls-ca", "", "")
ldapConfigPath := flags.String("ldap-config", "", "")
host := flags.String("host", "", "")
p := flags.String("p", "", "")
@@ -212,6 +232,16 @@ func server(args []string) {
if config.KeySeed == "" {
config.KeySeed = os.Getenv("CHISEL_KEY")
}
if p := ldapConfigPath; p != nil && *p != "" {
l, err := settings.LDAPParseConfig(*p)
if err != nil {
log.Fatal(err)
}
config.LDAPConfig = l
}
s, err := chserver.NewServer(config)
if err != nil {
log.Fatal(err)
+38 -16
View File
@@ -3,6 +3,7 @@ package chserver
import (
"context"
"errors"
"fmt"
"log"
"net/http"
"net/http/httputil"
@@ -22,14 +23,15 @@ import (
// Config is the configuration for the chisel service
type Config struct {
KeySeed string
AuthFile string
Auth string
Proxy string
Socks5 bool
Reverse bool
KeepAlive time.Duration
TLS TLSConfig
KeySeed string
AuthFile string
Auth string
Proxy string
Socks5 bool
Reverse bool
KeepAlive time.Duration
TLS TLSConfig
LDAPConfig *settings.LDAPConfig
}
// Server respresent a chisel service
@@ -60,7 +62,7 @@ func NewServer(c *Config) (*Server, error) {
sessions: settings.NewUsers(),
}
server.Info = true
server.users = settings.NewUserIndex(server.Logger)
server.users = settings.NewUserIndex(server.Logger, c.LDAPConfig != nil)
if c.AuthFile != "" {
if err := server.users.LoadUsers(c.AuthFile); err != nil {
return nil, err
@@ -72,6 +74,9 @@ func NewServer(c *Config) (*Server, error) {
if u.Name != "" {
server.users.AddUser(u)
}
if c.LDAPConfig != nil && u.Pass != "" {
log.Printf("warning: LDAP auth enabled, user '%s' password will not be used", u.Name)
}
}
//generate private key (optionally using seed)
key, err := ccrypto.GenerateKey(c.KeySeed)
@@ -177,14 +182,31 @@ func (s *Server) authUser(c ssh.ConnMetadata, password []byte) (*ssh.Permissions
// check the user exists and has matching password
n := c.User()
user, found := s.users.Get(n)
if !found || user.Pass != string(password) {
s.Debugf("Login failed for user: %s", n)
return nil, errors.New("Invalid authentication for username: %s")
if !found {
return nil, errors.New("user not found")
}
// insert the user session map
// TODO this should probably have a lock on it given the map isn't thread-safe
s.sessions.Set(string(c.SessionID()), user)
return nil, nil
if string(password) == "" {
return nil, errors.New("password attempt not set")
}
if l := s.config.LDAPConfig; l != nil {
// ldap config defined, must use ldap auth
if err := settings.LDAPAuthUser(user, password, l); err != nil {
return nil, fmt.Errorf("user ldap auth failed: %w", err)
}
// ldap authentication successful
// insert the user session map
s.sessions.Set(string(c.SessionID()), user)
return nil, nil
} else if user.Pass == string(password) {
// local authentication successful
// insert the user session map
s.sessions.Set(string(c.SessionID()), user)
return nil, nil
}
if s.config.LDAPConfig.URL != "" {
}
return nil, errors.New("user auth failed")
}
// AddUser adds a new user into the server user index
+1
View File
@@ -1,3 +1,4 @@
//go:build pprof
// +build pprof
package cos
+2 -1
View File
@@ -1,4 +1,5 @@
//+build !windows
//go:build !windows
// +build !windows
package cos
+2 -1
View File
@@ -1,4 +1,5 @@
//+build windows
//go:build windows
// +build windows
package cos
+130
View File
@@ -0,0 +1,130 @@
package settings
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"log"
"os"
"github.com/go-ldap/ldap/v3"
)
// LDAPConfig enables LDAP auth
type LDAPConfig struct {
BindDN string `json:"bindDN"`
BindPassword string `json:"bindPassword"`
URL string `json:"url"`
BaseDN string `json:"baseDN"`
Filter string `json:"filter"`
IDMapTo string `json:"idMapTo"`
CA string `json:"ca"`
Insecure bool `json:"insecure"`
}
// parse the LDAP config file
func LDAPParseConfig(path string) (*LDAPConfig, error) {
file, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("LDAP config file error")
}
config := &LDAPConfig{}
err = json.Unmarshal([]byte(file), config)
if err != nil {
return nil, fmt.Errorf("error occured during unmarshaling ldap config file")
}
return config, nil
}
// authenticate a user using ldap credentials
func LDAPAuthUser(user *User, password []byte, config *LDAPConfig) error {
log.Printf("User %s to be authenticated in LDAP", user.Name)
l, err := connectTLS(config)
if err != nil {
log.Printf("Error occured during TLS connection to %s", config.URL)
return fmt.Errorf("error occured during TLS connection to %s", config.URL)
}
defer l.Close()
// Normal Bind and Search
result, err := bindAndSearch(l, config, user)
if err != nil {
log.Printf("User %s not found in LDAP", user.Name)
return fmt.Errorf("User %s not found in LDAP", user.Name)
}
userdn := result.Entries[0].DN
log.Printf("DN:%s", userdn)
if len(result.Entries) != 1 {
log.Printf("too many entries returned for user %s", user.Name)
return fmt.Errorf("too many entries returned")
}
// Bind as the user to verify their password
err = l.Bind(userdn, string(password[:]))
if err != nil {
return fmt.Errorf("bad password for user %s", user.Name)
}
return nil
}
// LDAP Connection with TLS
func connectTLS(ldapconfig *LDAPConfig) (*ldap.Conn, error) {
var tlsConf *tls.Config
if ldapconfig.Insecure {
tlsConf = &tls.Config{InsecureSkipVerify: true}
}
if ldapconfig.CA != "" {
log.Printf("CA file %s", ldapconfig.CA)
certpool := x509.NewCertPool()
CAfile, err := os.ReadFile(ldapconfig.CA)
if err != nil {
log.Printf("LDAP CA file error")
return nil, fmt.Errorf("LDAP CA file error")
}
certpool.AppendCertsFromPEM([]byte(CAfile))
tlsConf = &tls.Config{RootCAs: certpool}
log.Printf("CA file %s loaded", ldapconfig.CA)
}
l, err := ldap.DialTLS("tcp", ldapconfig.URL, tlsConf)
if err != nil {
log.Printf("TLS error: %s", err)
return nil, err
}
return l, nil
}
// Normal Bind and Search
func bindAndSearch(l *ldap.Conn, config *LDAPConfig, user *User) (*ldap.SearchResult, error) {
l.Bind(config.BindDN, config.BindPassword)
var filter string
if config.Filter != "" {
filter = fmt.Sprintf("(&(%s)(%s=%s))", config.Filter, config.IDMapTo, user.Name)
} else {
filter = fmt.Sprintf("(%s=%s)", config.IDMapTo, user.Name)
}
log.Printf("filter %s", filter)
searchReq := ldap.NewSearchRequest(
config.BaseDN,
ldap.ScopeWholeSubtree,
ldap.NeverDerefAliases,
0,
0,
false,
filter,
[]string{"dn"},
nil,
)
result, err := l.Search(searchReq)
if err != nil {
log.Printf("Search Error: %s", err)
return nil, fmt.Errorf("search Error: %s", err)
}
if len(result.Entries) == 0 {
return nil, fmt.Errorf("couldn't fetch search entries")
}
return result, nil
}
+14 -17
View File
@@ -48,7 +48,7 @@ func DecodeRemote(s string) (*Remote, error) {
}
parts := regexp.MustCompile(`(\[[^\[\]]+\]|[^\[\]:]+):?`).FindAllStringSubmatch(s, -1)
if len(parts) <= 0 || len(parts) >= 5 {
return nil, errors.New("Invalid remote")
return nil, errors.New("invalid remote")
}
r := &Remote{Reverse: reverse}
//parse from back to front, to set 'remote' fields first,
@@ -82,10 +82,10 @@ func DecodeRemote(s string) (*Remote, error) {
continue
}
if !r.Socks && (r.RemotePort == "" && r.LocalPort == "") {
return nil, errors.New("Missing ports")
return nil, errors.New("missing ports")
}
if !isHost(p) {
return nil, errors.New("Invalid host")
return nil, errors.New("invalid host")
}
if !r.Socks && r.RemoteHost == "" {
r.RemoteHost = p
@@ -145,15 +145,12 @@ func isPort(s string) bool {
func isHost(s string) bool {
_, err := url.Parse("//" + s)
if err != nil {
return false
}
return true
return err == nil
}
var l4Proto = regexp.MustCompile(`(?i)\/(tcp|udp)$`)
//L4Proto extacts the layer-4 protocol from the given string
// L4Proto extacts the layer-4 protocol from the given string
func L4Proto(s string) (head, proto string) {
if l4Proto.MatchString(s) {
l := len(s)
@@ -162,7 +159,7 @@ func L4Proto(s string) (head, proto string) {
return s, ""
}
//implement Stringer
// implement Stringer
func (r Remote) String() string {
sb := strings.Builder{}
if r.Reverse {
@@ -177,7 +174,7 @@ func (r Remote) String() string {
return sb.String()
}
//Encode remote to a string
// Encode remote to a string
func (r Remote) Encode() string {
if r.LocalPort == "" {
r.LocalPort = r.RemotePort
@@ -193,7 +190,7 @@ func (r Remote) Encode() string {
return local + ":" + remote
}
//Local is the decodable local portion
// Local is the decodable local portion
func (r Remote) Local() string {
if r.Stdio {
return "stdio"
@@ -204,7 +201,7 @@ func (r Remote) Local() string {
return r.LocalHost + ":" + r.LocalPort
}
//Remote is the decodable remote portion
// Remote is the decodable remote portion
func (r Remote) Remote() string {
if r.Socks {
return "socks"
@@ -215,8 +212,8 @@ func (r Remote) Remote() string {
return r.RemoteHost + ":" + r.RemotePort
}
//UserAddr is checked when checking if a
//user has access to a given remote
// UserAddr is checked when checking if a
// user has access to a given remote
func (r Remote) UserAddr() string {
if r.Reverse {
return "R:" + r.LocalHost + ":" + r.LocalPort
@@ -224,7 +221,7 @@ func (r Remote) UserAddr() string {
return r.RemoteHost + ":" + r.RemotePort
}
//CanListen checks if the port can be listened on
// CanListen checks if the port can be listened on
func (r Remote) CanListen() bool {
//valid protocols
switch r.LocalProto {
@@ -253,7 +250,7 @@ func (r Remote) CanListen() bool {
type Remotes []*Remote
//Filter out forward reversed/non-reversed remotes
// Filter out forward reversed/non-reversed remotes
func (rs Remotes) Reversed(reverse bool) Remotes {
subset := Remotes{}
for _, r := range rs {
@@ -265,7 +262,7 @@ func (rs Remotes) Reversed(reverse bool) Remotes {
return subset
}
//Encode back into strings
// Encode back into strings
func (rs Remotes) Encode() []string {
s := make([]string, len(rs))
for i, r := range rs {
+15 -6
View File
@@ -4,7 +4,7 @@ import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
"regexp"
"sync"
@@ -72,14 +72,16 @@ func (u *Users) Reset(users []*User) {
type UserIndex struct {
*cio.Logger
*Users
configFile string
configFile string
externalAuth bool
}
// NewUserIndex creates a source for users
func NewUserIndex(logger *cio.Logger) *UserIndex {
func NewUserIndex(logger *cio.Logger, externalAuth bool) *UserIndex {
return &UserIndex{
Logger: logger.Fork("users"),
Users: NewUsers(),
Logger: logger.Fork("users"),
Users: NewUsers(),
externalAuth: externalAuth,
}
}
@@ -125,7 +127,7 @@ func (u *UserIndex) loadUserIndex() error {
if u.configFile == "" {
return errors.New("configuration file not set")
}
b, err := ioutil.ReadFile(u.configFile)
b, err := os.ReadFile(u.configFile)
if err != nil {
return fmt.Errorf("Failed to read auth file: %s, error: %s", u.configFile, err)
}
@@ -134,12 +136,16 @@ func (u *UserIndex) loadUserIndex() error {
return errors.New("Invalid JSON: " + err.Error())
}
users := []*User{}
passwords := false
for auth, remotes := range raw {
user := &User{}
user.Name, user.Pass = ParseAuth(auth)
if user.Name == "" {
return errors.New("Invalid user:pass string")
}
if user.Pass != "" {
passwords = true
}
for _, r := range remotes {
if r == "" || r == "*" {
user.Addrs = append(user.Addrs, UserAllowAll)
@@ -153,6 +159,9 @@ func (u *UserIndex) loadUserIndex() error {
}
users = append(users, user)
}
if u.externalAuth && passwords {
u.Infof("warning: LDAP auth enabled, user passwords will not be used")
}
//swap
u.Reset(users)
return nil