forked from cloudfoundry/cli
/
wrap_for_cf_on_k8s.go
124 lines (101 loc) · 3.19 KB
/
wrap_for_cf_on_k8s.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package shared
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/pem"
"errors"
"fmt"
"net/http"
"k8s.io/client-go/rest"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/tools/clientcmd/api"
"k8s.io/client-go/transport"
"github.com/LukasHeimann/cloudfoundrycli/v8/actor/v7action"
"github.com/LukasHeimann/cloudfoundrycli/v8/command"
// imported for the side effects
_ "k8s.io/client-go/plugin/pkg/client/auth/azure"
_ "k8s.io/client-go/plugin/pkg/client/auth/gcp"
_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
)
//go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 net/http.RoundTripper
func WrapForCFOnK8sAuth(config command.Config, k8sConfigGetter v7action.KubernetesConfigGetter, roundTripper http.RoundTripper) (http.RoundTripper, error) {
username, err := config.CurrentUserName()
if err != nil {
return nil, err
}
if username == "" {
return nil, errors.New("current user not set")
}
k8sConfig, err := k8sConfigGetter.Get()
if err != nil {
return nil, err
}
restConfig, err := clientcmd.NewDefaultClientConfig(
*k8sConfig,
&clientcmd.ConfigOverrides{
Context: api.Context{AuthInfo: username},
},
).ClientConfig()
if err != nil {
return nil, err
}
// Special case for certs, since we don't want mtls
cert, err := getCert(restConfig)
if err != nil {
return nil, err
}
transportConfig, err := restConfig.TransportConfig()
if err != nil {
return nil, fmt.Errorf("failed to get transport config: %w", err)
}
if cert != nil {
return certRoundTripper{
cert: cert,
roundTripper: roundTripper,
}, nil
}
if transportConfig.WrapTransport == nil {
// i.e. not auth-provider or exec plugin
return transport.HTTPWrappersForConfig(transportConfig, roundTripper)
}
// using auth provider to generate token
return transportConfig.WrapTransport(roundTripper), nil
}
func getCert(restConfig *rest.Config) (*tls.Certificate, error) {
tlsConfig, err := rest.TLSConfigFor(restConfig)
if err != nil {
return nil, fmt.Errorf("failed to get tls config: %w", err)
}
if tlsConfig != nil && tlsConfig.GetClientCertificate != nil {
cert, err := tlsConfig.GetClientCertificate(nil)
if err != nil {
return nil, fmt.Errorf("failed to get client certificate: %w", err)
}
if len(cert.Certificate) > 0 && cert.PrivateKey != nil {
return cert, nil
}
}
return nil, nil
}
type certRoundTripper struct {
cert *tls.Certificate
roundTripper http.RoundTripper
}
func (rt certRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
var buf bytes.Buffer
if err := pem.Encode(&buf, &pem.Block{Type: "CERTIFICATE", Bytes: rt.cert.Certificate[0]}); err != nil {
return nil, fmt.Errorf("could not convert certificate to PEM format: %w", err)
}
key, err := x509.MarshalPKCS8PrivateKey(rt.cert.PrivateKey)
if err != nil {
return nil, fmt.Errorf("could not marshal private key: %w", err)
}
if err := pem.Encode(&buf, &pem.Block{Type: "PRIVATE KEY", Bytes: key}); err != nil {
return nil, fmt.Errorf("could not convert key to PEM format: %w", err)
}
auth := "ClientCert " + base64.StdEncoding.EncodeToString(buf.Bytes())
req.Header.Set("Authorization", auth)
return rt.roundTripper.RoundTrip(req)
}