1package client
2
3import (
4	"bytes"
5	"crypto/x509"
6	"io/ioutil"
7	"net"
8	"os"
9
10	"github.com/hashicorp/vault/sdk/helper/certutil"
11)
12
13const (
14	// These environment variables aren't set by default.
15	// Vault may read them in if set through these environment variables.
16	// Example here:
17	// https://kubernetes.io/docs/tasks/inject-data-application/environment-variable-expose-pod-information/
18	// The client itself does nothing directly with these variables, it's
19	// up to the caller. However, they live here so they'll be consistently
20	// named should the client ever be reused.
21	// We generally recommend preferring environmental settings over configured
22	// ones, allowing settings from the Downward API to override hard-coded
23	// ones.
24	EnvVarKubernetesNamespace = "VAULT_K8S_NAMESPACE"
25	EnvVarKubernetesPodName   = "VAULT_K8S_POD_NAME"
26
27	// The service host and port environment variables are
28	// set by default inside a Kubernetes environment.
29	EnvVarKubernetesServiceHost = "KUBERNETES_SERVICE_HOST"
30	EnvVarKubernetesServicePort = "KUBERNETES_SERVICE_PORT"
31)
32
33var (
34	// These are presented as variables so they can be updated
35	// to point at test fixtures if needed. They aren't passed
36	// into inClusterConfig to avoid dependency injection.
37	Scheme     = "https://"
38	TokenFile  = "/var/run/secrets/kubernetes.io/serviceaccount/token"
39	RootCAFile = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
40)
41
42// inClusterConfig returns a config object which uses the service account
43// kubernetes gives to services. It's intended for clients that expect to be
44// running inside a service running on kubernetes. It will return ErrNotInCluster
45// if called from a process not running in a kubernetes environment.
46// inClusterConfig is based on this:
47// https://github.com/kubernetes/client-go/blob/a56922badea0f2a91771411eaa1173c9e9243908/rest/config.go#L451
48func inClusterConfig() (*Config, error) {
49	host, port := os.Getenv(EnvVarKubernetesServiceHost), os.Getenv(EnvVarKubernetesServicePort)
50	if len(host) == 0 || len(port) == 0 {
51		return nil, ErrNotInCluster
52	}
53
54	token, err := ioutil.ReadFile(TokenFile)
55	if err != nil {
56		return nil, err
57	}
58
59	caBytes, err := ioutil.ReadFile(RootCAFile)
60	if err != nil {
61		return nil, err
62	}
63	pool, err := certutil.NewCertPool(bytes.NewReader(caBytes))
64	if err != nil {
65		return nil, err
66	}
67	return &Config{
68		Host:            Scheme + net.JoinHostPort(host, port),
69		CACertPool:      pool,
70		BearerToken:     string(token),
71		BearerTokenFile: TokenFile,
72	}, nil
73}
74
75// This config is based on the one returned here:
76// https://github.com/kubernetes/client-go/blob/a56922badea0f2a91771411eaa1173c9e9243908/rest/config.go#L451
77// It is pared down to the absolute minimum fields used by this code.
78// The CACertPool is promoted to the top level from being originally on the TLSClientConfig
79// because it is the only parameter of the TLSClientConfig used by this code.
80// Also, it made more sense to simply reuse the pool rather than holding raw values
81// and parsing it repeatedly.
82type Config struct {
83	CACertPool *x509.CertPool
84
85	// Host must be a host string, a host:port pair, or a URL to the base of the apiserver.
86	// If a URL is given then the (optional) Path of that URL represents a prefix that must
87	// be appended to all request URIs used to access the apiserver. This allows a frontend
88	// proxy to easily relocate all of the apiserver endpoints.
89	Host string
90
91	// Server requires Bearer authentication. This client will not attempt to use
92	// refresh tokens for an OAuth2 flow.
93	BearerToken string
94
95	// Path to a file containing a BearerToken.
96	// If set, checks for a new token in the case of authorization errors.
97	BearerTokenFile string
98}
99