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

Add support for ServiceAccount auth to kubeletstats #324

Merged
merged 11 commits into from
Jun 17, 2020
Merged
5 changes: 5 additions & 0 deletions receiver/kubeletstatsreceiver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ TLS tells this receiver to use TLS for auth and requires that the fields
ServiceAccount tells this receiver to use the default service account token
to authenticate to the kubelet API.

Note: a missing or empty `endpoint` will cause the hostname on which the collector
is running to be used as the endpoint. If the hostNetwork flag is set, and the
collector is running in a pod, this hostname will resolve to the node's network
namespace.

Copy link
Member

@dmitryax dmitryax Jun 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we add a serviceAccount usage example here with downward API?

If I understand correctly it should be:

  kubeletstats:
    authType: serviceAccount
    endpoint: https://${K8S_NODE_NAME}:10250

Another note:

Make sure that it's set using the downward API in the collector pod spec as follows:

env:
  - name: K8S_NODE_NAME
    valueFrom:
      fieldRef:
        fieldPath: spec.nodeName

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, that's a good idea. Done.

```yaml
receivers:
kubeletstats:
Expand Down
1 change: 1 addition & 0 deletions receiver/kubeletstatsreceiver/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ require (
github.com/golang/protobuf v1.3.5
github.com/open-telemetry/opentelemetry-collector-contrib/internal/common v0.0.0-00010101000000-000000000000
github.com/open-telemetry/opentelemetry-collector-contrib/receiver/redisreceiver v0.0.0-20200518175917-05cf2ea24e6c
github.com/pkg/errors v0.9.1
github.com/stretchr/testify v1.5.1
go.opentelemetry.io/collector v0.3.1-0.20200612184320-01ce74db9c44
go.uber.org/zap v1.10.0
Expand Down
7 changes: 4 additions & 3 deletions receiver/kubeletstatsreceiver/kubelet/cert.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,22 +16,23 @@ package kubelet

import (
"crypto/x509"
"errors"
"io/ioutil"

"github.com/pkg/errors"
)

func systemCertPoolPlusPath(certPath string) (*x509.CertPool, error) {
sysCerts, err := x509.SystemCertPool()
if err != nil {
return nil, err
return nil, errors.WithMessage(err, "Could not load system x509 cert pool")
}
return certPoolPlusPath(sysCerts, certPath)
}

func certPoolPlusPath(certPool *x509.CertPool, certPath string) (*x509.CertPool, error) {
certBytes, err := ioutil.ReadFile(certPath)
if err != nil {
return nil, err
return nil, errors.Wrapf(err, "Cert path %s could not be read", certPath)
}
ok := certPool.AppendCertsFromPEM(certBytes)
if !ok {
Expand Down
29 changes: 17 additions & 12 deletions receiver/kubeletstatsreceiver/kubelet/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"net/http"
"os"

"github.com/pkg/errors"
"go.uber.org/zap"

"github.com/open-telemetry/opentelemetry-collector-contrib/internal/common/k8sconfig"
Expand Down Expand Up @@ -61,7 +62,7 @@ func newTLSClient(endpoint string, cfg *ClientConfig, logger *zap.Logger) (Clien
[]tls.Certificate{clientCert},
nil,
logger,
), nil
)
}

func newServiceAccountClient(endpoint string, caCertPath string, tokenPath string, logger *zap.Logger) (*clientImpl, error) {
Expand All @@ -77,7 +78,7 @@ func newServiceAccountClient(endpoint string, caCertPath string, tokenPath strin
tr.TLSClientConfig = &tls.Config{
RootCAs: rootCAs,
}
return defaultTLSClient(endpoint, true, rootCAs, nil, tok, logger), nil
return defaultTLSClient(endpoint, true, rootCAs, nil, tok, logger)
}

func defaultTLSClient(
Expand All @@ -87,35 +88,39 @@ func defaultTLSClient(
certificates []tls.Certificate,
tok []byte,
logger *zap.Logger,
) *clientImpl {
) (*clientImpl, error) {
tr := defaultTransport()
tr.TLSClientConfig = &tls.Config{
RootCAs: rootCAs,
Certificates: certificates,
InsecureSkipVerify: insecureSkipVerify,
}
if endpoint == "" {
endpoint = defaultEndpoint(logger)
var err error
endpoint, err = defaultEndpoint()
if err != nil {
return nil, err
}
logger.Warn("Kubelet endpoint not defined, using default endpoint " + endpoint)
}
return &clientImpl{
baseURL: "https://" + endpoint,
httpClient: http.Client{Transport: tr},
tok: tok,
logger: logger,
}
}, nil
}

func defaultEndpoint(logger *zap.Logger) (endpoint string) {
// This will work if hostNetwork is turned on, in which case the pod has access
// to the node's loopback device.
// https://kubernetes.io/docs/concepts/policy/pod-security-policy/#host-namespaces
func defaultEndpoint() (string, error) {
hostname, err := os.Hostname()
if err != nil {
logger.Error("unable to get hostname", zap.Error(err))
endpoint = "localhost"
} else {
endpoint = hostname
return "", errors.WithMessage(err, "Unable to get hostname for default endpoint")
}
const kubeletPort = "10250"
endpoint += ":" + kubeletPort
return endpoint
return hostname + ":" + kubeletPort, nil
}

func defaultTransport() *http.Transport {
Expand Down
9 changes: 6 additions & 3 deletions receiver/kubeletstatsreceiver/kubelet/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ func TestNewClient(t *testing.T) {

func TestDefaultTLSClient(t *testing.T) {
endpoint := "localhost:9876"
client := defaultTLSClient(endpoint, true, &x509.CertPool{}, nil, nil, zap.NewNop())
client, err := defaultTLSClient(endpoint, true, &x509.CertPool{}, nil, nil, zap.NewNop())
require.NoError(t, err)
require.NotNil(t, client.httpClient.Transport)
require.Equal(t, "https://"+endpoint, client.baseURL)
}
Expand All @@ -86,7 +87,8 @@ func TestSvcAcctClient(t *testing.T) {
}

func TestDefaultEndpoint(t *testing.T) {
endpt := defaultEndpoint(zap.NewNop())
endpt, err := defaultEndpoint()
require.NoError(t, err)
require.True(t, strings.HasSuffix(endpt, ":10250"))
}

Expand Down Expand Up @@ -124,7 +126,8 @@ func TestSABadTokenPath(t *testing.T) {
}

func TestTLSDefaultEndpoint(t *testing.T) {
client := defaultTLSClient("", true, nil, nil, nil, zap.NewNop())
client, err := defaultTLSClient("", true, nil, nil, nil, zap.NewNop())
require.NoError(t, err)
require.True(t, strings.HasPrefix(client.baseURL, "https://"))
require.True(t, strings.HasSuffix(client.baseURL, ":10250"))
}
Expand Down