mirror of
https://github.com/jpillora/chisel
synced 2026-06-08 15:07:02 +00:00
Ldap authentication #369
commit878646310bAuthor: 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> commit3a6f13c097Author: 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> commit3f92a77b5dMerge:6b87e3fce307e5Author: 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 commit6b87e3fe49Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 16:52:56 2022 -0500 MD again commitb66e317f5bAuthor: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 16:50:20 2022 -0500 markdown adjustments commit62920afd7dMerge:3e05ec58f96052Author: 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 commit3e05ec57faAuthor: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 16:14:53 2022 -0500 markdown adjustments commit659c06d3d1Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 16:10:52 2022 -0500 markdown adjustments commitcfb1d00000Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 15:58:32 2022 -0500 Changes as recommended by J.Pillora commit69a8ce0fd5Author: 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 commit40da15740aAuthor: 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 commit4708fe182bAuthor: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 19:33:27 2022 -0400 ca file debug commit92b20ba512Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 18:51:14 2022 -0400 add user in debugging log when too many entries commitba54e8d759Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 18:43:24 2022 -0400 remove comments in ldap.go commit505ba024c3Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 18:39:23 2022 -0400 new --ldap-config in README.md commit111b545fc7Author: 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> commit8f9605217bAuthor: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 16:14:53 2022 -0500 markdown adjustments commit2d7c977825Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 16:10:52 2022 -0500 markdown adjustments commit7c0e7414cbAuthor: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Fri Nov 25 15:58:32 2022 -0500 Changes as recommended by J.Pillora commitf7cdb96cb8Author: 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 commit00836b1e61Author: 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 commit6f7bc5db73Merge:79c0a67bea4540Author: jdoucerain <jerome.doucerain@bell.ca> Date: Tue Oct 18 09:53:06 2022 -0400 Merge branch 'jpillora:master' into LDAP-authentication commit79c0a676c5Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 19:33:27 2022 -0400 ca file debug commit5b77520719Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 18:51:14 2022 -0400 add user in debugging log when too many entries commit6c5081371dAuthor: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 18:43:24 2022 -0400 remove comments in ldap.go commit4bc9fc4766Author: Jerome Doucerain <jerome.doucerain@bell.ca> Date: Wed Jun 29 18:39:23 2022 -0400 new --ldap-config in README.md commit6c1f26968cAuthor: 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:
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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=
|
||||
|
||||
@@ -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
@@ -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,3 +1,4 @@
|
||||
//go:build pprof
|
||||
// +build pprof
|
||||
|
||||
package cos
|
||||
|
||||
+2
-1
@@ -1,4 +1,5 @@
|
||||
//+build !windows
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cos
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
//+build windows
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cos
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user