Skip to content
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

Plumb token and token file through rest.Config #71713

Merged
merged 1 commit into from
Dec 4, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ func restConfigFromKubeconfig(configAuthInfo *clientcmdapi.AuthInfo) (*rest.Conf
return nil, err
}
config.BearerToken = string(tokenBytes)
config.BearerTokenFile = configAuthInfo.TokenFile
}
if len(configAuthInfo.Impersonate) > 0 {
config.Impersonate = rest.ImpersonationConfig{
Expand Down
4 changes: 0 additions & 4 deletions staging/src/k8s.io/client-go/rest/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ go_test(
"config_test.go",
"plugin_test.go",
"request_test.go",
"token_source_test.go",
"url_utils_test.go",
"urlbackoff_test.go",
],
Expand Down Expand Up @@ -41,7 +40,6 @@ go_test(
"//staging/src/k8s.io/client-go/util/testing:go_default_library",
"//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/golang.org/x/oauth2:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
Expand All @@ -53,7 +51,6 @@ go_library(
"config.go",
"plugin.go",
"request.go",
"token_source.go",
"transport.go",
"url_utils.go",
"urlbackoff.go",
Expand All @@ -80,7 +77,6 @@ go_library(
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//staging/src/k8s.io/client-go/util/flowcontrol:go_default_library",
"//vendor/golang.org/x/net/http2:go_default_library",
"//vendor/golang.org/x/oauth2:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
Expand Down
26 changes: 16 additions & 10 deletions staging/src/k8s.io/client-go/rest/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ type Config struct {
// TODO: demonstrate an OAuth2 compatible client.
BearerToken string

// Path to a file containing a BearerToken.
// If set, the contents are periodically read.
// The last successfully read value takes precedence over BearerToken.
BearerTokenFile string

// Impersonate is the configuration that RESTClient will use for impersonation.
Impersonate ImpersonationConfig

Expand Down Expand Up @@ -322,9 +327,8 @@ func InClusterConfig() (*Config, error) {
return nil, ErrNotInCluster
}

ts := NewCachedFileTokenSource(tokenFile)

if _, err := ts.Token(); err != nil {
token, err := ioutil.ReadFile(tokenFile)
if err != nil {
return nil, err
}

Expand All @@ -340,7 +344,8 @@ func InClusterConfig() (*Config, error) {
// TODO: switch to using cluster DNS.
Host: "https://" + net.JoinHostPort(host, port),
TLSClientConfig: tlsClientConfig,
WrapTransport: TokenSourceWrapTransport(ts),
BearerToken: string(token),
BearerTokenFile: tokenFile,
}, nil
}

Expand Down Expand Up @@ -430,12 +435,13 @@ func AnonymousClientConfig(config *Config) *Config {
// CopyConfig returns a copy of the given config
func CopyConfig(config *Config) *Config {
return &Config{
Host: config.Host,
APIPath: config.APIPath,
ContentConfig: config.ContentConfig,
Username: config.Username,
Password: config.Password,
BearerToken: config.BearerToken,
Host: config.Host,
APIPath: config.APIPath,
ContentConfig: config.ContentConfig,
Username: config.Username,
Password: config.Password,
BearerToken: config.BearerToken,
BearerTokenFile: config.BearerTokenFile,
Impersonate: ImpersonationConfig{
Groups: config.Impersonate.Groups,
Extra: config.Impersonate.Extra,
Expand Down
1 change: 1 addition & 0 deletions staging/src/k8s.io/client-go/rest/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ func TestAnonymousConfig(t *testing.T) {
// is added to Config, update AnonymousClientConfig to preserve the field otherwise.
expected.Impersonate = ImpersonationConfig{}
expected.BearerToken = ""
expected.BearerTokenFile = ""
expected.Username = ""
expected.Password = ""
expected.AuthProvider = nil
Expand Down
7 changes: 4 additions & 3 deletions staging/src/k8s.io/client-go/tools/clientcmd/client_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,11 +229,12 @@ func (config *DirectClientConfig) getUserIdentificationPartialConfig(configAuthI
if len(configAuthInfo.Token) > 0 {
mergedConfig.BearerToken = configAuthInfo.Token
} else if len(configAuthInfo.TokenFile) > 0 {
ts := restclient.NewCachedFileTokenSource(configAuthInfo.TokenFile)
if _, err := ts.Token(); err != nil {
tokenBytes, err := ioutil.ReadFile(configAuthInfo.TokenFile)
if err != nil {
return nil, err
}
mergedConfig.WrapTransport = restclient.TokenSourceWrapTransport(ts)
mergedConfig.BearerToken = string(tokenBytes)
mergedConfig.BearerTokenFile = configAuthInfo.TokenFile
}
if len(configAuthInfo.Impersonate) > 0 {
mergedConfig.Impersonate = restclient.ImpersonationConfig{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ package clientcmd

import (
"io/ioutil"
"net/http"
"os"
"reflect"
"strings"
Expand Down Expand Up @@ -334,19 +333,7 @@ func TestBasicTokenFile(t *testing.T) {
t.Fatalf("Unexpected error: %v", err)
}

var out *http.Request
clientConfig.WrapTransport(fakeTransport(func(req *http.Request) (*http.Response, error) {
out = req
return &http.Response{}, nil
})).RoundTrip(&http.Request{})

matchStringArg(token, strings.TrimPrefix(out.Header.Get("Authorization"), "Bearer "), t)
}

type fakeTransport func(*http.Request) (*http.Response, error)

func (ft fakeTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return ft(req)
matchStringArg(token, clientConfig.BearerToken, t)
}

func TestPrecedenceTokenFile(t *testing.T) {
Expand Down
4 changes: 4 additions & 0 deletions staging/src/k8s.io/client-go/transport/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,11 @@ go_test(
srcs = [
"cache_test.go",
"round_trippers_test.go",
"token_source_test.go",
"transport_test.go",
],
embed = [":go_default_library"],
deps = ["//vendor/golang.org/x/oauth2:go_default_library"],
)

go_library(
Expand All @@ -22,12 +24,14 @@ go_library(
"cache.go",
"config.go",
"round_trippers.go",
"token_source.go",
"transport.go",
],
importmap = "k8s.io/kubernetes/vendor/k8s.io/client-go/transport",
importpath = "k8s.io/client-go/transport",
deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//vendor/golang.org/x/oauth2:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
Expand Down
7 changes: 6 additions & 1 deletion staging/src/k8s.io/client-go/transport/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ type Config struct {
// Bearer token for authentication
BearerToken string

// Path to a file containing a BearerToken.
// If set, the contents are periodically read.
// The last successfully read value takes precedence over BearerToken.
BearerTokenFile string

// Impersonate is the config that this Config will impersonate using
Impersonate ImpersonationConfig

Expand Down Expand Up @@ -80,7 +85,7 @@ func (c *Config) HasBasicAuth() bool {

// HasTokenAuth returns whether the configuration has token authentication or not.
func (c *Config) HasTokenAuth() bool {
return len(c.BearerToken) != 0
return len(c.BearerToken) != 0 || len(c.BearerTokenFile) != 0
}

// HasCertAuth returns whether the configuration has certificate authentication or not.
Expand Down
39 changes: 36 additions & 3 deletions staging/src/k8s.io/client-go/transport/round_trippers.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"strings"
"time"

"golang.org/x/oauth2"
"k8s.io/klog"

utilnet "k8s.io/apimachinery/pkg/util/net"
Expand All @@ -44,7 +45,11 @@ func HTTPWrappersForConfig(config *Config, rt http.RoundTripper) (http.RoundTrip
case config.HasBasicAuth() && config.HasTokenAuth():
return nil, fmt.Errorf("username/password or bearer token may be set, but not both")
case config.HasTokenAuth():
rt = NewBearerAuthRoundTripper(config.BearerToken, rt)
var err error
rt, err = NewBearerAuthWithRefreshRoundTripper(config.BearerToken, config.BearerTokenFile, rt)
if err != nil {
return nil, err
}
case config.HasBasicAuth():
rt = NewBasicAuthRoundTripper(config.Username, config.Password, rt)
}
Expand Down Expand Up @@ -265,13 +270,35 @@ func (rt *impersonatingRoundTripper) WrappedRoundTripper() http.RoundTripper { r

type bearerAuthRoundTripper struct {
bearer string
source oauth2.TokenSource
rt http.RoundTripper
}

// NewBearerAuthRoundTripper adds the provided bearer token to a request
// unless the authorization header has already been set.
func NewBearerAuthRoundTripper(bearer string, rt http.RoundTripper) http.RoundTripper {
return &bearerAuthRoundTripper{bearer, rt}
return &bearerAuthRoundTripper{bearer, nil, rt}
}

// NewBearerAuthRoundTripper adds the provided bearer token to a request
// unless the authorization header has already been set.
// If tokenFile is non-empty, it is periodically read,
// and the last successfully read content is used as the bearer token.
// If tokenFile is non-empty and bearer is empty, the tokenFile is read
// immediately to populate the initial bearer token.
func NewBearerAuthWithRefreshRoundTripper(bearer string, tokenFile string, rt http.RoundTripper) (http.RoundTripper, error) {
if len(tokenFile) == 0 {
return &bearerAuthRoundTripper{bearer, nil, rt}, nil
}
source := NewCachedFileTokenSource(tokenFile)
if len(bearer) == 0 {
token, err := source.Token()
if err != nil {
return nil, err
}
bearer = token.AccessToken
}
return &bearerAuthRoundTripper{bearer, source, rt}, nil
}

func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
Expand All @@ -280,7 +307,13 @@ func (rt *bearerAuthRoundTripper) RoundTrip(req *http.Request) (*http.Response,
}

req = utilnet.CloneRequest(req)
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", rt.bearer))
token := rt.bearer
if rt.source != nil {
if refreshedToken, err := rt.source.Token(); err == nil {
token = refreshedToken.AccessToken
}
}
req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", token))
return rt.rt.RoundTrip(req)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package rest
package transport

import (
"fmt"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package rest
package transport

import (
"fmt"
Expand Down