Skip to content

Commit 0f822ef

Browse files
authored
[auth] Add Athenz authentication provider (apache#227)
* feat: add athenz authentication provider * style: fix style errors
1 parent fcda9b5 commit 0f822ef

File tree

7 files changed

+366
-2
lines changed

7 files changed

+366
-2
lines changed

go.mod

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,6 @@ require (
1515
github.com/spaolacci/murmur3 v1.1.0
1616
github.com/spf13/cobra v0.0.3
1717
github.com/spf13/pflag v1.0.3 // indirect
18-
github.com/stretchr/testify v1.3.0
18+
github.com/stretchr/testify v1.4.0
19+
github.com/yahoo/athenz v1.8.55
1920
)

go.sum

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,27 @@
1+
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
2+
github.com/ardielle/ardielle-go v1.5.2 h1:TilHTpHIQJ27R1Tl/iITBzMwiUGSlVfiVhwDNGM3Zj4=
3+
github.com/ardielle/ardielle-go v1.5.2/go.mod h1:I4hy1n795cUhaVt/ojz83SNVCYIGsAFAONtv2Dr7HUI=
4+
github.com/ardielle/ardielle-tools v1.5.4/go.mod h1:oZN+JRMnqGiIhrzkRN9l26Cej9dEx4jeNG6A+AdkShk=
15
github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6 h1:KXlsf+qt/X5ttPGEjR0tPH1xaWWoKBEg9Q1THAj2h3I=
26
github.com/beefsack/go-rate v0.0.0-20180408011153-efa7637bb9b6/go.mod h1:6YNgTHLutezwnBvyneBbwvB8C82y3dcoOj5EQJIdGXA=
37
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b h1:AP/Y7sqYicnjGDfD5VcY4CIfh1hRXBUavxrvELjTiOE=
48
github.com/bmizerany/perks v0.0.0-20141205001514-d9a9656a3a4b/go.mod h1:ac9efd0D1fsDb3EJvhqgXRbFx7bs2wqZ10HQPeU8U/Q=
9+
github.com/boynton/repl v0.0.0-20170116235056-348863958e3e/go.mod h1:Crc/GCZ3NXDVCio7Yr0o+SSrytpcFhLmVCIzi0s49t4=
510
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
611
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
712
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
13+
github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4wg/adLLz5xh5CmpiCA=
14+
github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0=
815
github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg=
916
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
1017
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
1118
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
19+
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
20+
github.com/gorilla/mux v1.7.3/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
1221
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
1322
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
23+
github.com/jawher/mow.cli v1.0.4/go.mod h1:5hQj2V8g+qYmLUVWqu4Wuja1pI57M83EChYLVZ0sMKk=
24+
github.com/jawher/mow.cli v1.1.0/go.mod h1:aNaQlc7ozF3vw6IJ2dHjp2ZFiA4ozMIYY6PyuRJwlUg=
1425
github.com/klauspost/compress v1.9.2 h1:LfVyl+ZlLlLDeQ/d2AqfGIIH4qEDu0Ed2S5GyhCWIWY=
1526
github.com/klauspost/compress v1.9.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
1627
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
@@ -31,8 +42,36 @@ github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
3142
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
3243
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
3344
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
45+
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
46+
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
3447
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
3548
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
3649
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
50+
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
51+
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
52+
github.com/yahoo/athenz v1.8.55 h1:xGhxN3yLq334APyn0Zvcc+aqu78Q7BBhYJevM3EtTW0=
53+
github.com/yahoo/athenz v1.8.55/go.mod h1:G7LLFUH7Z/r4QAB7FfudfuA7Am/eCzO1GlzBhDL6Kv0=
54+
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
55+
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
56+
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
57+
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
58+
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
59+
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
3760
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
3861
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
62+
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
63+
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
64+
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
65+
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
66+
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
67+
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
68+
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
69+
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
70+
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
71+
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
72+
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
73+
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
74+
gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
75+
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
76+
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
77+
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

pulsar/client.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ func NewAuthenticationTLS(certificatePath string, privateKeyPath string) Authent
5757
return auth.NewAuthenticationTLS(certificatePath, privateKeyPath)
5858
}
5959

60+
func NewAuthenticationAthenz(authParams map[string]string) Authentication {
61+
athenz, _ := auth.NewAuthenticationAthenzWithParams(authParams)
62+
return athenz
63+
}
64+
6065
// Builder interface that is used to construct a Pulsar Client instance.
6166
type ClientOptions struct {
6267
// Configure the service URL for the Pulsar service.

pulsar/client_impl.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ func newClient(options ClientOptions) (Client, error) {
8080
return nil, errors.New("invalid auth provider interface")
8181
}
8282
}
83+
err = authProvider.Init()
84+
if err != nil {
85+
return nil, err
86+
}
8387

8488
connectionTimeout := options.ConnectionTimeout
8589
if connectionTimeout.Nanoseconds() == 0 {

pulsar/internal/auth/athenz.go

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
package auth
19+
20+
import (
21+
"crypto/tls"
22+
"encoding/base64"
23+
"errors"
24+
"io/ioutil"
25+
"regexp"
26+
"strings"
27+
"time"
28+
29+
zms "github.com/yahoo/athenz/libs/go/zmssvctoken"
30+
zts "github.com/yahoo/athenz/libs/go/ztsroletoken"
31+
)
32+
33+
const (
34+
minExpire = 2 * time.Hour
35+
maxExpire = 24 * time.Hour
36+
)
37+
38+
type athenzAuthProvider struct {
39+
providerDomain string
40+
tenantDomain string
41+
tenantService string
42+
privateKey string
43+
keyID string
44+
principalHeader string
45+
ztsURL string
46+
tokenBuilder zms.TokenBuilder
47+
roleToken zts.RoleToken
48+
zmsNewTokenBuilder func(domain, name string, privateKeyPEM []byte, keyVersion string) (zms.TokenBuilder, error)
49+
ztsNewRoleToken func(tok zms.Token, domain string, opts zts.RoleTokenOptions) zts.RoleToken
50+
}
51+
52+
type privateKeyURI struct {
53+
Scheme string
54+
MediaTypeAndEncodingType string
55+
Data string
56+
Path string
57+
}
58+
59+
func NewAuthenticationAthenzWithParams(params map[string]string) (Provider, error) {
60+
return NewAuthenticationAthenz(
61+
params["providerDomain"],
62+
params["tenantDomain"],
63+
params["tenantService"],
64+
params["privateKey"],
65+
params["keyId"],
66+
params["principalHeader"],
67+
params["ztsUrl"],
68+
), nil
69+
}
70+
71+
func NewAuthenticationAthenz(
72+
providerDomain string,
73+
tenantDomain string,
74+
tenantService string,
75+
privateKey string,
76+
keyID string,
77+
principalHeader string,
78+
ztsURL string) Provider {
79+
var fixedKeyID string
80+
if keyID == "" {
81+
fixedKeyID = "0"
82+
} else {
83+
fixedKeyID = keyID
84+
}
85+
ztsNewRoleToken := func(tok zms.Token, domain string, opts zts.RoleTokenOptions) zts.RoleToken {
86+
return zts.RoleToken(zts.NewRoleToken(tok, domain, opts))
87+
}
88+
89+
return &athenzAuthProvider{
90+
providerDomain: providerDomain,
91+
tenantDomain: tenantDomain,
92+
tenantService: tenantService,
93+
privateKey: privateKey,
94+
keyID: fixedKeyID,
95+
principalHeader: principalHeader,
96+
ztsURL: strings.TrimSuffix(ztsURL, "/"),
97+
zmsNewTokenBuilder: zms.NewTokenBuilder,
98+
ztsNewRoleToken: ztsNewRoleToken,
99+
}
100+
}
101+
102+
func (p *athenzAuthProvider) Init() error {
103+
uriSt := parseURI(p.privateKey)
104+
var keyData []byte
105+
106+
if uriSt.Scheme == "data" {
107+
if uriSt.MediaTypeAndEncodingType != "application/x-pem-file;base64" {
108+
return errors.New("Unsupported mediaType or encodingType: " + uriSt.MediaTypeAndEncodingType)
109+
}
110+
key, err := base64.StdEncoding.DecodeString(uriSt.Data)
111+
if err != nil {
112+
return err
113+
}
114+
keyData = key
115+
} else if uriSt.Scheme == "file" {
116+
key, err := ioutil.ReadFile(uriSt.Path)
117+
if err != nil {
118+
return err
119+
}
120+
keyData = key
121+
} else {
122+
return errors.New("Unsupported URI Scheme: " + uriSt.Scheme)
123+
}
124+
125+
tb, err := p.zmsNewTokenBuilder(p.tenantDomain, p.tenantService, keyData, p.keyID)
126+
if err != nil {
127+
return err
128+
}
129+
p.tokenBuilder = tb
130+
131+
roleToken := p.ztsNewRoleToken(p.tokenBuilder.Token(), p.providerDomain, zts.RoleTokenOptions{
132+
BaseZTSURL: p.ztsURL + "/zts/v1",
133+
MinExpire: minExpire,
134+
MaxExpire: maxExpire,
135+
AuthHeader: p.principalHeader,
136+
})
137+
p.roleToken = roleToken
138+
139+
return nil
140+
}
141+
142+
func (p *athenzAuthProvider) Name() string {
143+
return "athenz"
144+
}
145+
146+
func (p *athenzAuthProvider) GetTLSCertificate() (*tls.Certificate, error) {
147+
return nil, nil
148+
}
149+
150+
func (p *athenzAuthProvider) GetData() ([]byte, error) {
151+
tok, err := p.roleToken.RoleTokenValue()
152+
if err != nil {
153+
return nil, err
154+
}
155+
156+
return []byte(tok), nil
157+
}
158+
159+
func (p *athenzAuthProvider) Close() error {
160+
return nil
161+
}
162+
163+
func parseURI(uri string) privateKeyURI {
164+
var uriSt privateKeyURI
165+
// scheme mediatype[;base64] path file
166+
const expression = `^(?:([^:/?#]+):)(?:([;/\\\-\w]*),)?(?:/{0,2}((?:[^?#/]*/)*))?([^?#]*)`
167+
168+
// when expression cannot be parsed, then panics
169+
re := regexp.MustCompile(expression)
170+
if re.MatchString(uri) {
171+
groups := re.FindStringSubmatch(uri)
172+
uriSt.Scheme = groups[1]
173+
uriSt.MediaTypeAndEncodingType = groups[2]
174+
uriSt.Data = groups[4]
175+
uriSt.Path = groups[3] + groups[4]
176+
}
177+
178+
return uriSt
179+
}

0 commit comments

Comments
 (0)