Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit dabffea

Browse files
committedMay 26, 2022
Support of SSL protocol
The patch adds support for using SSL to encrypt the client-server communications [1]. It uses a wrapper around the OpenSSL library for full compatibility with Tarantool Enterprise (GOST cryptographic algorithms [2] are not supported by Golang's crypto/tls). The feature can be disabled using a build tag [3] 'go_tarantool_ssl_disable'. 1. https://www.tarantool.io/en/enterprise_doc/security/#enterprise-iproto-encryption 2. https://github.com/gost-engine/engine 3. https://pkg.go.dev/go/build#hdr-Build_Constraints Closes #155
1 parent d44ffa0 commit dabffea

18 files changed

+939
-11
lines changed
 

‎.github/workflows/testing.yml

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ on:
66
workflow_dispatch:
77

88
jobs:
9-
linux:
9+
run-tests-ce:
1010
# We want to run on external PRs, but not on our own internal
1111
# PRs as they'll be run by the push to the branch.
1212
#
@@ -26,9 +26,6 @@ jobs:
2626
- '2.9'
2727
- '2.x-latest'
2828
coveralls: [false]
29-
include:
30-
- tarantool: '2.x-latest'
31-
coveralls: true
3229

3330
steps:
3431
- name: Clone the connector
@@ -66,3 +63,60 @@ jobs:
6663
6764
- name: Check workability of benchmark tests
6865
run: make bench-deps bench DURATION=1x COUNT=1
66+
run-tests-ee:
67+
if: github.event_name == 'push' ||
68+
github.event.pull_request.head.repo.full_name != github.repository
69+
70+
runs-on: ubuntu-latest
71+
72+
strategy:
73+
fail-fast: false
74+
matrix:
75+
sdk-version:
76+
- '1.10.11-0-gf0b0e7ecf-r470'
77+
- '2.8.3-21-g7d35cd2be-r470'
78+
coveralls: [false]
79+
ssl: [false]
80+
include:
81+
- sdk-version: '2.10.0-beta2-59-gc51e2ba67-r469'
82+
coveralls: true
83+
ssl: true
84+
85+
steps:
86+
- name: Clone the connector
87+
uses: actions/checkout@v2
88+
89+
- name: Setup Tarantool ${{ matrix.sdk-version }}
90+
run: |
91+
ARCHIVE_NAME=tarantool-enterprise-bundle-${{ matrix.sdk-version }}.tar.gz
92+
curl -O -L https://${{ secrets.SDK_DOWNLOAD_TOKEN }}@download.tarantool.io/enterprise/${ARCHIVE_NAME}
93+
tar -xzf ${ARCHIVE_NAME}
94+
rm -f ${ARCHIVE_NAME}
95+
96+
- name: Setup golang for the connector and tests
97+
uses: actions/setup-go@v2
98+
with:
99+
go-version: 1.13
100+
101+
- name: Install test dependencies
102+
run: |
103+
source tarantool-enterprise/env.sh
104+
make deps
105+
106+
- name: Run tests
107+
run: |
108+
source tarantool-enterprise/env.sh
109+
make test
110+
env:
111+
TEST_TNT_SSL: ${{matrix.ssl}}
112+
113+
- name: Run tests, collect code coverage data and send to Coveralls
114+
if: ${{ matrix.coveralls }}
115+
env:
116+
COVERALLS_TOKEN: ${{ secrets.GITHUB_TOKEN }}
117+
run: |
118+
source tarantool-enterprise/env.sh
119+
make coveralls
120+
121+
- name: Check workability of benchmark tests
122+
run: make bench-deps bench DURATION=1x COUNT=1

‎CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1818
- queue-utube handling (#85)
1919
- Master discovery (#113)
2020
- SQL support (#62)
21+
- SSL support (#155)
2122

2223
### Fixed
2324

‎CONTRIBUTING.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ make test
2929
The tests set up all required `tarantool` processes before run and clean up
3030
afterwards.
3131

32+
If you have Tarantool Enterprise Edition 2.10 or newer, you can run additional
33+
SSL tests. To do this, you need to set an environment variable 'TEST_TNT_SSL':
34+
35+
```bash
36+
TEST_TNT_SSL=true make test
37+
```
38+
3239
If you want to run the tests for a specific package:
3340
```bash
3441
make test-<SUBDIR>

‎Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ clean:
2323
deps: clean
2424
( cd ./queue; tarantoolctl rocks install queue 1.1.0 )
2525

26+
.PHONY: testdata
27+
testdata:
28+
(cd ./testdata; ./generate.sh)
29+
2630
.PHONY: test
2731
test:
2832
go test ./... -v -p 1

‎connection.go

Lines changed: 44 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ const (
2525
connClosed = 2
2626
)
2727

28+
const (
29+
connTransportNone = ""
30+
connTransportSsl = "ssl"
31+
)
32+
2833
type ConnEventKind int
2934
type ConnLogKind int
3035

@@ -207,6 +212,32 @@ type Opts struct {
207212
Handle interface{}
208213
// Logger is user specified logger used for error messages.
209214
Logger Logger
215+
// Transport is the connection type, by default the connection is unencrypted.
216+
Transport string
217+
// SslOpts is used only if the Transport == 'ssl' is set.
218+
Ssl SslOpts
219+
}
220+
221+
// SslOpts is a way to configure ssl transport.
222+
type SslOpts struct {
223+
// KeyFile is a path to a private SSL key file.
224+
KeyFile string
225+
// CertFile is a path to an SSL sertificate file.
226+
CertFile string
227+
// CaFile is a path to a trusted certificate authorities (CA) file.
228+
CaFile string
229+
// Ciphers is a colon-separated (:) list of SSL cipher suites the connection
230+
// can use.
231+
//
232+
// We don't provide a list of supported ciphers. This is what OpenSSL
233+
// does. The only limitation is usage of TLSv1.2 (because other protocol
234+
// versions don't seem to support the GOST cipher). To add additional
235+
// ciphers (GOST cipher), you must configure OpenSSL.
236+
//
237+
// See also
238+
//
239+
// * https://www.openssl.org/docs/man1.1.1/man1/ciphers.html
240+
Ciphers string
210241
}
211242

212243
// Connect creates and configures a new Connection.
@@ -358,8 +389,10 @@ func (conn *Connection) Handle() interface{} {
358389
func (conn *Connection) dial() (err error) {
359390
var connection net.Conn
360391
network := "tcp"
392+
opts := conn.opts
361393
address := conn.addr
362-
timeout := conn.opts.Reconnect / 2
394+
timeout := opts.Reconnect / 2
395+
transport := opts.Transport
363396
if timeout == 0 {
364397
timeout = 500 * time.Millisecond
365398
} else if timeout > 5*time.Second {
@@ -383,11 +416,17 @@ func (conn *Connection) dial() (err error) {
383416
} else if addrLen >= 4 && address[0:4] == "tcp:" {
384417
address = address[4:]
385418
}
386-
connection, err = net.DialTimeout(network, address, timeout)
419+
if transport == connTransportNone {
420+
connection, err = net.DialTimeout(network, address, timeout)
421+
} else if transport == connTransportSsl {
422+
connection, err = sslDialTimeout(network, address, timeout, opts.Ssl)
423+
} else {
424+
err = errors.New("An unsupported transport type: " + transport)
425+
}
387426
if err != nil {
388427
return
389428
}
390-
dc := &DeadlineIO{to: conn.opts.Timeout, c: connection}
429+
dc := &DeadlineIO{to: opts.Timeout, c: connection}
391430
r := bufio.NewReaderSize(dc, 128*1024)
392431
w := bufio.NewWriterSize(dc, 128*1024)
393432
greeting := make([]byte, 128)
@@ -400,8 +439,8 @@ func (conn *Connection) dial() (err error) {
400439
conn.Greeting.auth = bytes.NewBuffer(greeting[64:108]).String()
401440

402441
// Auth
403-
if conn.opts.User != "" {
404-
scr, err := scramble(conn.Greeting.auth, conn.opts.Pass)
442+
if opts.User != "" {
443+
scr, err := scramble(conn.Greeting.auth, opts.Pass)
405444
if err != nil {
406445
err = errors.New("auth: scrambling failure " + err.Error())
407446
connection.Close()

‎example_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,29 @@ type Tuple struct {
2020
func example_connect() *tarantool.Connection {
2121
conn, err := tarantool.Connect(server, opts)
2222
if err != nil {
23-
panic("Connection is not established")
23+
panic("Connection is not established: " + err.Error())
2424
}
2525
return conn
2626
}
2727

28+
// Example demonstrates how to use SSL transport.
29+
func ExampleSslOpts() {
30+
var opts = tarantool.Opts{
31+
User: "test",
32+
Pass: "test",
33+
Transport: "ssl",
34+
Ssl: tarantool.SslOpts{
35+
KeyFile: "testdata/localhost.key",
36+
CertFile: "testdata/localhost.crt",
37+
CaFile: "testdata/ca.crt",
38+
},
39+
}
40+
_, err := tarantool.Connect("127.0.0.1:3013", opts)
41+
if err != nil {
42+
panic("Connection is not established: " + err.Error())
43+
}
44+
}
45+
2846
func ExampleConnection_Select() {
2947
conn := example_connect()
3048
defer conn.Close()

‎export_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,19 @@
11
package tarantool
22

3+
import (
4+
"net"
5+
"time"
6+
)
7+
38
func (schema *Schema) ResolveSpaceIndex(s interface{}, i interface{}) (spaceNo, indexNo uint32, err error) {
49
return schema.resolveSpaceIndex(s, i)
510
}
11+
12+
func SslDialTimeout(network, address string, timeout time.Duration,
13+
opts SslOpts) (connection net.Conn, err error) {
14+
return sslDialTimeout(network, address, timeout, opts)
15+
}
16+
17+
func SslCreateContext(opts SslOpts) (ctx interface{}, err error) {
18+
return sslCreateContext(opts)
19+
}

‎go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ require (
66
github.com/google/uuid v1.3.0
77
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
88
github.com/stretchr/testify v1.7.1 // indirect
9+
github.com/tarantool/go-openssl v0.0.8-0.20220419150948-be4921aa2f87
910
google.golang.org/appengine v1.6.7 // indirect
1011
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
1112
gopkg.in/vmihailenco/msgpack.v2 v2.9.2

‎go.sum

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,25 @@ github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
77
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
88
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
99
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
10+
github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0=
11+
github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
1012
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
1113
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
1214
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
1315
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
16+
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU=
17+
github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc=
1418
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
1519
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
1620
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
21+
github.com/tarantool/go-openssl v0.0.8-0.20220419150948-be4921aa2f87 h1:JGzuBxNBq5saVtPUcuu5Y4+kbJON6H02//OT+RNqGts=
22+
github.com/tarantool/go-openssl v0.0.8-0.20220419150948-be4921aa2f87/go.mod h1:M7H4xYSbzqpW/ZRBMyH0eyqQBsnhAMfsYk5mv0yid7A=
1723
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
1824
golang.org/x/net v0.0.0-20190603091049-60506f45cf65 h1:+rhAzEzT3f4JtomfC371qB+0Ola2caSKcY69NUBZrRQ=
1925
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
2026
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
27+
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb h1:fgwFCsaw9buMuxNd6+DQfAuSFqbNiQZpcgJQAgJsK6k=
28+
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
2129
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
2230
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
2331
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=

‎ssl.go

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
//go:build !go_tarantool_ssl_disable
2+
// +build !go_tarantool_ssl_disable
3+
4+
package tarantool
5+
6+
import (
7+
"errors"
8+
"io/ioutil"
9+
"net"
10+
"time"
11+
12+
"github.com/tarantool/go-openssl"
13+
)
14+
15+
func sslDialTimeout(network, address string, timeout time.Duration,
16+
opts SslOpts) (connection net.Conn, err error) {
17+
var ctx interface{}
18+
if ctx, err = sslCreateContext(opts); err != nil {
19+
return
20+
}
21+
22+
return openssl.DialTimeout(network, address, timeout, ctx.(*openssl.Ctx), 0)
23+
}
24+
25+
// interface{} is a hack. It helps to avoid dependency of go-openssl in build
26+
// of tests with the tag 'go_tarantool_ssl_disable'.
27+
func sslCreateContext(opts SslOpts) (ctx interface{}, err error) {
28+
var sslCtx *openssl.Ctx
29+
30+
// Require TLSv1.2, because other protocol versions don't seem to
31+
// support the GOST cipher.
32+
if sslCtx, err = openssl.NewCtxWithVersion(openssl.TLSv1_2); err != nil {
33+
return
34+
}
35+
ctx = sslCtx
36+
sslCtx.SetMaxProtoVersion(openssl.TLS1_2_VERSION)
37+
sslCtx.SetMinProtoVersion(openssl.TLS1_2_VERSION)
38+
39+
if opts.CertFile != "" {
40+
if err = sslLoadCert(sslCtx, opts.CertFile); err != nil {
41+
return
42+
}
43+
}
44+
45+
if opts.KeyFile != "" {
46+
if err = sslLoadKey(sslCtx, opts.KeyFile); err != nil {
47+
return
48+
}
49+
}
50+
51+
if opts.CaFile != "" {
52+
if err = sslCtx.LoadVerifyLocations(opts.CaFile, ""); err != nil {
53+
return
54+
}
55+
verifyFlags := openssl.VerifyPeer | openssl.VerifyFailIfNoPeerCert
56+
sslCtx.SetVerify(verifyFlags, nil)
57+
}
58+
59+
if opts.Ciphers != "" {
60+
sslCtx.SetCipherList(opts.Ciphers)
61+
}
62+
63+
return
64+
}
65+
66+
func sslLoadCert(ctx *openssl.Ctx, certFile string) (err error) {
67+
var certBytes []byte
68+
if certBytes, err = ioutil.ReadFile(certFile); err != nil {
69+
return
70+
}
71+
72+
certs := openssl.SplitPEM(certBytes)
73+
if len(certs) == 0 {
74+
err = errors.New("No PEM certificate found in " + certFile)
75+
return
76+
}
77+
first, certs := certs[0], certs[1:]
78+
79+
var cert *openssl.Certificate
80+
if cert, err = openssl.LoadCertificateFromPEM(first); err != nil {
81+
return
82+
}
83+
if err = ctx.UseCertificate(cert); err != nil {
84+
return
85+
}
86+
87+
for _, pem := range certs {
88+
if cert, err = openssl.LoadCertificateFromPEM(pem); err != nil {
89+
break
90+
}
91+
if err = ctx.AddChainCertificate(cert); err != nil {
92+
break
93+
}
94+
}
95+
return
96+
}
97+
98+
func sslLoadKey(ctx *openssl.Ctx, keyFile string) (err error) {
99+
var keyBytes []byte
100+
if keyBytes, err = ioutil.ReadFile(keyFile); err != nil {
101+
return
102+
}
103+
104+
var key openssl.PrivateKey
105+
if key, err = openssl.LoadPrivateKeyFromPEM(keyBytes); err != nil {
106+
return
107+
}
108+
109+
return ctx.UsePrivateKey(key)
110+
}

‎ssl_disable.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
//go:build go_tarantool_ssl_disable
2+
// +build go_tarantool_ssl_disable
3+
4+
package tarantool
5+
6+
import (
7+
"errors"
8+
"net"
9+
"time"
10+
)
11+
12+
func sslDialTimeout(network, address string, timeout time.Duration,
13+
opts SslOpts) (connection net.Conn, err error) {
14+
return nil, errors.New("SSL support is disabled.")
15+
}
16+
17+
func sslCreateContext(opts SslOpts) (ctx interface{}, err error) {
18+
return nil, errors.New("SSL support is disabled.")
19+
}

‎ssl_test.go

Lines changed: 466 additions & 0 deletions
Large diffs are not rendered by default.

‎test_helpers/main.go

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,12 @@ package test_helpers
1313
import (
1414
"errors"
1515
"fmt"
16+
"io"
17+
"io/ioutil"
1618
"log"
1719
"os"
1820
"os/exec"
21+
"path/filepath"
1922
"regexp"
2023
"strconv"
2124
"time"
@@ -32,12 +35,28 @@ type StartOpts struct {
3235
// https://www.tarantool.io/en/doc/latest/reference/configuration/#cfg-basic-listen
3336
Listen string
3437

38+
// PingServer changes a host to connect to test startup of a Tarantool
39+
// instance. By default, it uses Listen value as the host for the connection.
40+
PingServer string
41+
42+
// PingTransport changes Opts.Transport for a connection that checks startup
43+
// of a Tarantool instance.
44+
PingTransport string
45+
46+
// PingSsl changes Opts.Ssl for a connection that checks startup of
47+
// a Tarantool instance.
48+
PingSsl tarantool.SslOpts
49+
3550
// WorkDir is box.cfg work_dir parameter for tarantool.
3651
// Specify folder to store tarantool data files.
3752
// Folder must be unique for each tarantool process used simultaneously.
3853
// https://www.tarantool.io/en/doc/latest/reference/configuration/#confval-work_dir
3954
WorkDir string
4055

56+
// SslCertsDir is a path to a directory with SSL certificates. It will be
57+
// copied to the working directory.
58+
SslCertsDir string
59+
4160
// User is a username used to connect to tarantool.
4261
// All required grants must be given in InitScript.
4362
User string
@@ -185,6 +204,14 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
185204
return inst, err
186205
}
187206

207+
// Copy SSL certificates.
208+
if startOpts.SslCertsDir != "" {
209+
err = copySslCerts(startOpts.WorkDir, startOpts.SslCertsDir)
210+
if err != nil {
211+
return inst, err
212+
}
213+
}
214+
188215
// Options for restarting tarantool instance.
189216
inst.Opts = startOpts
190217

@@ -204,11 +231,19 @@ func StartTarantool(startOpts StartOpts) (TarantoolInstance, error) {
204231
User: startOpts.User,
205232
Pass: startOpts.Pass,
206233
SkipSchema: true,
234+
Transport: startOpts.PingTransport,
235+
Ssl: startOpts.PingSsl,
207236
}
208237

209238
var i uint
210239
for i = 0; i <= startOpts.ConnectRetry; i++ {
211-
err = isReady(startOpts.Listen, &opts)
240+
var server string
241+
if startOpts.PingServer != "" {
242+
server = startOpts.PingServer
243+
} else {
244+
server = startOpts.Listen
245+
}
246+
err = isReady(server, &opts)
212247

213248
// Both connect and ping is ok.
214249
if err == nil {
@@ -254,3 +289,60 @@ func StopTarantoolWithCleanup(inst TarantoolInstance) {
254289
}
255290
}
256291
}
292+
293+
func copySslCerts(dst string, sslCertsDir string) (err error) {
294+
dstCertPath := filepath.Join(dst, sslCertsDir)
295+
if err = os.Mkdir(dstCertPath, 0755); err != nil {
296+
return
297+
}
298+
if err = copyDirectoryFiles(sslCertsDir, dstCertPath); err != nil {
299+
return
300+
}
301+
return
302+
}
303+
304+
func copyDirectoryFiles(scrDir, dest string) error {
305+
entries, err := ioutil.ReadDir(scrDir)
306+
if err != nil {
307+
return err
308+
}
309+
for _, entry := range entries {
310+
sourcePath := filepath.Join(scrDir, entry.Name())
311+
destPath := filepath.Join(dest, entry.Name())
312+
_, err := os.Stat(sourcePath)
313+
if err != nil {
314+
return err
315+
}
316+
317+
if err := copyFile(sourcePath, destPath); err != nil {
318+
return err
319+
}
320+
321+
if err := os.Chmod(destPath, entry.Mode()); err != nil {
322+
return err
323+
}
324+
}
325+
return nil
326+
}
327+
328+
func copyFile(srcFile, dstFile string) error {
329+
out, err := os.Create(dstFile)
330+
if err != nil {
331+
return err
332+
}
333+
334+
defer out.Close()
335+
336+
in, err := os.Open(srcFile)
337+
if err != nil {
338+
return err
339+
}
340+
defer in.Close()
341+
342+
_, err = io.Copy(out, in)
343+
if err != nil {
344+
return err
345+
}
346+
347+
return nil
348+
}

‎testdata/ca.crt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDLzCCAhegAwIBAgIUMMZTmNkhr4qOfSwInVk2dAJvoBEwDQYJKoZIhvcNAQEL
3+
BQAwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD0V4YW1wbGUtUm9vdC1DQTAeFw0y
4+
MjA1MjYwNjE3NDBaFw00NDEwMjkwNjE3NDBaMCcxCzAJBgNVBAYTAlVTMRgwFgYD
5+
VQQDDA9FeGFtcGxlLVJvb3QtQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEK
6+
AoIBAQCRq/eaA3I6CB8t770H2XDdzcp1yuC/+TZOxV5o0LuRkogTvL2kYULBrfx1
7+
rVZu8zQJTx1fmSRj1cN8j+IrmXN5goZ3mYFTnnIOgkyi+hJysVlo5s0Kp0qtLLGM
8+
OuaVbxw2oAy75if5X3pFpiDaMvFBtJKsh8+SkncBIC5bbKC5AoLdFANLmPiH0CGr
9+
Mv3rL3ycnbciI6J4uKHcWnYGGiMjBomaZ7jd/cOjcjmGfpI5d0nq13G11omkyEyR
10+
wNX0eJRL02W+93Xu7tD+FEFMxFvak+70GvX+XWomwYw/Pjlio8KbTAlJxhfK2Lh6
11+
H798k17VfxIrOk0KjzZS7+a20hZ/AgMBAAGjUzBRMB0GA1UdDgQWBBT2f5o8r75C
12+
PWST36akpkKRRTbhvjAfBgNVHSMEGDAWgBT2f5o8r75CPWST36akpkKRRTbhvjAP
13+
BgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQA9pb75p6mnqp2MQHSr
14+
5SKRf2UV4wQIUtXgF6V9vNfvVzJii+Lzrqir1YMk5QgavCzD96KlJcqJCcH559RY
15+
5743AxI3tdWfA3wajBctoy35oYnT4M30qbkryYLTUlv7PmeNWvrksTURchyyDt5/
16+
3T73yj5ZalmzKN6+xLfUDdnudspfWlUMutKU50MU1iuQESf4Fwd53vOg9jMcWJ2E
17+
vAgfVI0XAvYdU3ybJrUvBq5zokYR2RzGv14uHxwVPnLBjrBEHRnbrXvLZJhuIS2b
18+
xZ3CqwWi+9bvNqHz09HvhkU2b6fCGweKaAUGSo8OfQ5FRkjTUomMI/ZLs/qtJ6JR
19+
zzVt
20+
-----END CERTIFICATE-----

‎testdata/empty

Whitespace-only changes.

‎testdata/generate.sh

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#!/usr/bin/env bash
2+
set -xeuo pipefail
3+
# An example how-to re-generate testing certificates (because usually
4+
# TLS certificates have expiration dates and some day they will expire).
5+
#
6+
# The instruction is valid for:
7+
#
8+
# $ openssl version
9+
# OpenSSL 3.0.2 15 Mar 2022 (Library: OpenSSL 3.0.2 15 Mar 2022)
10+
11+
cat <<EOF > domains.ext
12+
authorityKeyIdentifier=keyid,issuer
13+
basicConstraints=CA:FALSE
14+
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
15+
subjectAltName = @alt_names
16+
[alt_names]
17+
DNS.1 = localhost
18+
IP.1 = 127.0.0.1
19+
EOF
20+
21+
openssl req -x509 -nodes -new -sha256 -days 8192 -newkey rsa:2048 -keyout ca.key -out ca.pem -subj "/C=US/CN=Example-Root-CA"
22+
openssl x509 -outform pem -in ca.pem -out ca.crt
23+
24+
openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=US/ST=YourState/L=YourCity/O=Example-Certificates/CN=localhost"
25+
openssl x509 -req -sha256 -days 8192 -in localhost.csr -CA ca.pem -CAkey ca.key -CAcreateserial -extfile domains.ext -out localhost.crt

‎testdata/localhost.crt

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
-----BEGIN CERTIFICATE-----
2+
MIIDkjCCAnqgAwIBAgIUAvSBJ3nSv7kdKw1IQ7AjchzI7T8wDQYJKoZIhvcNAQEL
3+
BQAwJzELMAkGA1UEBhMCVVMxGDAWBgNVBAMMD0V4YW1wbGUtUm9vdC1DQTAeFw0y
4+
MjA1MjYwNjE3NDBaFw00NDEwMjkwNjE3NDBaMGcxCzAJBgNVBAYTAlVTMRIwEAYD
5+
VQQIDAlZb3VyU3RhdGUxETAPBgNVBAcMCFlvdXJDaXR5MR0wGwYDVQQKDBRFeGFt
6+
cGxlLUNlcnRpZmljYXRlczESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG
7+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnbFY+BMqlddktbitgaZICws4Zyj8LFy9QzO+
8+
AYSQyqFuTCI+cGqbP5r6Qf4f3xHNGykHJGn18brpiFWhNMaVkkgU3dycU8fFayVN
9+
hLEJAXd4acWP1h5/aH4cOZgl+xJlmU2iLHtP/TLYEDDiVkfqL/MgUIMxbndIaiU0
10+
/e81v+2gi8ydyI6aElN8KbAaFPzXCZ28/RmO/0m36YzF+FSMVD1Hx8xO5V+Q9N1q
11+
dsyrMdh0nCxDDXGdBgDrKt5+U1uJkDpTHfjMAkf7oBoRd8DJ8O74bpue03W5WxKQ
12+
NjNfvHSgkBaQSdnxR93FSCr/Gs6WcUd50Y8z+ZCTNkup0KROTwIDAQABo3YwdDAf
13+
BgNVHSMEGDAWgBT2f5o8r75CPWST36akpkKRRTbhvjAJBgNVHRMEAjAAMAsGA1Ud
14+
DwQEAwIE8DAaBgNVHREEEzARgglsb2NhbGhvc3SHBH8AAAEwHQYDVR0OBBYEFOwH
15+
aHK6QrEfltP7wwldUWrQJ9viMA0GCSqGSIb3DQEBCwUAA4IBAQAGHGuloGJqLoPZ
16+
2iRnb/NaiArowLnUz4Z3ENKMB2KbZFGijMJSXO9i9ZLCFL+O93vCTspKGtHqVX2o
17+
dxcrF7EZ9EaHIijWjKGEp1PszunBIca+Te+zyRg9Z+F9gwRsJYB8ctBGjIhe4qEv
18+
ZSlRY489UVNKLTcHcl6nlUled9hciBJHpXuitiiNhzUareP38hROyiUhrAy8L21L
19+
t7Ww5YGRuSTxM5LQfPZcCy40++TlyvXs0DCQ8ZuUbqZv64bNHbaLOyxIqKfPypXa
20+
nS3AYZzUJjHj7vZwHoL1SyvBjx/DQAsWaEv137d8FlMqCsWLXfCsuNpKeQYZOyDS
21+
7ploP9Gl
22+
-----END CERTIFICATE-----

‎testdata/localhost.key

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
-----BEGIN PRIVATE KEY-----
2+
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCdsVj4EyqV12S1
3+
uK2BpkgLCzhnKPwsXL1DM74BhJDKoW5MIj5waps/mvpB/h/fEc0bKQckafXxuumI
4+
VaE0xpWSSBTd3JxTx8VrJU2EsQkBd3hpxY/WHn9ofhw5mCX7EmWZTaIse0/9MtgQ
5+
MOJWR+ov8yBQgzFud0hqJTT97zW/7aCLzJ3IjpoSU3wpsBoU/NcJnbz9GY7/Sbfp
6+
jMX4VIxUPUfHzE7lX5D03Wp2zKsx2HScLEMNcZ0GAOsq3n5TW4mQOlMd+MwCR/ug
7+
GhF3wMnw7vhum57TdblbEpA2M1+8dKCQFpBJ2fFH3cVIKv8azpZxR3nRjzP5kJM2
8+
S6nQpE5PAgMBAAECggEAFv81l9wHsll6pOu9VfJ/gCjPPXAjMn8F1OaXV5ZTHVHk
9+
iXLXA0LwyBpcU8JxOHFapZLaqUtQpEObahf+zfkF+BLOBDr3i1pPZpxGjUraIt4e
10+
7+HxY4sIDp+Rky6mn1JkAbLqKy2CkUzYaKgQYf/T3dFJjaRMUa1QoLYzX7MCdi5B
11+
GnBICzi2UVsn3HU934l/gJKV+SlprdbrGJ+fRklP2AxLey3EOrwooUViy+k3+w5E
12+
dzBH2HpLL0XuIHaBXQ01J6Mu3ud9ApFLC+Rh+2UFTW/WPnNe+B6BO5CGNN52Pfdr
13+
Q5l+VzmRkXXo2fio+w4z/az8axT/DdhKGT2oBlp35QKBgQDZVGdKjkalH3QH2pdy
14+
CWJIiybzY1R0CpimfgDLIdqEsps9qqgLXsUFB5yTcCRmg8RSWWHvhMVMyJtBcsdY
15+
xGhmHxsFBxuray60UljxBcRQTwqvAX7mP8WEv8t80kbhyaxvOfkg8JD1n2hS7NjL
16+
dOIG9Mh8L0YSOCRkbfv90OnYXQKBgQC5wGs35Ksnqi2swX5MLYfcBaImzoNde86n
17+
cXJ0yyF82O1pk8DkmU2EDcUoQfkKxr3ANvVDG4vYaguIhYsJqPg/t8XQt/epDz/O
18+
WZhqogn0ysaTv2FHrWcgPAkq82hpNII5NfPP8aRaYh8OUSfh4WHkW84m6+usqwjI
19+
wbOq36qmmwKBgGMFFdreYEmzvwYlDoOiyukKncCfLUeB3HNfTbU/w3RafGjobJBh
20+
qZrVEP4MRkl/F9/9YaXj9JE7haGYTkOfmYGOAp2T04OS3kDClEucuQluOgvqvorh
21+
23jUej5xAGK3pJ046M2dTi7bZokB6PUqWCGbPg127JI4ijxH8FyA50rxAoGAQO2d
22+
jMAFg6vco1JPT1lq7+GYOHBfQsIQDj99fo2yeu1or0rSVhWwHsShcdz9rGKj2Rhc
23+
ysRKMa9/sIzdeNbzT3JxVu+3RgTqjLqMqFlTmZl3qBVxb5iRP5c8rSLAEGYmTtEp
24+
FDqm9GDv8hU0F6SsjyH4AWrdylFOlL4Ai237PJkCgYBDC1wAwBD8WXJqRrYVGj7X
25+
l4TQQ0hO7La/zgbasSgLNaJcYu32nut6D0O8IlmcQ2nO0BGPjQmJFGp6xawjViRu
26+
np7fEkJQEf1pK0yeA8A3urjXccuUXEA9kKeqaSZYDzICPFaOlezPPPpW0hbkhnPe
27+
dQn3DcoY6e6o0K5ltt1RvQ==
28+
-----END PRIVATE KEY-----

0 commit comments

Comments
 (0)
Please sign in to comment.