/
tls.go
126 lines (108 loc) · 3.43 KB
/
tls.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
125
126
// Copyright Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package forwarder
import (
"context"
"crypto/tls"
"encoding/pem"
"fmt"
"strings"
"time"
"istio.io/istio/pkg/hbone"
"istio.io/istio/pkg/test/echo"
"istio.io/istio/pkg/test/echo/proto"
)
var _ protocol = &tlsProtocol{}
type tlsProtocol struct {
e *executor
}
func newTLSProtocol(e *executor) protocol {
return &tlsProtocol{e: e}
}
func (c *tlsProtocol) ForwardEcho(ctx context.Context, cfg *Config) (*proto.ForwardEchoResponse, error) {
return doForward(ctx, cfg, c.e, c.makeRequest)
}
func (c *tlsProtocol) makeRequest(ctx context.Context, cfg *Config, requestID int) (string, error) {
conn, err := newTLSConnection(cfg)
if err != nil {
return "", err
}
defer func() { _ = conn.Close() }()
msgBuilder := strings.Builder{}
echo.ForwarderURLField.WriteForRequest(&msgBuilder, requestID, cfg.Request.Url)
// Apply per-request timeout to calculate deadline for reads/writes.
ctx, cancel := context.WithTimeout(ctx, cfg.timeout)
defer cancel()
// Apply the deadline to the connection.
deadline, _ := ctx.Deadline()
if err := conn.SetWriteDeadline(deadline); err != nil {
return msgBuilder.String(), err
}
if err := conn.SetReadDeadline(deadline); err != nil {
return msgBuilder.String(), err
}
if err := conn.HandshakeContext(ctx); err != nil {
return "", err
}
// Make sure the client writes something to the buffer
message := "HelloWorld"
if cfg.Request.Message != "" {
message = cfg.Request.Message
}
start := time.Now()
if _, err := conn.Write([]byte(message + "\n")); err != nil {
fwLog.Warnf("TCP write failed: %v", err)
return msgBuilder.String(), err
}
cs := conn.ConnectionState()
echo.LatencyField.WriteForRequest(&msgBuilder, requestID, fmt.Sprintf("%v", time.Since(start)))
echo.CipherField.WriteForRequest(&msgBuilder, requestID, tls.CipherSuiteName(cs.CipherSuite))
echo.TLSVersionField.WriteForRequest(&msgBuilder, requestID, versionName(cs.Version))
echo.TLSServerName.WriteForRequest(&msgBuilder, requestID, cs.ServerName)
echo.AlpnField.WriteForRequest(&msgBuilder, requestID, cs.NegotiatedProtocol)
for n, i := range cs.PeerCertificates {
pemBlock := pem.Block{
Type: "CERTIFICATE",
Bytes: i.Raw,
}
echo.WriteBodyLine(&msgBuilder, requestID, fmt.Sprintf("Response%d=%q", n, string(pem.EncodeToMemory(&pemBlock))))
}
msg := msgBuilder.String()
return msg, nil
}
func versionName(v uint16) string {
switch v {
case tls.VersionTLS10:
return "1.0"
case tls.VersionTLS11:
return "1.1"
case tls.VersionTLS12:
return "1.2"
case tls.VersionTLS13:
return "1.3"
default:
return fmt.Sprintf("unknown-%v", v)
}
}
func (c *tlsProtocol) Close() error {
return nil
}
func newTLSConnection(cfg *Config) (*tls.Conn, error) {
address := cfg.Request.Url[len(cfg.scheme+"://"):]
con, err := hbone.TLSDialWithDialer(newDialer(cfg), "tcp", address, cfg.tlsConfig)
if err != nil {
return nil, err
}
return con, nil
}