mirror of
https://github.com/jpillora/chisel
synced 2026-06-08 15:07:02 +00:00
ldap authentication
Signed-off-by: Jerome Doucerain <jerome.doucerain@bell.ca>
This commit is contained in:
@@ -6,14 +6,15 @@ require (
|
||||
github.com/andrew-d/go-termutil v0.0.0-20150726205930-009166a695a2 // indirect
|
||||
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.3 // indirect
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
github.com/jpillora/ansi v1.0.2 // indirect
|
||||
github.com/jpillora/backoff v1.0.0
|
||||
github.com/jpillora/requestlog v1.0.0
|
||||
github.com/jpillora/sizestr v1.0.0
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce // indirect
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b // indirect
|
||||
)
|
||||
|
||||
@@ -1,9 +1,15 @@
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e h1:ZU22z/2YRFLyf/P4ZwUYSdNCWsMEI0VeyrFoI2rAhJQ=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20211209120228-48547f28849e/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
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/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.3 h1:JCKUtJPIcyOuG7ctGabLKMgIlKnGumD/iGjuWeEruDI=
|
||||
github.com/go-ldap/ldap/v3 v3.4.3/go.mod h1:7LdHfVt6iIOESVEe3Bs4Jp2sHEKgDeduAhgM1/f9qmo=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/jpillora/ansi v1.0.2 h1:+Ei5HCAH0xsrQRCT2PDr4mq9r4Gm4tg+arNdXRkB22s=
|
||||
@@ -18,9 +24,13 @@ github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9
|
||||
github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e h1:gsTQYXdTw2Gq7RBsWvlQ91b+aEQ6bXFUngBGuR8sPpI=
|
||||
golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29 h1:tkVvjkPTB7pnW3jnid7kNyAMPVWllTNOf/qKDze4p9o=
|
||||
golang.org/x/crypto v0.0.0-20220331220935-ae2d96664a29/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q=
|
||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
|
||||
@@ -159,9 +159,13 @@ var serverHelp = `
|
||||
provide a certificate notification email by setting CHISEL_LE_EMAIL.
|
||||
|
||||
--tls-ca, a path to a PEM encoded CA certificate bundle or a directory
|
||||
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.
|
||||
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 file containing the ldap authentication
|
||||
configuration.
|
||||
|
||||
` + commonHelp
|
||||
|
||||
func server(args []string) {
|
||||
@@ -181,6 +185,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", "", "")
|
||||
flags.StringVar(&config.LdapConfigFile,"ldap-config","","")
|
||||
|
||||
host := flags.String("host", "", "")
|
||||
p := flags.String("p", "", "")
|
||||
@@ -322,7 +327,7 @@ var clientHelp = `
|
||||
client's internal SOCKS5 proxy.
|
||||
|
||||
When stdio is used as local-host, the tunnel will connect standard
|
||||
input/output of this program with the remote. This is useful when
|
||||
input/output of this program with the remote. This is useful when
|
||||
combined with ssh ProxyCommand. You can use
|
||||
ssh -o ProxyCommand='chisel client chiselserver stdio:%h:%p' \
|
||||
user@example.com
|
||||
@@ -381,11 +386,11 @@ var clientHelp = `
|
||||
may be still verified (see --fingerprint) after inner connection
|
||||
is established.
|
||||
|
||||
--tls-key, a path to a PEM encoded private key used for client
|
||||
--tls-key, a path to a PEM encoded private key used for client
|
||||
authentication (mutual-TLS).
|
||||
|
||||
--tls-cert, a path to a PEM encoded certificate matching the provided
|
||||
private key. The certificate must have client authentication
|
||||
--tls-cert, a path to a PEM encoded certificate matching the provided
|
||||
private key. The certificate must have client authentication
|
||||
enabled (mutual-TLS).
|
||||
` + commonHelp
|
||||
|
||||
|
||||
+15
-1
@@ -30,6 +30,8 @@ type Config struct {
|
||||
Reverse bool
|
||||
KeepAlive time.Duration
|
||||
TLS TLSConfig
|
||||
LdapConfigFile string
|
||||
LdapConfig settings.LdapConfig
|
||||
}
|
||||
|
||||
// Server respresent a chisel service
|
||||
@@ -113,6 +115,12 @@ func NewServer(c *Config) (*Server, error) {
|
||||
if c.Reverse {
|
||||
server.Infof("Reverse tunnelling enabled")
|
||||
}
|
||||
// ldap authentication
|
||||
if c.LdapConfigFile != "" {
|
||||
if c.LdapConfig,err = server.LdapParseConfig(c.LdapConfigFile); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return server, nil
|
||||
}
|
||||
|
||||
@@ -177,7 +185,7 @@ 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) {
|
||||
if !found || ( user.Pass != string(password) && s.config.LdapConfigFile == "" ) || ( s.config.LdapConfigFile != "" && found && settings.LdapAuthUser(user,password,s.config.LdapConfig) != nil) {
|
||||
s.Debugf("Login failed for user: %s", n)
|
||||
return nil, errors.New("Invalid authentication for username: %s")
|
||||
}
|
||||
@@ -215,3 +223,9 @@ func (s *Server) DeleteUser(user string) {
|
||||
func (s *Server) ResetUsers(users []*settings.User) {
|
||||
s.users.Reset(users)
|
||||
}
|
||||
|
||||
// LdapParseConfig is validating the given ldap config
|
||||
func (s *Server) LdapParseConfig(LdapConfigFile string) (settings.LdapConfig,error) {
|
||||
ldapConfig,err := settings.ParseConfigFile(LdapConfigFile)
|
||||
return ldapConfig,err
|
||||
}
|
||||
|
||||
@@ -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,255 @@
|
||||
package settings
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
"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 ParseConfigFile(Configfile string) (LdapConfig, error) {
|
||||
var ldapConfig LdapConfig
|
||||
file, err := ioutil.ReadFile(Configfile)
|
||||
if err != nil {
|
||||
return ldapConfig, fmt.Errorf("Ldap config file error")
|
||||
}
|
||||
err = json.Unmarshal([]byte(file), &ldapConfig)
|
||||
if err != nil {
|
||||
return ldapConfig, fmt.Errorf("Error occured during unmarshaling ldap config file")
|
||||
}
|
||||
return ldapConfig, nil
|
||||
}
|
||||
|
||||
// authenticate a user using ldap credentials
|
||||
func LdapAuthUser(user *User,password []byte,ldapconfig LdapConfig) error {
|
||||
log.Printf("User %s to be authenticated in LDAP",user.Name)
|
||||
l, err := ConnectTLS(ldapconfig)
|
||||
if err != nil {
|
||||
log.Printf("Error occured during TLS connection to %s",ldapconfig.Url)
|
||||
return fmt.Errorf("Error occured during TLS connection to %s",ldapconfig.Url)
|
||||
}
|
||||
defer l.Close()
|
||||
// Normal Bind and Search
|
||||
result, err := BindAndSearch(l,ldapconfig,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")
|
||||
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
|
||||
// var certpool *x509.CertPool
|
||||
|
||||
if ldapconfig.Insecure {
|
||||
tlsConf = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
|
||||
if ldapconfig.CA != "" {
|
||||
certpool := x509.NewCertPool()
|
||||
CAfile, err := ioutil.ReadFile(ldapconfig.CA)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Ldap CA file error")
|
||||
}
|
||||
certpool.AppendCertsFromPEM([]byte(CAfile))
|
||||
tlsConf = &tls.Config{RootCAs: certpool}
|
||||
}
|
||||
|
||||
l, err := ldap.DialTLS("tcp", ldapconfig.Url, tlsConf)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// Normal Bind and Search
|
||||
func BindAndSearch(l *ldap.Conn,ldapconfig LdapConfig,user *User) (*ldap.SearchResult, error) {
|
||||
var filter string
|
||||
l.Bind(ldapconfig.BindDN, ldapconfig.BindPassword)
|
||||
if ldapconfig.Filter != "" {
|
||||
filter = fmt.Sprintf("(&(%s)(%s=%s))",ldapconfig.Filter,ldapconfig.IDMapTo,user.Name)
|
||||
} else {
|
||||
filter = fmt.Sprintf("(%s=%s)",ldapconfig.IDMapTo,user.Name)
|
||||
}
|
||||
log.Printf("filter %s",filter)
|
||||
searchReq := ldap.NewSearchRequest(
|
||||
ldapconfig.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 result, nil
|
||||
} else {
|
||||
return nil, fmt.Errorf("Couldn't fetch search entries")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// package main
|
||||
//
|
||||
// import (
|
||||
// "fmt"
|
||||
// "log"
|
||||
//
|
||||
// "github.com/go-ldap/ldap/v3"
|
||||
// )
|
||||
//
|
||||
// const (
|
||||
// BindUsername = "user@example.com"
|
||||
// BindPassword = "password"
|
||||
// FQDN = "DC.example.com"
|
||||
// BaseDN = "cn=Configuration,dc=example,dc=com"
|
||||
// Filter = "(objectClass=*)"
|
||||
// )
|
||||
//
|
||||
// func main() {
|
||||
// // TLS Connection
|
||||
// l, err := ConnectTLS()
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// defer l.Close()
|
||||
//
|
||||
// // Non-TLS Connection
|
||||
// //l, err := Connect()
|
||||
// //if err != nil {
|
||||
// // log.Fatal(err)
|
||||
// //}
|
||||
// //defer l.Close()
|
||||
//
|
||||
// // Anonymous Bind and Search
|
||||
// result, err := AnonymousBindAndSearch(l)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// result.Entries[0].Print()
|
||||
//
|
||||
// // Normal Bind and Search
|
||||
// result, err = BindAndSearch(l)
|
||||
// if err != nil {
|
||||
// log.Fatal(err)
|
||||
// }
|
||||
// result.Entries[0].Print()
|
||||
// }
|
||||
//
|
||||
// // Ldap Connection with TLS
|
||||
// func ConnectTLS() (*ldap.Conn, error) {
|
||||
// // You can also use IP instead of FQDN
|
||||
// l, err := ldap.DialURL(fmt.Sprintf("ldaps://%s:636", FQDN))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return l, nil
|
||||
// }
|
||||
//
|
||||
// // Ldap Connection without TLS
|
||||
// func Connect() (*ldap.Conn, error) {
|
||||
// // You can also use IP instead of FQDN
|
||||
// l, err := ldap.DialURL(fmt.Sprintf("ldap://%s:389", FQDN))
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
//
|
||||
// return l, nil
|
||||
// }
|
||||
//
|
||||
// // Anonymous Bind and Search
|
||||
// func AnonymousBindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
|
||||
// l.UnauthenticatedBind("")
|
||||
//
|
||||
// anonReq := ldap.NewSearchRequest(
|
||||
// "",
|
||||
// ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
|
||||
// ldap.NeverDerefAliases,
|
||||
// 0,
|
||||
// 0,
|
||||
// false,
|
||||
// Filter,
|
||||
// []string{},
|
||||
// nil,
|
||||
// )
|
||||
// result, err := l.Search(anonReq)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("Anonymous Bind Search Error: %s", err)
|
||||
// }
|
||||
//
|
||||
// if len(result.Entries) > 0 {
|
||||
// result.Entries[0].Print()
|
||||
// return result, nil
|
||||
// } else {
|
||||
// return nil, fmt.Errorf("Couldn't fetch anonymous bind search entries")
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// // Normal Bind and Search
|
||||
// func BindAndSearch(l *ldap.Conn) (*ldap.SearchResult, error) {
|
||||
// l.Bind(BindUsername, BindPassword)
|
||||
//
|
||||
// searchReq := ldap.NewSearchRequest(
|
||||
// BaseDN,
|
||||
// ldap.ScopeBaseObject, // you can also use ldap.ScopeWholeSubtree
|
||||
// ldap.NeverDerefAliases,
|
||||
// 0,
|
||||
// 0,
|
||||
// false,
|
||||
// Filter,
|
||||
// []string{},
|
||||
// nil,
|
||||
// )
|
||||
// result, err := l.Search(searchReq)
|
||||
// if err != nil {
|
||||
// return nil, fmt.Errorf("Search Error: %s", err)
|
||||
// }
|
||||
//
|
||||
// if len(result.Entries) > 0 {
|
||||
// return result, nil
|
||||
// } else {
|
||||
// return nil, fmt.Errorf("Couldn't fetch search entries")
|
||||
// }
|
||||
// }
|
||||
Reference in New Issue
Block a user