@@ -17,19 +17,23 @@ limitations under the License.
17
17
package main
18
18
19
19
import (
20
+ "bufio"
21
+ "context"
20
22
"crypto/tls"
21
23
"crypto/x509"
22
24
"flag"
23
25
"fmt"
24
- "io"
25
26
"io/ioutil"
27
+ "net"
28
+ "net/http"
26
29
"os"
27
30
28
31
"github.com/spf13/cobra"
29
32
"github.com/spf13/pflag"
30
33
"google.golang.org/grpc"
31
34
"google.golang.org/grpc/credentials"
32
35
"k8s.io/klog"
36
+
33
37
"sigs.k8s.io/apiserver-network-proxy/pkg/agent/client"
34
38
"sigs.k8s.io/apiserver-network-proxy/pkg/util"
35
39
)
@@ -54,23 +58,45 @@ func main() {
54
58
}
55
59
56
60
type GrpcProxyClientOptions struct {
57
- clientCert string
58
- clientKey string
59
- caCert string
61
+ clientCert string
62
+ clientKey string
63
+ caCert string
64
+ requestProto string
65
+ requestPath string
66
+ requestHost string
67
+ requestPort int
68
+ proxyHost string
69
+ proxyPort int
70
+ mode string
71
+
60
72
}
61
73
62
74
func (o * GrpcProxyClientOptions ) Flags () * pflag.FlagSet {
63
75
flags := pflag .NewFlagSet ("proxy" , pflag .ContinueOnError )
64
76
flags .StringVar (& o .clientCert , "clientCert" , o .clientCert , "If non-empty secure communication with this cert." )
65
77
flags .StringVar (& o .clientKey , "clientKey" , o .clientKey , "If non-empty secure communication with this key." )
66
78
flags .StringVar (& o .caCert , "caCert" , o .caCert , "If non-empty the CAs we use to validate clients." )
79
+ flags .StringVar (& o .requestProto , "requestProto" , o .requestProto , "The protocol for the request to send through the proxy." )
80
+ flags .StringVar (& o .requestPath , "requestPath" , o .requestPath , "The url request to send through the proxy." )
81
+ flags .StringVar (& o .requestHost , "requestHost" , o .requestHost , "The host of the request server." )
82
+ flags .IntVar (& o .requestPort , "requestPort" , o .requestPort , "The port the request server is listening on." )
83
+ flags .StringVar (& o .proxyHost , "proxyHost" , o .proxyHost , "The host of the proxy server." )
84
+ flags .IntVar (& o .proxyPort , "proxyPort" , o .proxyPort , "The port the proxy server is listening on." )
85
+ flags .StringVar (& o .mode , "mode" , o .mode , "Mode can be either 'grpc' or 'http-connect'." )
67
86
return flags
68
87
}
69
88
70
89
func (o * GrpcProxyClientOptions ) Print () {
71
- klog .Warningf ("ClientCert set to \" %s\" .\n " , o .clientCert )
72
- klog .Warningf ("ClientKey set to \" %s\" .\n " , o .clientKey )
73
- klog .Warningf ("CACert set to \" %s\" .\n " , o .caCert )
90
+ klog .Warningf ("ClientCert set to %q.\n " , o .clientCert )
91
+ klog .Warningf ("ClientKey set to %q.\n " , o .clientKey )
92
+ klog .Warningf ("CACert set to %q.\n " , o .caCert )
93
+ klog .Warningf ("RequestProto set to %q.\n " , o .requestProto )
94
+ klog .Warningf ("RequestPath set to %q.\n " , o .requestPath )
95
+ klog .Warningf ("RequestHost set to %q.\n " , o .requestHost )
96
+ klog .Warningf ("RequestPort set to %d.\n " , o .requestPort )
97
+ klog .Warningf ("ProxyHost set to %q.\n " , o .proxyHost )
98
+ klog .Warningf ("ProxyPort set to %d.\n " , o .proxyPort )
99
+ klog .Warningf ("Mode set to %q.\n " , o .mode )
74
100
}
75
101
76
102
func (o * GrpcProxyClientOptions ) Validate () error {
@@ -79,30 +105,55 @@ func (o *GrpcProxyClientOptions) Validate() error {
79
105
return err
80
106
}
81
107
if o .clientCert == "" {
82
- return fmt .Errorf ("cannot have client cert empty when client key is set to \" %s \" " , o .clientKey )
108
+ return fmt .Errorf ("cannot have client cert empty when client key is set to %q " , o .clientKey )
83
109
}
84
110
}
85
111
if o .clientCert != "" {
86
112
if _ , err := os .Stat (o .clientCert ); os .IsNotExist (err ) {
87
113
return err
88
114
}
89
115
if o .clientKey == "" {
90
- return fmt .Errorf ("cannot have client key empty when client cert is set to \" %s \" " , o .clientCert )
116
+ return fmt .Errorf ("cannot have client key empty when client cert is set to %q " , o .clientCert )
91
117
}
92
118
}
93
119
if o .caCert != "" {
94
120
if _ , err := os .Stat (o .caCert ); os .IsNotExist (err ) {
95
121
return err
96
122
}
97
123
}
124
+ if o .requestProto != "http" && o .requestProto != "https" {
125
+ return fmt .Errorf ("request protocol must be set to either 'http' or 'https' not %q" , o .requestProto )
126
+ }
127
+ if o .mode != "grpc" && o .mode != "http-connect" {
128
+ return fmt .Errorf ("mode must be set to either 'grpc' or 'http-connect' not %q" , o .mode )
129
+ }
130
+ if o .requestPort > 49151 {
131
+ return fmt .Errorf ("please do not try to use ephemeral port %d for the request server port" , o .requestPort )
132
+ }
133
+ if o .requestPort < 1024 {
134
+ return fmt .Errorf ("please do not try to use reserved port %d for the request server port" , o .requestPort )
135
+ }
136
+ if o .proxyPort > 49151 {
137
+ return fmt .Errorf ("please do not try to use ephemeral port %d for the proxy server port" , o .proxyPort )
138
+ }
139
+ if o .proxyPort < 1024 {
140
+ return fmt .Errorf ("please do not try to use reserved port %d for the proxy server port" , o .proxyPort )
141
+ }
98
142
return nil
99
143
}
100
144
101
145
func newGrpcProxyClientOptions () * GrpcProxyClientOptions {
102
146
o := GrpcProxyClientOptions {
103
- clientCert : "" ,
104
- clientKey : "" ,
105
- caCert : "" ,
147
+ clientCert : "" ,
148
+ clientKey : "" ,
149
+ caCert : "" ,
150
+ requestProto : "http" ,
151
+ requestPath : "/" ,
152
+ requestHost : "localhost" ,
153
+ requestPort : 8000 ,
154
+ proxyHost : "localhost" ,
155
+ proxyPort : 8090 ,
156
+ mode : "grpc" ,
106
157
}
107
158
return & o
108
159
}
@@ -125,24 +176,54 @@ type Client struct {
125
176
func (c * Client ) run (o * GrpcProxyClientOptions ) error {
126
177
o .Print ()
127
178
if err := o .Validate (); err != nil {
128
- return err
179
+ return fmt . Errorf ( "failed to validate proxy client options, got %v" , err )
129
180
}
130
181
131
182
// Run remote simple http service on server side as
132
183
// "python -m SimpleHTTPServer"
133
184
185
+
186
+ dialer , err := c .getDialer (o )
187
+ if err != nil {
188
+ return fmt .Errorf ("failed to get dialer for client, got %v" , err )
189
+ }
190
+ transport := & http.Transport {
191
+ DialContext : dialer ,
192
+ }
193
+ client := & http.Client {
194
+ Transport : transport ,
195
+ }
196
+ requestURL := fmt .Sprintf ("%s://%s:%d%s" , o .requestProto , o .requestHost , o .requestPort , o .requestPath )
197
+ request , err := http .NewRequest ("GET" , requestURL , nil )
198
+ if err != nil {
199
+ return fmt .Errorf ("failed to create request %s to send, got %v" , requestURL , err )
200
+ }
201
+ response , err := client .Do (request )
202
+ if err != nil {
203
+ return fmt .Errorf ("failed to send request to client, got %v" , err )
204
+ }
205
+ data , err := ioutil .ReadAll (response .Body )
206
+ if err != nil {
207
+ return fmt .Errorf ("failed to read response from client, got %v" , err )
208
+ }
209
+ klog .Info (string (data ))
210
+
211
+ return nil
212
+ }
213
+
214
+ func (c * Client ) getDialer (o * GrpcProxyClientOptions ) (func (ctx context.Context , network , addr string ) (net.Conn , error ), error ) {
134
215
clientCert , err := tls .LoadX509KeyPair (o .clientCert , o .clientKey )
135
216
if err != nil {
136
- return err
217
+ return nil , fmt . Errorf ( "failed to read key pair %s & %s, got %v" , o . clientCert , o . clientKey , err )
137
218
}
138
219
certPool := x509 .NewCertPool ()
139
220
caCert , err := ioutil .ReadFile (o .caCert )
140
221
if err != nil {
141
- return err
222
+ return nil , fmt . Errorf ( "failed to read cert file %s, got %v" , o . caCert , err )
142
223
}
143
224
ok := certPool .AppendCertsFromPEM (caCert )
144
225
if ! ok {
145
- return fmt .Errorf ("failed to append CA cert to the cert pool" )
226
+ return nil , fmt .Errorf ("failed to append CA cert to the cert pool" )
146
227
}
147
228
148
229
transportCreds := credentials .NewTLS (& tls.Config {
@@ -151,33 +232,59 @@ func (c *Client) run(o *GrpcProxyClientOptions) error {
151
232
RootCAs : certPool ,
152
233
})
153
234
154
- dialOption := grpc .WithTransportCredentials (transportCreds )
155
- tunnel , err := client .CreateGrpcTunnel ("localhost:8090" , dialOption )
156
- if err != nil {
157
- return err
158
- }
159
-
160
- conn , err := tunnel .Dial ("tcp" , "localhost:8000" )
161
- if err != nil {
162
- return err
163
- }
164
-
165
- _ , err = conn .Write ([]byte ("GET / HTTP/1.1\r \n Host: localhost\r \n \r \n " ))
166
- if err != nil {
167
- return err
168
- }
235
+ var proxyConn net.Conn
236
+ switch o .mode {
237
+ case "grpc" :
238
+ dialOption := grpc .WithTransportCredentials (transportCreds )
239
+ serverAddress := fmt .Sprintf ("%s:%d" , o .proxyHost , o .proxyPort )
240
+ tunnel , err := client .CreateGrpcTunnel (serverAddress , dialOption )
241
+ if err != nil {
242
+ return nil , fmt .Errorf ("failed to create tunnel %s, got %v" , serverAddress , err )
243
+ }
169
244
170
- var buf [1 << 12 ]byte
245
+ requestAddress := fmt .Sprintf ("%s:%d" , o .requestHost , o .requestPort )
246
+ proxyConn , err = tunnel .Dial ("tcp" , requestAddress )
247
+ if err != nil {
248
+ return nil , fmt .Errorf ("failed to dial request %s, got %v" , requestAddress , err )
249
+ }
250
+ case "http-connect" :
251
+ proxyAddress := fmt .Sprintf ("%s:%d" , o .proxyHost , o .proxyPort )
252
+ requestAddress := fmt .Sprintf ("%s:%d" , o .requestHost , o .requestPort )
171
253
172
- for {
173
- n , err := conn .Read (buf [:])
174
- if err == io .EOF {
175
- break
254
+ proxyConn , err = tls .Dial ("tcp" , proxyAddress ,
255
+ & tls.Config {
256
+ ServerName : o .proxyHost ,
257
+ Certificates : []tls.Certificate {clientCert },
258
+ RootCAs : certPool ,
259
+ },
260
+ )
261
+ if err != nil {
262
+ return nil , fmt .Errorf ("dialing proxy %q failed: %v" , proxyAddress , err )
176
263
}
264
+ fmt .Fprintf (proxyConn , "CONNECT %s HTTP/1.1\r \n Host: %s\r \n \r \n " , requestAddress , "127.0.0.1" )
265
+ br := bufio .NewReader (proxyConn )
266
+ res , err := http .ReadResponse (br , nil )
177
267
if err != nil {
178
- return err
268
+ return nil , fmt .Errorf ("reading HTTP response from CONNECT to %s via proxy %s failed: %v" ,
269
+ requestAddress , proxyAddress , err )
179
270
}
180
- klog .Info (string (buf [:n ]))
271
+ if res .StatusCode != 200 {
272
+ return nil , fmt .Errorf ("proxy error from %s while dialing %s: %v" , proxyAddress , requestAddress , res .Status )
273
+ }
274
+
275
+ // It's safe to discard the bufio.Reader here and return the
276
+ // original TCP conn directly because we only use this for
277
+ // TLS, and in TLS the client speaks first, so we know there's
278
+ // no unbuffered data. But we can double-check.
279
+ if br .Buffered () > 0 {
280
+ return nil , fmt .Errorf ("unexpected %d bytes of buffered data from CONNECT proxy %q" ,
281
+ br .Buffered (), proxyAddress )
282
+ }
283
+ default :
284
+ return nil , fmt .Errorf ("failed to process mode %s" , o .mode )
181
285
}
182
- return nil
286
+
287
+ return func (ctx context.Context , network , addr string ) (net.Conn , error ) {
288
+ return proxyConn , nil
289
+ }, nil
183
290
}
0 commit comments