Skip to content

Commit ed6c55c

Browse files
authored
Merge pull request #82 from MattMencel/mysql-tls
initial pass at adding tls support to mysql
2 parents a4cf5be + 1bf20ae commit ed6c55c

File tree

15 files changed

+106
-29
lines changed

15 files changed

+106
-29
lines changed

README.md

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -23,20 +23,29 @@ It currently supports **MySQL**, **PostgreSQL** and **MSSQL**.
2323
with a Crossplane provider (e.g. a [CloudSQLInstance] with provider-gcp) or you can
2424
create for an existing server as follows:
2525

26-
```
27-
kubectl create secret generic db-conn \
28-
--from-literal=username=admin \
29-
--from-literal=password='t0ps3cr3t' \
30-
--from-literal=endpoint=my.sql-server.com \
31-
--from-literal=port=3306
32-
```
26+
```
27+
kubectl create secret generic db-conn \
28+
--from-literal=username=admin \
29+
--from-literal=password='t0ps3cr3t' \
30+
--from-literal=endpoint=my.sql-server.com \
31+
--from-literal=port=3306
32+
```
3333

3434
2. Create managed resource for your SQL server flavor:
3535

36-
- **MySQL**: `Database`, `Grant`, `User` (See [the examples](examples/mysql))
37-
- **PostgreSQL**: `Database`, `Grant`, `Extension`, `Role` (See [the examples](examples/postgresql))
38-
- **MSSQL**: `Database`, `Grant`, `User` (See [the examples](examples/mssql))
36+
- **MySQL**: `Database`, `Grant`, `User` (See [the examples](examples/mysql))
37+
- **PostgreSQL**: `Database`, `Grant`, `Extension`, `Role` (See [the examples](examples/postgresql))
38+
- **MSSQL**: `Database`, `Grant`, `User` (See [the examples](examples/mssql))
3939

40-
[Crossplane]: https://crossplane.io
41-
[CloudSQLInstance]: https://doc.crds.dev/github.com/crossplane/provider-gcp/database.gcp.crossplane.io/CloudSQLInstance/[email protected]
40+
[crossplane]: https://crossplane.io
41+
[cloudsqlinstance]: https://doc.crds.dev/github.com/crossplane/provider-gcp/database.gcp.crossplane.io/CloudSQLInstance/[email protected]
4242
[created automatically]: https://crossplane.io/docs/v1.5/concepts/managed-resources.html#connection-details
43+
44+
## Contributing
45+
46+
1. Fork the project and clone locally.
47+
2. Create a branch with the changes.
48+
3. Install go version 1.17.
49+
4. Run `make` to initialize the "build". Make submodules used for CI/CD.
50+
5. Run `make reviewable` to run code generation, linters, and tests.
51+
6. Commit, push, and PR.

apis/mysql/v1alpha1/provider_types.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,14 @@ import (
2626
type ProviderConfigSpec struct {
2727
// Credentials required to authenticate to this provider.
2828
Credentials ProviderCredentials `json:"credentials"`
29+
// tls=true enables TLS / SSL encrypted connection to the server.
30+
// Use skip-verify if you want to use a self-signed or invalid certificate (server side)
31+
// or use preferred to use TLS only when advertised by the server. This is similar
32+
// to skip-verify, but additionally allows a fallback to a connection which is
33+
// not encrypted. Neither skip-verify nor preferred add any reliable security.
34+
// +kubebuilder:validation:Enum=true;skip-verify;preferred
35+
// +optional
36+
TLS *string `json:"tls"`
2937
}
3038

3139
const (

apis/mysql/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build

cluster/local/integration_tests.sh

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ echo "created cache dir at ${CACHE_PATH}"
7272
docker tag "${BUILD_IMAGE}" "${PACKAGE_IMAGE}"
7373
"${UP}" xpkg xp-extract --from-daemon "${PACKAGE_IMAGE}" -o "${CACHE_PATH}/${PACKAGE_NAME}.gz" && chmod 644 "${CACHE_PATH}/${PACKAGE_NAME}.gz"
7474

75+
7576
# create kind cluster with extra mounts
7677
echo_step "creating k8s cluster using kind"
7778
KIND_CONFIG="$( cat <<EOF
@@ -171,7 +172,8 @@ EOF
171172
echo "${INSTALL_YAML}" | "${KUBECTL}" apply -f -
172173

173174
echo_step "waiting for provider to be installed"
174-
kubectl wait "provider.pkg.crossplane.io/${PACKAGE_NAME}" --for=condition=healthy --timeout=60s
175+
"${KUBECTL}" wait "provider.pkg.crossplane.io/${PACKAGE_NAME}" --for=condition=healthy --timeout=60s
176+
175177

176178
# printing the cache dir contents can be useful for troubleshooting failures
177179
echo_step "check kind node cache dir contents"

examples/mysql/config.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ spec:
99
connectionSecretRef:
1010
namespace: default
1111
name: db-conn
12+
# tls one of preferred(default), skip-verify, or true
13+
tls: preferred

package/crds/mysql.sql.crossplane.io_providerconfigs.yaml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,13 @@ spec:
6868
required:
6969
- source
7070
type: object
71+
tls:
72+
description: tls=true enables TLS / SSL encrypted connection to the server. Use skip-verify if you want to use a self-signed or invalid certificate (server side) or use preferred to use TLS only when advertised by the server. This is similar to skip-verify, but additionally allows a fallback to a connection which is not encrypted. Neither skip-verify nor preferred add any reliable security.
73+
enum:
74+
- true
75+
- skip-verify
76+
- preferred
77+
type: string
7178
required:
7279
- credentials
7380
type: object

pkg/clients/mysql/mysql.go

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"database/sql"
66
"fmt"
7+
"net/url"
78
"strings"
89

910
"github.com/crossplane-contrib/provider-sql/pkg/clients/xsql"
@@ -21,24 +22,43 @@ type mySQLDB struct {
2122
dsn string
2223
endpoint string
2324
port string
25+
tls string
2426
}
2527

2628
// New returns a new MySQL database client.
27-
func New(creds map[string][]byte) xsql.DB {
29+
func New(creds map[string][]byte, tls *string) xsql.DB {
2830
// TODO(negz): Support alternative connection secret formats?
2931
endpoint := string(creds[xpv1.ResourceCredentialsSecretEndpointKey])
3032
port := string(creds[xpv1.ResourceCredentialsSecretPortKey])
33+
username := string(creds[xpv1.ResourceCredentialsSecretUserKey])
34+
password := string(creds[xpv1.ResourceCredentialsSecretPasswordKey])
35+
if tls == nil {
36+
defaultTLS := "preferred"
37+
tls = &defaultTLS
38+
}
39+
dsn := DSN(username, password, endpoint, port, *tls)
40+
3141
return mySQLDB{
32-
dsn: fmt.Sprintf("%s:%s@tcp(%s:%s)/",
33-
creds[xpv1.ResourceCredentialsSecretUserKey],
34-
creds[xpv1.ResourceCredentialsSecretPasswordKey],
35-
endpoint,
36-
port),
42+
dsn: dsn,
3743
endpoint: endpoint,
3844
port: port,
45+
tls: *tls,
3946
}
4047
}
4148

49+
// DSN returns the DSN URL
50+
func DSN(username, password, endpoint, port, tls string) string {
51+
// Use net/url UserPassword to encode the username and password
52+
// This will ensure that any special characters in the username or password
53+
// are percent-encoded for use in the user info portion of the DSN URL
54+
userInfo := url.UserPassword(username, password)
55+
return fmt.Sprintf("%s@tcp(%s:%s)/?tls=%s",
56+
userInfo,
57+
endpoint,
58+
port,
59+
tls)
60+
}
61+
4262
// ExecTx is unsupported in MySQL.
4363
func (c mySQLDB) ExecTx(ctx context.Context, ql []xsql.Query) error {
4464
return errors.Errorf(errNotSupported, "transactions")

pkg/clients/mysql/mysql_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package mysql
2+
3+
import (
4+
"fmt"
5+
"testing"
6+
)
7+
8+
func TestDSNURLEscaping(t *testing.T) {
9+
endpoint := "endpoint"
10+
port := "3306"
11+
user := "username"
12+
rawPass := "password^"
13+
encPass := "password%5E"
14+
tls := "true"
15+
dsn := DSN(user, rawPass, endpoint, port, tls)
16+
if dsn != fmt.Sprintf("%s:%s@tcp(%s:%s)/?tls=%s",
17+
user,
18+
encPass,
19+
endpoint,
20+
port,
21+
tls) {
22+
t.Errorf("DSN string did not match expected output with URL encoded")
23+
}
24+
}

pkg/controller/mysql/database/reconciler.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ func Setup(mgr ctrl.Manager, l logging.Logger) error {
7777
type connector struct {
7878
kube client.Client
7979
usage resource.Tracker
80-
newDB func(creds map[string][]byte) xsql.DB
80+
newDB func(creds map[string][]byte, tls *string) xsql.DB
8181
}
8282

8383
func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) {
@@ -110,7 +110,7 @@ func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.E
110110
return nil, errors.Wrap(err, errGetSecret)
111111
}
112112

113-
return &external{db: c.newDB(s.Data)}, nil
113+
return &external{db: c.newDB(s.Data, pc.Spec.TLS)}, nil
114114
}
115115

116116
type external struct{ db xsql.DB }

0 commit comments

Comments
 (0)