4
4
package cert
5
5
6
6
import (
7
- "bytes"
8
7
"context"
9
- "crypto/tls"
10
- "encoding/json"
8
+ "errors"
11
9
"fmt"
12
- "io"
13
- "net/http"
14
10
15
- "github.com/hashicorp/go-rootcerts"
16
11
"github.com/hashicorp/vault/api"
17
12
)
18
13
14
+ const defaultMountPath = "cert"
15
+
19
16
type CertAuth struct {
20
- role string
21
- caCert string
22
- caCertBytes []byte
23
- clientCert string
24
- clientKey string
25
- insecureSkipVerify bool
17
+ role string
18
+ mountPath string
26
19
}
27
20
28
21
var _ api.AuthMethod = (* CertAuth )(nil )
29
22
30
23
type LoginOption func (a * CertAuth ) error
31
24
32
25
// NewCertAuth initializes a new Cert auth method interface to be
33
- // passed as a parameter to the client.Auth().Login method.
26
+ // passed as a parameter to the client.Auth().Login method. The client and other
27
+ // TLS configuration should be set up on the passed in client, you can use
28
+ // the NewCertAuthClient function to set up the client with the proper TLS client attributes.
34
29
//
35
- // Supported options: WithCACert, WithCACertBytes, WithInsecure
36
- func NewCertAuth (roleName , clientCert , clientKey string , opts ... LoginOption ) (* CertAuth , error ) {
37
- if roleName == "" {
38
- return nil , fmt .Errorf ("no role name provided for login" )
39
- }
40
-
41
- if clientCert == "" || clientKey == "" {
42
- return nil , fmt .Errorf ("client certificate and key must be provided" )
43
- }
44
-
45
- a := & CertAuth {
46
- role : roleName ,
47
- clientCert : clientCert ,
48
- clientKey : clientKey ,
49
- }
30
+ // Supported options: WithRole, WithMountPath
31
+ func NewCertAuth (opts ... LoginOption ) (* CertAuth , error ) {
32
+ a := & CertAuth {}
50
33
51
34
// Loop through each option
52
35
for _ , opt := range opts {
@@ -58,111 +41,112 @@ func NewCertAuth(roleName, clientCert, clientKey string, opts ...LoginOption) (*
58
41
}
59
42
}
60
43
44
+ if a .mountPath == "" {
45
+ a .mountPath = defaultMountPath
46
+ }
47
+
61
48
// return the modified auth struct instance
62
49
return a , nil
63
50
}
64
51
65
52
// Login sets up the required request body for the Cert auth method's /login
66
- // endpoint, and performs a write to it.
67
- // It adds the client cert and key to the request .
53
+ // endpoint, and performs a write to it. We assume the passed in client has the
54
+ // proper TLS client certificates set up .
68
55
func (a * CertAuth ) Login (ctx context.Context , client * api.Client ) (* api.Secret , error ) {
69
56
if ctx == nil {
70
57
ctx = context .Background ()
71
58
}
72
59
73
- c , err := a .httpClient ()
74
- if err != nil {
75
- return nil , err
60
+ if client == nil {
61
+ return nil , fmt .Errorf ("client is required for login with the associated client certs initialized" )
76
62
}
77
63
78
- reqBody , err := json .Marshal (map [string ]interface {}{
79
- "name" : a .role ,
80
- })
81
- if err != nil {
82
- return nil , fmt .Errorf ("unable to marshal login data: %w" , err )
64
+ loginData := make (map [string ]interface {})
65
+ if a .role != "" {
66
+ loginData ["name" ] = a .role
83
67
}
84
68
85
- url := fmt .Sprintf ("%s/v1/auth/cert/login" , client .Address ())
86
- req , err := http .NewRequestWithContext (ctx , http .MethodPost , url , bytes .NewBuffer (reqBody ))
87
- if err != nil {
88
- return nil , fmt .Errorf ("unable to create request: %w" , err )
89
- }
90
-
91
- resp , err := c .Do (req )
69
+ certAuthPath := fmt .Sprintf ("auth/%s/login" , a .mountPath )
70
+ resp , err := client .Logical ().WriteWithContext (ctx , certAuthPath , loginData )
92
71
if err != nil {
93
72
return nil , fmt .Errorf ("unable to log in with cert auth: %w" , err )
94
73
}
95
74
96
- defer resp .Body .Close ()
75
+ return resp , nil
76
+ }
97
77
98
- body , err := io .ReadAll (resp .Body )
99
- if err != nil {
100
- return nil , fmt .Errorf ("unable to read response body: %w" , err )
78
+ // WithRole specifies the role to use for the login request.
79
+ func WithRole (roleName string ) LoginOption {
80
+ return func (a * CertAuth ) error {
81
+ a .role = roleName
82
+ return nil
101
83
}
84
+ }
102
85
103
- if resp .StatusCode >= 400 {
104
- return nil , fmt .Errorf ("unable to log in with cert auth, response code: %d. response body: %s" , resp .StatusCode , string (body ))
86
+ // WithMountPath specifies the mount path to use for the login request.
87
+ func WithMountPath (mountPath string ) LoginOption {
88
+ return func (a * CertAuth ) error {
89
+ a .mountPath = mountPath
90
+ return nil
105
91
}
92
+ }
106
93
107
- var secret api.Secret
108
- if err := json .Unmarshal (body , & secret ); err != nil {
109
- return nil , fmt .Errorf ("unable to unmarshal response body: %w" , err )
94
+ // NewDefaultCertAuthClient initializes a new client with a default configuration
95
+ // with the provided address and TLS configuration. The TLSConfig must have the ClientCert and ClientKey
96
+ // fields set.
97
+ func NewDefaultCertAuthClient (address string , tlsConfig * api.TLSConfig ) (* api.Client , error ) {
98
+ if tlsConfig == nil {
99
+ return nil , errors .New ("tls config is required for cert auth client" )
110
100
}
111
101
112
- return & secret , nil
113
- }
102
+ if tlsConfig .ClientCert == "" || tlsConfig .ClientKey == "" {
103
+ return nil , errors .New ("client cert and key are required for cert auth client" )
104
+ }
114
105
115
- func (a * CertAuth ) httpClient () (* http.Client , error ) {
116
- cert , err := tls .LoadX509KeyPair (a .clientCert , a .clientKey )
117
- if err != nil {
118
- return nil , fmt .Errorf ("unable to load cert: %w" , err )
106
+ if len (address ) == 0 {
107
+ return nil , errors .New ("address is required for cert auth client" )
119
108
}
120
109
121
- tlsConfig := & tls.Config {
122
- InsecureSkipVerify : a .insecureSkipVerify ,
123
- Certificates : []tls.Certificate {cert },
110
+ cfg := api .DefaultConfig ()
111
+ cfg .Address = address
112
+ err := cfg .ConfigureTLS (tlsConfig )
113
+ if err != nil {
114
+ return nil , fmt .Errorf ("failed configuring TLS on client config: %w" , err )
124
115
}
125
116
126
- if a .caCert != "" || len (a .caCertBytes ) > 0 {
127
- err = rootcerts .ConfigureTLS (tlsConfig , & rootcerts.Config {
128
- CAPath : a .caCert ,
129
- CACertificate : a .caCertBytes ,
130
- })
131
- if err != nil {
132
- return nil , fmt .Errorf ("unable to configure TLS: %w" , err )
133
- }
117
+ client , err := api .NewClient (cfg )
118
+ if err != nil {
119
+ return nil , fmt .Errorf ("failed to create client: %v" , err )
134
120
}
135
121
136
- return & http.Client {
137
- Transport : & http.Transport {
138
- TLSClientConfig : tlsConfig ,
139
- },
140
- }, nil
122
+ return client , nil
141
123
}
142
124
143
- // WithCACert sets the CA cert to be used for the login request.
144
- // caCert is the path to the CA cert file.
145
- func WithCACert (caCert string ) LoginOption {
146
- return func (a * CertAuth ) error {
147
- a .caCert = caCert
148
- return nil
125
+ // NewCertAuthClient initializes a new client based on the passed in client
126
+ // with the provided TLS configuration. The TLSConfig must have the ClientCert and ClientKey
127
+ // fields set.
128
+ func NewCertAuthClient (c * api.Client , config * api.TLSConfig ) (* api.Client , error ) {
129
+ if c == nil {
130
+ return nil , errors .New ("base client is required for cert auth client" )
131
+ }
132
+ if config == nil {
133
+ return nil , errors .New ("tls config is required for cert auth client" )
149
134
}
150
- }
151
135
152
- // WithCACertBytes sets the CA cert to be used for the login request.
153
- // caCertBytes is the bytes of the CA cert.
154
- // caCertBytes takes precedence over caCert.
155
- func WithCACertBytes (caCertBytes []byte ) LoginOption {
156
- return func (a * CertAuth ) error {
157
- a .caCertBytes = caCertBytes
158
- return nil
136
+ if config .ClientCert == "" || config .ClientKey == "" {
137
+ return nil , errors .New ("client cert and key are required for cert auth client" )
159
138
}
160
- }
161
139
162
- // WithInsecure skips the verification of the server's certificate chain and host name.
163
- func WithInsecure () LoginOption {
164
- return func (a * CertAuth ) error {
165
- a .insecureSkipVerify = true
166
- return nil
140
+ conf := c .CloneConfig ()
141
+ err := conf .ConfigureTLS (config )
142
+ if err != nil {
143
+ return nil , fmt .Errorf ("failed to configure TLS on client: %w" , err )
167
144
}
145
+
146
+ client , err := api .NewClient (conf )
147
+ if err != nil {
148
+ return nil , fmt .Errorf ("failed to create client: %v" , err )
149
+ }
150
+
151
+ return client , nil
168
152
}
0 commit comments