Skip to content

Commit 7f9dab0

Browse files
authored
Chore/add tls (#85)
* first cut at rest tls * simplify to just self-sign * Add dummy test to do deploy * Updates: ensure docker dependencies are clear and notes about other container toolsets * remove dead comment
1 parent f63e00f commit 7f9dab0

File tree

9 files changed

+250
-0
lines changed

9 files changed

+250
-0
lines changed

rest/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ Where possible, we use [httpbingo.org](https://httpbingo.org) as our REST endpoi
1111
- [postbody](postbody) shows how a POST body can be automatically filled with field arguments with `Content-Type:application/x-www-form-urlencoded`. This is the easiest way to send postbodies down the REST API
1212
- [restWithConfigYaml](restWithConfigYaml) shows how REST query parameters can also be fetched from `config.yaml`--this allows you to keep your SDL code separate from your secrets.
1313
- [restWithParameters](restWithParameters) shows how GraphQL field arguments are automatically added to the REST call--there is nothing for you to do!
14+
- [tls](tls) shows how to use `@rest(tls:)` using the local stepzen service

rest/tls/README.md

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
# TLS
2+
3+
4+
For more information on using TLS in your REST apis and other services [see our documentation](https://www.ibm.com/docs/en/api-connect-graphql/saas?topic=directives-directive-rest#tls-string__title__1).
5+
6+
## Using `@rest(tls:)`
7+
8+
This examples demonstrates a number of API Connect for GraphQL capabilities:
9+
- Use of `@rest(tls:)`
10+
- stepzen service
11+
- Simple ecmascript capability for reshaping data.
12+
13+
## mTLS and certificates
14+
15+
API Connect for GraphQL supports mTLS and custom certificates (self-signed, private,
16+
etc.) by using using a combination of a `@rest(tls:)` argument
17+
that refers to a configuration in the `config.yaml`
18+
19+
When the tls entry is given the name of a configuration entry, you can provide
20+
- `ca` - the server `ca` or `ca` chain (starting with the leaf certificate)
21+
- `cert` - the client certificate
22+
- `key` - the client certifcate key
23+
The data should be in PEM format.
24+
25+
In our examples, we have two configuration: `selfsign` with a `ca` entry and `selfsignedmtls` with all three entries.
26+
27+
TLS 1.2 and 1.3 are supported. TLS 1.0 and 1.1 are not supported.
28+
29+
## Try it out!
30+
31+
`rest_self` in tls.graphql provides an example of self signed certificates by pointing to the `selfsign` resource in config.yaml. That configuration contains `ca: STEPZEN_SERVER_CRT`
32+
During `stepzen deploy`, the `STEPZEN_SERVER_CRT` environment variable is expanded and the result will be a yaml that looks like:
33+
```
34+
configurationset:
35+
- configuration:
36+
name: selfsign
37+
ca: |
38+
-----BEGIN CERTIFICATE-----
39+
MIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL
40+
...
41+
-----END CERTIFICATE-----
42+
```
43+
44+
The `selfsignedmtls` configuration contains an example mutual TLS configuration.
45+
46+
Two safe approaches are to set the environment variables from secrets or to have a `.env` file.
47+
48+
See tricks below for some possible hurdles.
49+
50+
### Running a test
51+
52+
Testing mTLS or self-signed certificates locally is best done using local API Connect for GraphQL.
53+
In the following, we'll generate the certificates using openssl, use openssl to for trivialself-signed cert servers
54+
and use the stepzen cli local service mode as a client.
55+
56+
Note: if you are not using Docker, see Tricks and hints/Container tools.
57+
58+
#### Steps
59+
```
60+
stepzen service start
61+
stepzen login --config ~/.stepzen/stepzen-config.local.yaml
62+
(cd tests; make env)
63+
# WARNING: if you are not using Docker, please see
64+
# Tricks and hints/Container tools
65+
stepzen deploy
66+
67+
# start trivial local TLS server using openssl
68+
# enable DEBUG if there are issues
69+
((cd tests; make run_validation_server_self_sign) &
70+
# wait until it gets establish 1-30s
71+
72+
# run the actual tests
73+
stepzen request -f operations.graphql
74+
75+
# cleanup
76+
stepzen service stop
77+
# restore your SaaS or other credentials
78+
```
79+
80+
81+
## Tricks and hints
82+
83+
### Container tools
84+
85+
API Connect for GraphQL local services runs inside of a container using Docker, Podman, or other container runtime toolset. Each of these have a slightly different method whereby containers can access the host machine's localhost. The details of these are varied depending upon the actual toolset.
86+
87+
By default, `rest_self` uses `host.docker.internal` which works in most modern Docker environments.
88+
89+
For Podman, you may need to change this to `host.containers.internal` or `localhost` depending on your podman defaults. You may also need to modify your podman default configuration to allow for such access.
90+
91+
### env variables
92+
93+
You can set `STEPZEN_*` env variables in .env or using export.
94+
95+
For example:
96+
```
97+
export STEPZEN_SERVER_CRT=`cat server.pem`
98+
```
99+
will set STEPZEN_SERVER_CRT to something like this:
100+
```
101+
-----BEGIN CERTIFICATE-----\nMIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL\nBQA....\nEUhqWbTk+y13A1OPfWbJu82zTKfJFvCAUgCf -----END CERTIFICATE-----"
102+
```
103+
104+
Be aware that the `\n` will show up as spaces if you do echo $STEPZEN_SERVER_CRT
105+
106+
To double check, you'll want to do something like this:
107+
```
108+
cat <<EOF
109+
$STEPZEN_SERVER_CRT
110+
EOF
111+
```
112+
or use techniques outlined in Misconfigured config.yaml below.
113+
114+
You can also set the env variables in `.env`. In that case, you can either replace the line breaks with `\n` or simply quote them:
115+
```
116+
STEPZEN_SERVER_CRT="-----BEGIN CERTIFICATE-----\nMIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL\n...\n-----END CERTIFICATE-----"
117+
```
118+
or
119+
```
120+
STEPZEN_SERVER_CRT="-----BEGIN CERTIFICATE-----
121+
MIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL
122+
...
123+
-----END CERTIFICATE-----"
124+
```
125+
126+
127+
### Misconfigured config.yaml
128+
129+
You can check if the config.yaml being uploaded by running
130+
```
131+
DEBUG="stepzen:sdk:trace" stepzen deploy
132+
```
133+
and looking for the configuration. Useful if the .env or exported variables are not
134+
as expected in the config.yaml or if you've directly placed the data in the `config.yaml`
135+
that it is as you expected.
136+
137+
You are looking for something that looks like:
138+
```
139+
"configuration":{"configurationset":[{"configuration":{"name":"selfsign","ca":
140+
"-----BEGIN CERTIFICATE-----\nMIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL\nBQAwbTE...
141+
```
142+
notice the '\n'. If you see spaces you've done something wrong upsteram.
143+
144+
145+
### Debugging
146+
You can debug using stepzen request by adding `-H "stepzen-debug-level: 1"`
147+
```
148+
stepzen request '{rest_self}' -H "stepzen-debug-level: 1"
149+
```
150+
and you can check the output for issues. You can check if tls values are being passed by looking for the tls
151+
block. In this case for mutual tls, we'll have:
152+
```
153+
"tls": {
154+
"clientCertificate": {
155+
"keyPresent": true,
156+
"present": true
157+
},
158+
"serverRootCA": {
159+
"present": true
160+
}
161+
},
162+
```

rest/tls/config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
configurationset:
2+
- configuration:
3+
name: selfsign
4+
ca: STEPZEN_SERVER_CRT
5+
- configuration:
6+
name: selfsignedmtls
7+
ca: STEPZEN_SERVER_CRT
8+
cert: STEPZEN_CLIENT_CRT
9+
key: STEPZEN_CLIENT_KEY

rest/tls/index.graphql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
schema @sdl(files: ["tls.graphql"]) {
2+
query: Query
3+
}

rest/tls/operations.graphql

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
query run {
2+
rest_self
3+
}

rest/tls/stepzen.config.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"endpoint": "api/tls"
3+
}

rest/tls/tests/Makefile

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Makefile to build and validate a pair of *example* self-signed certificates for *simple* tests
2+
3+
# enable to debug ssl server
4+
# DEBUG:=-debug
5+
all: client.crt server.crt env
6+
7+
# server.crt client.key server.key
8+
client.crt:
9+
openssl req -x509 -newkey rsa:4096 -keyout client.key -out client.crt -sha256 -days 7650 \
10+
-subj "/C=US/ST=Florida/L=Jacksonville/O=LOCALCLIENT/OU=Com/CN=localhost" -nodes \
11+
-addext "subjectAltName = DNS:localhost, DNS:myalt, DNS:host.docker.internal"
12+
13+
server.crt:
14+
openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -sha256 -days 7650 \
15+
-subj "/C=US/ST=California/L=San Jose/O=LOCALSERVER/OU=Com/CN=localhost" -nodes \
16+
-addext "subjectAltName = DNS:localhost, DNS:host.docker.internal"
17+
18+
run_validation_client_self_sign: client.crt
19+
curl --cacert server.crt https://localhost:9443 -debug
20+
21+
run_validation_server_self_sign: server.crt
22+
openssl s_server -accept 8443 -cert server.crt -key server.key $(DEBUG) -www
23+
24+
clean:
25+
rm -f server.crt server.key client.crt client.key
26+
27+
env: ../.env
28+
29+
../.env: client.crt server.crt
30+
( echo STEPZEN_CLIENT_CRT=\""`cat client.crt`"\"; \
31+
echo STEPZEN_CLIENT_KEY=\""`cat client.key`"\"; \
32+
echo STEPZEN_SERVER_CRT=\""`cat server.crt`"\") > ../.env

rest/tls/tests/Test.js

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const fs = require("fs");
2+
const path = require("node:path");
3+
const {
4+
deployAndRun,
5+
stepzen,
6+
getTestDescription,
7+
} = require("../../../tests/gqltest.js");
8+
9+
testDescription = getTestDescription("snippets", __dirname);
10+
11+
describe(testDescription, function () {
12+
// empty tests since this test is not valid for SaaS
13+
const tests = [];
14+
return deployAndRun(__dirname, tests, stepzen.admin);
15+
});

rest/tls/tls.graphql

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
type Query {
2+
"""
3+
will contact localhost using host.docker.internal and 8443 and selfsign configuration
4+
the ecmascript is used to repackage any content coming back (openssl s_server returns html)
5+
"""
6+
rest_self: JSON
7+
@rest(
8+
endpoint: "https://host.docker.internal:8443/"
9+
# alternate endpoint settings for different container toolsets
10+
# endpoint: "https://localhost:8443/"
11+
# endpoint: "https://host.rancher-desktop.internal:8443/"
12+
tls: "selfsign"
13+
ecmascript: """
14+
function transformREST(s) {
15+
return JSON.stringify(
16+
{data100: s.length>100,
17+
accept_8443: s.includes("-accept 8443")
18+
})
19+
}
20+
"""
21+
)
22+
}

0 commit comments

Comments
 (0)