Skip to content

Commit 1f9b6c6

Browse files
add feature: encrypt credential file
1 parent 9f17328 commit 1f9b6c6

File tree

2 files changed

+94
-148
lines changed

2 files changed

+94
-148
lines changed

README.md

Lines changed: 21 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -1,144 +1,22 @@
11

2-
## Cloud SQL Proxy
3-
The Cloud SQL Proxy allows a user with the appropriate permissions to connect
4-
to a Second Generation Cloud SQL database without having to deal with IP whitelisting or SSL
5-
certificates manually. It works by opening unix/tcp sockets on the local machine
6-
and proxying connections to the associated Cloud SQL instances when the sockets
7-
are used.
8-
9-
To build from source, ensure you have [go installed](https://golang.org/doc/install)
10-
and have set [GOPATH](https://github.com/golang/go/wiki/GOPATH). Then, simply do a go get:
11-
12-
GO111MODULE=on go get github.com/GoogleCloudPlatform/cloudsql-proxy/cmd/cloud_sql_proxy
13-
14-
The cloud_sql_proxy will be placed in `$GOPATH/bin` after `go get` completes.
15-
16-
cloud_sql_proxy takes a few arguments to configure what instances to connect to and connection behavior:
17-
18-
* `-fuse`: requires access to `/dev/fuse` as well as the `fusermount` binary. An
19-
optional `-fuse_tmp` flag can specify where to place temporary files. The
20-
directory indicated by `-dir` is mounted.
21-
* `-instances="project1:region:instance1,project3:region:instance1"`: A comma-separated list
22-
of instances to open inside `-dir`. Also supports exposing a tcp port and renaming the default Unix Domain Sockets; see examples below.
23-
Same list can be provided via INSTANCES environment variable, in case when both are provided - proxy will use command line flag.
24-
* `-instances_metadata=metadata_key`: Usable on [GCE](https://cloud.google.com/compute/docs/quickstart) only. The given [GCE metadata](https://cloud.google.com/compute/docs/metadata) key will be
25-
polled for a list of instances to open in `-dir`. The metadata key is relative from `computeMetadata/v1/`. The format for the value is the same as the 'instances' flag. A hanging-poll strategy is used, meaning that changes to
26-
the metadata value will be reflected in the `-dir` even while the proxy is
27-
running. When an instance is removed from the list the corresponding socket
28-
will be removed from `-dir` as well (unless it was also specified in
29-
`-instances`), but any existing connections to this instance will NOT be
30-
terminated.
31-
* `-ip_address_types=PUBLIC,PRIVATE`: A comma-delimited list of preferred IP
32-
types for connecting to an instance. For example, setting this to PRIVATE will
33-
force the proxy to connect to instances using an instance's associated private
34-
IP. Defaults to `PUBLIC,PRIVATE`
35-
* `-term_timeout=30s`: How long to wait for connections to close before shutting
36-
down the proxy. Defaults to 0.
37-
* `-skip_failed_instance_config`: Setting this flag will allow you to prevent the proxy from terminating when
38-
some instance configurations could not be parsed and/or are unavailable.
39-
* `-log_debug_stdout=true`: This is to log non-error output to stdOut instead of stdErr. For example, if you don't want connection related messages to log as errors, set this flag to true. Defaults to false.
40-
41-
Note: `-instances` and `-instances_metadata` may be used at the same time but
42-
are not compatible with the `-fuse` flag.
43-
44-
cloud_sql_proxy authentication can be configured in a few different ways. Those listed higher on the list will override options lower on the list:
45-
46-
1. `credential_file` flag
47-
2. `token` flag
48-
3. Service account key at path stored in `GOOGLE_APPLICATION_CREDENTIALS`
49-
4. gcloud _user_ credentials (set from `gcloud auth login`)
50-
5. Default Application Credentials via goauth:
51-
52-
1. `GOOGLE_APPLICATION_CREDENTIALS` (again)
53-
2. gcloud _application default_ credentials (set from ` gcloud auth application-default login`)
54-
3. appengine.AccessToken (for App Engine Go < =1.9)
55-
4. GCE/GAE metadata credentials
56-
57-
When the proxy authenticates under the default service account of the
58-
Compute Engine VM it is running on the VM must have at least the
59-
sqlservice.admin API scope ("https://www.googleapis.com/auth/sqlservice.admin")
60-
and the associated project must have the SQL Admin API
61-
enabled. The default service account must also have at least WRITER/EDITOR
62-
priviledges to any projects of target SQL instances.
63-
64-
Specifying the `-credential_file` flag allows use of the proxy outside of
65-
Google's cloud. Simply [create a new service
66-
account](https://cloud.google.com/sql/docs/mysql/sql-proxy#create-service-account),
67-
download the associated JSON file, and set `-credential_file` to the path of the
68-
JSON file. You can also set the GOOGLE_APPLICATION_CREDENTIALS environment variable
69-
instead of passing this flag.
70-
71-
## Example invocations:
72-
73-
./cloud_sql_proxy -dir=/cloudsql -instances=my-project:us-central1:sql-inst &
74-
mysql -u root -S /cloudsql/my-project:us-central1:sql-inst
75-
76-
# To retrieve instances from a custom metadata value (only when running on GCE)
77-
./cloud_sql_proxy -dir=/cloudsql -instances_metadata instance/attributes/<custom-metadata-key> &
78-
mysql -u root -S /cloudsql/my-project:us-central1:sql-inst
79-
80-
# For -fuse you do not need to specify instance names ahead of time:
81-
./cloud_sql_proxy -dir=/cloudsql -fuse &
82-
mysql -u root -S /cloudsql/my-project:us-central1:sql-inst
83-
84-
# For programs which do not support using Unix Domain Sockets, specify tcp:
85-
./cloud_sql_proxy -dir=/cloudsql -instances=my-project:us-central1:sql-inst=tcp:3306 &
86-
mysql -u root -h 127.0.0.1
87-
88-
# For programs which require a certain Unix Domain Socket name:
89-
./cloud_sql_proxy -dir=/cloudsql -instances=my-project:us-central1:sql-inst=unix:custom_socket_name &
90-
mysql -u root -S /cloudsql/custom_socket_name
91-
92-
# For programs which require a the Unix Domain Socket at a specific location, set an absolute path (overrides -dir):
93-
./cloud_sql_proxy -dir=/cloudsql -instances=my-project:us-central1:sql-inst=unix:/my/custom/sql-socket &
94-
mysql -u root -S /my/custom/sql-socket
95-
96-
## Container Images
97-
98-
For convenience, we maintain several containerized versions. These images are
99-
currently hosted in the following GCR repositories:
100-
* `gcr.io/cloudsql-docker/gce-proxy`
101-
* `us.gcr.io/cloudsql-docker/gce-proxy`
102-
* `eu.gcr.io/cloudsql-docker/gce-proxy`
103-
* `asia.gcr.io/cloudsql-docker/gce-proxy`
104-
105-
__Note:__
106-
107-
Each image is tagged with the version of the proxy it was released with. The
108-
following tags are currently supported:
109-
* `$VERSION` - default image (recommended)
110-
* `$VERSION-alpine` - uses [`alpine:3`](https://hub.docker.com/_/alpine) as a base image (only supported from v1.17 up)
111-
* `$VERSION-buster` - uses [`debian:buster`](https://hub.docker.com/_/debian) as a base image (only supported from v1.17 up)
112-
113-
__Note:__ We strongly recommend to always use the latest version of the proxy,
114-
and to update the version regularly. However, we recommend pinning to a
115-
specific tag and avoid the `latest` tag. Additionally, please note that
116-
the tagged version is _only_ that of the proxy - changes in base images may
117-
break specific setups, even on non-major version increments. As such,
118-
it's a best practice to test changes before deployment, and use automated
119-
rollbacks to revert potential failures.
120-
121-
## To use from Kubernetes:
122-
123-
### Deploying Cloud SQL Proxy as a sidecar container
124-
Follow this [page](https://github.com/GoogleCloudPlatform/kubernetes-engine-samples/tree/master/cloudsql). See also
125-
[Connecting from Google Kubernetes Engine](https://cloud.google.com/sql/docs/mysql/connect-kubernetes-engine).
126-
127-
128-
## Third Party
129-
130-
__WARNING__: _These distributions are not officially supported by Google._
131-
132-
### Installing via Homebrew
133-
134-
You can find a formula for with Homebrew [here](https://github.com/tclass/homebrew-cloud_sql_proxy).
135-
136-
137-
### K8s Cluster Service using Helm
138-
139-
Follow these [instructions](https://github.com/rimusz/charts/tree/master/stable/gcloud-sqlproxy).
140-
This chart creates a Deployment and a Service, but we recommend deploying the proxy as a sidecar container in your pods.
141-
142-
### .Net Proxy Wrapper (Nuget Package)
143-
144-
Install via Nuget, follow these [instructions](https://github.com/expert1-pty-ltd/cloudsql-proxy#install-via-nuget).
2+
# Cloud SQL Proxy Hardening
3+
+ [Features](#Features)
4+
+ [Requirements](#Requirements)
5+
+ [Installation](#Installation)
6+
+ [Usage](#Usage)
7+
## Fork from
8+
cloudsql-proxy: https://github.com/GoogleCloudPlatform/cloudsql-proxy/tree/v1.19.0
9+
## Features
10+
+ Replace plaintext credential file with encrypted one which bound to instance ID.
11+
## Requirements
12+
+ Go 1.15 or higher.
13+
## Installation
14+
1. git clone https://github.com/Funny-Systems-OSS/cloudsql-proxy-hardening.git
15+
2. cd ./cloudsql-proxy-hardening
16+
3. go build -o ../cloud_sql_proxy_funny ./cmd/cloud_sql_proxy/
17+
## Usage
18+
+ ./cloud_sql_proxy_funny <-credential_file credential_file_path> [-use_plainfile]
19+
+ -credential_file:\
20+
The encrypted credential file be used to retrieve Service Account credential in cloud_sql_proxy.
21+
+ -use_plainfile:\
22+
Setting this flag will allow you to use not encrypted credential file.

cmd/cloud_sql_proxy/cloud_sql_proxy.go

Lines changed: 73 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,12 @@ import (
3333
"sync"
3434
"syscall"
3535
"time"
36+
37+
"crypto/aes"
38+
"crypto/cipher"
39+
"crypto/md5"
40+
"encoding/hex"
41+
"strconv"
3642

3743
"github.com/GoogleCloudPlatform/cloudsql-proxy/logging"
3844
"github.com/GoogleCloudPlatform/cloudsql-proxy/proxy/certs"
@@ -92,15 +98,32 @@ You may set the GOOGLE_APPLICATION_CREDENTIALS environment variable for the same
9298

9399
// Setting to choose what API to connect to
94100
host = flag.String("host", "", "When set, the proxy uses this host as the base API path. Example: https://sqladmin.googleapis.com")
101+
102+
usePlainfile = flag.Bool("use_plainfile", false, "Setting this flag will allow you to use not encrypted credential file.")
103+
104+
strInstanceID, _ = metadata.InstanceID()
105+
instanceID, _ = strconv.Atoi(strInstanceID)
95106
)
96107

97108
const (
98109
minimumRefreshCfgThrottle = time.Second
99110

100111
port = 3307
112+
113+
funny = `
114+
________ ___ ___ ________ ________ ___ ___
115+
|\ _____\\ \|\ \|\ ___ \|\ ___ \ |\ \ / /|
116+
\ \ \__/\ \ \\\ \ \ \\ \ \ \ \\ \ \ \ \ \/ / /
117+
\ \ __\\ \ \\\ \ \ \\ \ \ \ \\ \ \ \ \ / /
118+
\ \ \_| \ \ \\\ \ \ \\ \ \ \ \\ \ \ \/ / /
119+
\ \__\ \ \_______\ \__\\ \__\ \__\\ \__\__/ / /
120+
\|__| \|_______|\|__| \|__|\|__| \|__|\___/ /
121+
\|___|/
122+
`
101123
)
102124

103125
func init() {
126+
fmt.Println(funny)
104127
flag.Usage = func() {
105128
fmt.Fprintf(os.Stderr, `
106129
The Cloud SQL Proxy allows simple, secure connectivity to Google Cloud SQL. It
@@ -211,7 +234,7 @@ Information for all flags:
211234
var defaultTmp = filepath.Join(os.TempDir(), "cloudsql-proxy-tmp")
212235

213236
// versionString indiciates the version of the proxy currently in use.
214-
var versionString = "1.19.0"
237+
var versionString = "1.19.0-funny"
215238

216239
// metadataString indiciates additional build or distribution metadata.
217240
var metadataString = ""
@@ -225,6 +248,37 @@ func semanticVersion() string {
225248
return v
226249
}
227250

251+
func md5sum(text string) string {
252+
hash := md5.Sum([]byte(text))
253+
return hex.EncodeToString(hash[:])
254+
}
255+
256+
func keyGenerator(val int) string {
257+
return md5sum(strconv.Itoa(val))[:32]
258+
}
259+
260+
func nonceGenerator(val int) string {
261+
return keyGenerator(val)[:12]
262+
}
263+
264+
func decrypt(ciphertext, key, nonce []byte) (plaintext []byte) {
265+
block, err := aes.NewCipher(key)
266+
if err != nil {
267+
log.Fatal(err)
268+
}
269+
270+
aesgcm, err := cipher.NewGCM(block)
271+
if err != nil {
272+
log.Fatal(err)
273+
}
274+
275+
plaintext, err = aesgcm.Open(nil, nonce, ciphertext, nil)
276+
if err != nil {
277+
log.Fatal(err)
278+
}
279+
return
280+
}
281+
228282
// userAgentFromVersionString returns an appropriate user agent string for identifying this proxy process.
229283
func userAgentFromVersionString() string {
230284
return "cloud_sql_proxy/" + semanticVersion()
@@ -272,10 +326,24 @@ func checkFlags(onGCE bool) error {
272326
}
273327

274328
func authenticatedClientFromPath(ctx context.Context, f string) (*http.Client, error) {
275-
all, err := ioutil.ReadFile(f)
276-
if err != nil {
277-
return nil, fmt.Errorf("invalid json file %q: %v", f, err)
329+
var all []byte
330+
if ! *usePlainfile {
331+
byteCiphertext, err := ioutil.ReadFile(f)
332+
if err != nil {
333+
return nil, fmt.Errorf("invalid json file %q: %v", f, err)
334+
}
335+
336+
key := keyGenerator(instanceID + 69)
337+
nonce := nonceGenerator(instanceID + 6969)
338+
all = decrypt(byteCiphertext, []byte(key), []byte(nonce))
339+
} else {
340+
var err error
341+
all, err = ioutil.ReadFile(f)
342+
if err != nil {
343+
return nil, fmt.Errorf("invalid json file %q: %v", f, err)
344+
}
278345
}
346+
279347
// First try and load this as a service account config, which allows us to see the service account email:
280348
if cfg, err := goauth.JWTConfigFromJSON(all, proxy.SQLScope); err == nil {
281349
logging.Infof("using credential file for authentication; email=%s", cfg.Email)
@@ -555,4 +623,4 @@ func main() {
555623
}()
556624

557625
proxyClient.Run(connSrc)
558-
}
626+
}

0 commit comments

Comments
 (0)