diff --git a/rest/README.md b/rest/README.md index 0f6ffa3..bc4b0b2 100644 --- a/rest/README.md +++ b/rest/README.md @@ -11,3 +11,4 @@ Where possible, we use [httpbingo.org](https://httpbingo.org) as our REST endpoi - [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 - [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. - [restWithParameters](restWithParameters) shows how GraphQL field arguments are automatically added to the REST call--there is nothing for you to do! +- [tls](tls) shows how to use `@rest(tls:)` using the local stepzen service diff --git a/rest/tls/README.md b/rest/tls/README.md new file mode 100644 index 0000000..eb7c6b4 --- /dev/null +++ b/rest/tls/README.md @@ -0,0 +1,162 @@ +# TLS + + +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). + +## Using `@rest(tls:)` + +This examples demonstrates a number of API Connect for GraphQL capabilities: +- Use of `@rest(tls:)` +- stepzen service +- Simple ecmascript capability for reshaping data. + +## mTLS and certificates + +API Connect for GraphQL supports mTLS and custom certificates (self-signed, private, +etc.) by using using a combination of a `@rest(tls:)` argument +that refers to a configuration in the `config.yaml` + +When the tls entry is given the name of a configuration entry, you can provide +- `ca` - the server `ca` or `ca` chain (starting with the leaf certificate) +- `cert` - the client certificate +- `key` - the client certifcate key +The data should be in PEM format. + +In our examples, we have two configuration: `selfsign` with a `ca` entry and `selfsignedmtls` with all three entries. + +TLS 1.2 and 1.3 are supported. TLS 1.0 and 1.1 are not supported. + +## Try it out! + +`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` +During `stepzen deploy`, the `STEPZEN_SERVER_CRT` environment variable is expanded and the result will be a yaml that looks like: +``` +configurationset: + - configuration: + name: selfsign + ca: | + -----BEGIN CERTIFICATE----- + MIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL + ... + -----END CERTIFICATE----- +``` + +The `selfsignedmtls` configuration contains an example mutual TLS configuration. + +Two safe approaches are to set the environment variables from secrets or to have a `.env` file. + +See tricks below for some possible hurdles. + +### Running a test + +Testing mTLS or self-signed certificates locally is best done using local API Connect for GraphQL. +In the following, we'll generate the certificates using openssl, use openssl to for trivialself-signed cert servers +and use the stepzen cli local service mode as a client. + +Note: if you are not using Docker, see Tricks and hints/Container tools. + +#### Steps +``` +stepzen service start +stepzen login --config ~/.stepzen/stepzen-config.local.yaml +(cd tests; make env) +# WARNING: if you are not using Docker, please see +# Tricks and hints/Container tools +stepzen deploy + +# start trivial local TLS server using openssl +# enable DEBUG if there are issues +((cd tests; make run_validation_server_self_sign) & +# wait until it gets establish 1-30s + +# run the actual tests +stepzen request -f operations.graphql + +# cleanup +stepzen service stop +# restore your SaaS or other credentials +``` + + +## Tricks and hints + +### Container tools + +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. + +By default, `rest_self` uses `host.docker.internal` which works in most modern Docker environments. + +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. + +### env variables + +You can set `STEPZEN_*` env variables in .env or using export. + +For example: +``` +export STEPZEN_SERVER_CRT=`cat server.pem` +``` +will set STEPZEN_SERVER_CRT to something like this: +``` +-----BEGIN CERTIFICATE-----\nMIIF5zCCA8+gAwIBAgIUS2BwtghuA7PREQ5AWzOeeT+tCe4wDQYJKoZIhvcNAQEL\nBQA....\nEUhqWbTk+y13A1OPfWbJu82zTKfJFvCAUgCf -----END CERTIFICATE-----" +``` + +Be aware that the `\n` will show up as spaces if you do echo $STEPZEN_SERVER_CRT + +To double check, you'll want to do something like this: +``` +cat < ../.env diff --git a/rest/tls/tests/Test.js b/rest/tls/tests/Test.js new file mode 100644 index 0000000..c5ff0aa --- /dev/null +++ b/rest/tls/tests/Test.js @@ -0,0 +1,15 @@ +const fs = require("fs"); +const path = require("node:path"); +const { + deployAndRun, + stepzen, + getTestDescription, +} = require("../../../tests/gqltest.js"); + +testDescription = getTestDescription("snippets", __dirname); + +describe(testDescription, function () { + // empty tests since this test is not valid for SaaS + const tests = []; + return deployAndRun(__dirname, tests, stepzen.admin); +}); diff --git a/rest/tls/tls.graphql b/rest/tls/tls.graphql new file mode 100644 index 0000000..3a84243 --- /dev/null +++ b/rest/tls/tls.graphql @@ -0,0 +1,22 @@ +type Query { + """ + will contact localhost using host.docker.internal and 8443 and selfsign configuration + the ecmascript is used to repackage any content coming back (openssl s_server returns html) + """ + rest_self: JSON + @rest( + endpoint: "https://host.docker.internal:8443/" + # alternate endpoint settings for different container toolsets + # endpoint: "https://localhost:8443/" + # endpoint: "https://host.rancher-desktop.internal:8443/" + tls: "selfsign" + ecmascript: """ + function transformREST(s) { + return JSON.stringify( + {data100: s.length>100, + accept_8443: s.includes("-accept 8443") + }) + } + """ + ) +}