File: dial.go

package info (click to toggle)
golang-github-spiffe-go-spiffe 2.5.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 1,116 kB
  • sloc: makefile: 157
file content (106 lines) | stat: -rw-r--r-- 3,076 bytes parent folder | download | duplicates (2)
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
package spiffetls

import (
	"context"
	"crypto/tls"
	"io"
	"net"

	"github.com/spiffe/go-spiffe/v2/spiffeid"
	"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
	"github.com/spiffe/go-spiffe/v2/workloadapi"
	"github.com/zeebo/errs"
)

// Dial creates an mTLS connection using an X509-SVID obtained from the
// Workload API. The server is authenticated using X.509 bundles also obtained
// from the Workload API. The server is authorized using the given authorizer.
//
// This is the same as DialWithMode using the MTLSClient mode.
func Dial(ctx context.Context, network, addr string, authorizer tlsconfig.Authorizer, options ...DialOption) (net.Conn, error) {
	return DialWithMode(ctx, network, addr, MTLSClient(authorizer), options...)
}

// DialWithMode creates a TLS connection using the specified mode.
func DialWithMode(ctx context.Context, network, addr string, mode DialMode, options ...DialOption) (_ net.Conn, err error) {
	m := mode.get()

	var sourceCloser io.Closer
	if !m.sourceUnneeded {
		source := m.source
		if source == nil {
			source, err = workloadapi.NewX509Source(ctx, m.options...)
			if err != nil {
				return nil, spiffetlsErr.New("cannot create X.509 source: %w", err)
			}
			// Close source if there is a failure after this point
			defer func() {
				if err != nil {
					source.Close()
				}
			}()
			sourceCloser = source
		}
		m.bundle = source
		m.svid = source
	}

	opt := &dialConfig{}
	for _, option := range options {
		option.apply(opt)
	}

	tlsConfig := &tls.Config{MinVersion: tls.VersionTLS12} // MinVersion is also set by the Hook methods, but just in case...
	if opt.baseTLSConf != nil {
		tlsConfig = opt.baseTLSConf
	}

	switch m.mode {
	case tlsClientMode:
		tlsconfig.HookTLSClientConfig(tlsConfig, m.bundle, m.authorizer)
	case mtlsClientMode:
		tlsconfig.HookMTLSClientConfig(tlsConfig, m.svid, m.bundle, m.authorizer, opt.tlsOptions...)
	case mtlsWebClientMode:
		tlsconfig.HookMTLSWebClientConfig(tlsConfig, m.svid, m.roots, opt.tlsOptions...)
	default:
		return nil, spiffetlsErr.New("unknown client mode: %v", m.mode)
	}

	var conn *tls.Conn
	if opt.dialer != nil {
		conn, err = tls.DialWithDialer(opt.dialer, network, addr, tlsConfig)
	} else {
		conn, err = tls.Dial(network, addr, tlsConfig)
	}
	if err != nil {
		return nil, spiffetlsErr.New("unable to dial: %w", err)
	}

	return &clientConn{
		Conn:         conn,
		sourceCloser: sourceCloser,
	}, nil
}

type clientConn struct {
	*tls.Conn
	sourceCloser io.Closer
}

func (c *clientConn) Close() error {
	var group errs.Group
	if c.sourceCloser != nil {
		group.Add(c.sourceCloser.Close())
	}
	if err := c.Conn.Close(); err != nil {
		group.Add(spiffetlsErr.New("unable to close TLS connection: %w", err))
	}
	return group.Err()
}

// PeerID returns the peer SPIFFE ID on the connection. The handshake must have
// been completed. Note that in Go's TLS stack, the TLS 1.3 handshake may not
// complete until the first read from the connection.
func (c *clientConn) PeerID() (spiffeid.ID, error) {
	return PeerIDFromConnectionState(c.Conn.ConnectionState())
}