Skip to content

support krb5 #77

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 19 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ zookeeper-*/
zookeeper-*.tar.gz
apache-zookeeper-*/
apache-zookeeper-*.tar.gz
go.sum
35 changes: 32 additions & 3 deletions conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,10 @@ type Conn struct {
logInfo bool // true if information messages are logged; false if only errors are logged

buf []byte

// sasl auth
hostname string // host with addr
saslConfig *SASLConfig
}

// connOption represents a connection option.
Expand Down Expand Up @@ -158,9 +162,9 @@ type HostProvider interface {
Init(servers []string) error
// Len returns the number of servers.
Len() int
// Next returns the next server to connect to. retryStart will be true if we've looped through
// Next returns the next server/hostname to connect to. retryStart will be true if we've looped through
// all known servers without Connected() being called.
Next() (server string, retryStart bool)
Next() (server string, hostname string, retryStart bool)
// Notify the HostProvider of a successful connection.
Connected()
}
Expand Down Expand Up @@ -205,6 +209,7 @@ func Connect(servers []string, sessionTimeout time.Duration, options ...connOpti
logInfo: true, // default is true for backwards compatability
buf: make([]byte, bufferSize),
resendZkAuthFn: resendZkAuth,
saslConfig: &SASLConfig{SASLType: NO_SASL},
}

// Set provided options.
Expand Down Expand Up @@ -310,6 +315,12 @@ func WithMaxConnBufferSize(maxBufferSize int) connOption {
}
}

func WithSASLConfig(saslConfig *SASLConfig) connOption {
return func(c *Conn) {
c.saslConfig = saslConfig
}
}

// Close will submit a close request with ZK and signal the connection to stop
// sending and receiving packets.
func (c *Conn) Close() {
Expand Down Expand Up @@ -367,7 +378,7 @@ func (c *Conn) connect() error {
var retryStart bool
for {
c.serverMu.Lock()
c.server, retryStart = c.hostProvider.Next()
c.server, c.hostname, retryStart = c.hostProvider.Next()
c.serverMu.Unlock()

c.setState(StateConnecting)
Expand Down Expand Up @@ -867,6 +878,13 @@ func (c *Conn) recvLoop(conn net.Conn) error {
if req.recvFunc != nil {
req.recvFunc(req, &res, err)
}

// when kerberos auth in GSSAPI_FINISH stage, won't return body data.
if req.opcode == opSetSASL && c.saslConfig.SASLType == KERBEROS && err == ErrShortBuffer && res.Err == 0 {
req.recvStruct = setSaslResponse{string([]byte{})}
err = nil
}

req.recvChan <- response{res.Zxid, err}
if req.opcode == opClose {
return io.EOF
Expand Down Expand Up @@ -1346,6 +1364,17 @@ func resendZkAuth(ctx context.Context, c *Conn) error {
c.credsMu.Lock()
defer c.credsMu.Unlock()

switch c.saslConfig.SASLType {
case KERBEROS:
var krbAuth = &KerberosAuth{Config: c.saslConfig.KerberosConfig}
if err := krbAuth.Authorize(ctx, c); err != nil {
c.logger.Printf("failed to authorize with kerberos, err: %s, zookeeper server: %s", err, c.hostname)
return err
} else {
c.logger.Printf("kerberos authorize successfully, zookeeper server: %s", c.hostname)
}
}

if c.logInfo {
c.logger.Printf("re-submitting `%d` credentials after reconnect", len(c.creds))
}
Expand Down
23 changes: 23 additions & 0 deletions constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const (
DefaultPort = 2181
)

// https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/ZooDefs.java
const (
opNotify = 0
opCreate = 1
Expand All @@ -32,6 +33,7 @@ const (
opClose = -11
opSetAuth = 100
opSetWatches = 101
opSetSASL = 102
opError = -1
// Not in protocol, used internally
opWatcherEvent = -2
Expand Down Expand Up @@ -224,6 +226,7 @@ var (
opClose: "close",
opSetAuth: "setAuth",
opSetWatches: "setWatches",
opSetSASL: "setSASL",

opWatcherEvent: "watcherEvent",
}
Expand Down Expand Up @@ -263,3 +266,23 @@ var (
ModeStandalone: "standalone",
}
)

type SASLType uint8

const (
NO_SASL SASLType = iota
KERBEROS
)

var (
saslTypeNames = map[SASLType]string{
KERBEROS: "kerberos",
}
)

func (s SASLType) String() string {
if name := saslTypeNames[s]; name != "" {
return name
}
return "none"
}
8 changes: 6 additions & 2 deletions dnshostprovider.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
type DNSHostProvider struct {
mu sync.Mutex // Protects everything, so we can add asynchronous updates later.
servers []string
hostMap map[string]string
curr int
last int
lookupHost func(string) ([]string, error) // Override of net.LookupHost, for testing.
Expand All @@ -31,6 +32,7 @@ func (hp *DNSHostProvider) Init(servers []string) error {
}

found := []string{}
hostMap := map[string]string{}
for _, server := range servers {
host, port, err := net.SplitHostPort(server)
if err != nil {
Expand All @@ -42,6 +44,7 @@ func (hp *DNSHostProvider) Init(servers []string) error {
}
for _, addr := range addrs {
found = append(found, net.JoinHostPort(addr, port))
hostMap[net.JoinHostPort(addr, port)] = server
}
}

Expand All @@ -53,6 +56,7 @@ func (hp *DNSHostProvider) Init(servers []string) error {
stringShuffle(found)

hp.servers = found
hp.hostMap = hostMap
hp.curr = -1
hp.last = -1

Expand All @@ -69,15 +73,15 @@ func (hp *DNSHostProvider) Len() int {
// Next returns the next server to connect to. retryStart will be true
// if we've looped through all known servers without Connected() being
// called.
func (hp *DNSHostProvider) Next() (server string, retryStart bool) {
func (hp *DNSHostProvider) Next() (server, hostname string, retryStart bool) {
hp.mu.Lock()
defer hp.mu.Unlock()
hp.curr = (hp.curr + 1) % len(hp.servers)
retryStart = hp.curr == hp.last
if hp.last == -1 {
hp.last = 0
}
return hp.servers[hp.curr], retryStart
return hp.servers[hp.curr], hp.hostMap[hp.servers[hp.curr]], retryStart
}

// Connected notifies the HostProvider of a successful connection.
Expand Down
15 changes: 10 additions & 5 deletions dnshostprovider_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,14 @@ func newLocalHostPortsFacade(inner HostProvider, ports []int) *localHostPortsFac
func (lhpf *localHostPortsFacade) Len() int { return lhpf.inner.Len() }
func (lhpf *localHostPortsFacade) Connected() { lhpf.inner.Connected() }
func (lhpf *localHostPortsFacade) Init(servers []string) error { return lhpf.inner.Init(servers) }
func (lhpf *localHostPortsFacade) Next() (string, bool) {
server, retryStart := lhpf.inner.Next()
func (lhpf *localHostPortsFacade) Next() (string, string, bool) {
server, hostname, retryStart := lhpf.inner.Next()

fmt.Println("hostname, ", hostname)

// If we've already set up a mapping for that server, just return it.
if localMapping := lhpf.mapped[server]; localMapping != "" {
return localMapping, retryStart
return localMapping, hostname, retryStart
}

if lhpf.nextPort == len(lhpf.ports) {
Expand All @@ -86,7 +88,7 @@ func (lhpf *localHostPortsFacade) Next() (string, bool) {
localMapping := fmt.Sprintf("localhost:%d", lhpf.ports[lhpf.nextPort])
lhpf.mapped[server] = localMapping
lhpf.nextPort++
return localMapping, retryStart
return localMapping, hostname, retryStart
}

var _ HostProvider = &localHostPortsFacade{}
Expand Down Expand Up @@ -213,10 +215,13 @@ func TestDNSHostProviderRetryStart(t *testing.T) {
}

for i, td := range testdata {
_, retryStartGot := hp.Next()
_, hostname, retryStartGot := hp.Next()
if retryStartGot != td.retryStartWant {
t.Errorf("%d: retryStart=%v; want %v", i, retryStartGot, td.retryStartWant)
}
if hostname != "foo.example.com:12345" {
t.Errorf("hostname expect %s, but get %s", "foo.example.com:12345", hostname)
}
if td.callConnected {
hp.Connected()
}
Expand Down
8 changes: 8 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
module github.com/go-zookeeper/zk

go 1.13

require (
github.com/jcmturner/gofork v1.0.0
github.com/jcmturner/gokrb5/v8 v8.4.2
github.com/jcmturner/krb5test v0.0.0-20201230140143-102e4b78cdb8
)

replace github.com/jcmturner/krb5test => github.com/zhuliquan/krb5test v0.0.0-20220221153311-7927889416f6
46 changes: 46 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.0/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8=
github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo=
github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
github.com/jcmturner/goidentity/v6 v6.0.1 h1:VKnZd2oEIMorCTsFBnJWbExfNN7yZr3EhJAxwOkZg6o=
github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
github.com/jcmturner/gokrb5/v8 v8.4.0/go.mod h1:T1hnNppQsBtxW0tCHMHTkAt8n/sABdzZgZdoFrZaZNM=
github.com/jcmturner/gokrb5/v8 v8.4.2 h1:6ZIM6b/JJN0X8UM43ZOM6Z4SJzla+a/u7scXFJzodkA=
github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
github.com/jcmturner/rpc/v2 v2.0.2/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZY=
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
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/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/zhuliquan/krb5test v0.0.0-20220221153311-7927889416f6 h1:45c1tojqnEeMLFsCaxe5U5vuAqqXRxDM1R25D3sqWEM=
github.com/zhuliquan/krb5test v0.0.0-20220221153311-7927889416f6/go.mod h1:eUQyrWMo2eMPWPezAYjLLwaiQHzJUD3su+1vAL10dOk=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Loading