From 603145daa502601432e2048c01916d1bf030053c Mon Sep 17 00:00:00 2001 From: "Charlie C. Kim" Date: Wed, 19 Nov 2025 09:40:58 -0800 Subject: [PATCH 1/5] first cut at rest tls --- rest/tls/config.yaml | 9 +++++++++ rest/tls/index.graphql | 4 ++++ rest/tls/operations.graphql | 4 ++++ rest/tls/stepzen.config.json | 3 +++ rest/tls/tests/Makefile | 35 +++++++++++++++++++++++++++++++++++ rest/tls/tls.graphql | 27 +++++++++++++++++++++++++++ 6 files changed, 82 insertions(+) create mode 100644 rest/tls/config.yaml create mode 100644 rest/tls/index.graphql create mode 100644 rest/tls/operations.graphql create mode 100644 rest/tls/stepzen.config.json create mode 100644 rest/tls/tests/Makefile create mode 100644 rest/tls/tls.graphql diff --git a/rest/tls/config.yaml b/rest/tls/config.yaml new file mode 100644 index 0000000..6f09e5c --- /dev/null +++ b/rest/tls/config.yaml @@ -0,0 +1,9 @@ +configurationset: + - configuration: + name: selfsign + ca: STEPZEN_SERVER_CRT + - configuration: + name: selfsignedmtls + ca: STEPZEN_SERVER_CRT + cert: STEPZEN_CLIENT_CRT + key: STEPZEN_CLIENT_KEY diff --git a/rest/tls/index.graphql b/rest/tls/index.graphql new file mode 100644 index 0000000..29590c7 --- /dev/null +++ b/rest/tls/index.graphql @@ -0,0 +1,4 @@ +schema @sdl(files: ["tls.graphql"]) { + query: Query +} + diff --git a/rest/tls/operations.graphql b/rest/tls/operations.graphql new file mode 100644 index 0000000..557db73 --- /dev/null +++ b/rest/tls/operations.graphql @@ -0,0 +1,4 @@ +query run { + rest_self + rest_self_mtls +} diff --git a/rest/tls/stepzen.config.json b/rest/tls/stepzen.config.json new file mode 100644 index 0000000..af1c0ea --- /dev/null +++ b/rest/tls/stepzen.config.json @@ -0,0 +1,3 @@ +{ + "endpoint": "api/miscellaneous" +} diff --git a/rest/tls/tests/Makefile b/rest/tls/tests/Makefile new file mode 100644 index 0000000..2c27b47 --- /dev/null +++ b/rest/tls/tests/Makefile @@ -0,0 +1,35 @@ +# Makefile to build and validate a pair of *example* self-signed certificates for *simple* tests + +# enable to debug ssl server +# DEBUG:=-debug +all: client.crt server.crt env + +# server.crt client.key server.key +client.crt: + openssl req -x509 -newkey rsa:4096 -keyout client.key -out client.crt -sha256 -days 7650 \ + -subj "/C=US/ST=Florida/L=Jacksonville/O=LOCALCLIENT/OU=Com/CN=localhost" -nodes \ + -addext "subjectAltName = DNS:localhost, DNS:myalt, DNS:host.docker.internal" + +server.crt: + openssl req -x509 -newkey rsa:4096 -keyout server.key -out server.crt -sha256 -days 7650 \ + -subj "/C=US/ST=California/L=San Jose/O=LOCALSERVER/OU=Com/CN=localhost" -nodes \ + -addext "subjectAltName = DNS:localhost, DNS:host.docker.internal" + +run_validation_server_self_sign_mtls: server.crt client.crt + openssl s_server -accept 9443 -cert server.crt -key server.key -Verify 2 -CAfile client.crt $(DEBUG) -www + +run_validation_client_self_sign_mtls: client.crt + curl --cert client.crt --key client.key --cacert server.crt https://localhost:9443 -debug + +run_validation_server_self_sign: server.crt + openssl s_server -accept 8443 -cert server.crt -key server.key $(DEBUG) -www + +clean: + rm -f server.crt server.key client.crt client.key + +env: ../.env + +../.env: client.crt server.crt + ( echo STEPZEN_CLIENT_CRT=\""`cat client.crt`"\"; \ + echo STEPZEN_CLIENT_KEY=\""`cat client.key`"\"; \ + echo STEPZEN_SERVER_CRT=\""`cat server.crt`"\") > ../.env diff --git a/rest/tls/tls.graphql b/rest/tls/tls.graphql new file mode 100644 index 0000000..08f2303 --- /dev/null +++ b/rest/tls/tls.graphql @@ -0,0 +1,27 @@ +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/" + tls: "selfsign" + ecmascript: """ + function transformREST(s) { return JSON.stringify({data100: s.length>100, accept_8443: s.includes("-accept 8443")})} + """ + ) + + """ + will contact localhost using host.docker.internal and 9443 and mtls configuration + the ecmascript is used to repackage any content coming back (openssl s_server returns html) + """ + rest_self_mtls: JSON + @rest( + endpoint: "https://host.docker.internal:9443/" + tls: "selfsignedmtls" + ecmascript: """ + function transformREST(s) { return JSON.stringify({data100: s.length>100, accept_9443: s.includes("-accept 9443")})} + """ + ) +} From fcadf92cf19cf4364973559bbcbe8089a5407ff4 Mon Sep 17 00:00:00 2001 From: "Charlie C. Kim" <13868028+cckim@users.noreply.github.com> Date: Wed, 19 Nov 2025 10:09:00 -0800 Subject: [PATCH 2/5] simplify to just self-sign --- rest/README.md | 1 + rest/tls/README.md | 151 +++++++++++++++++++++++++++++++++++ rest/tls/operations.graphql | 1 - rest/tls/stepzen.config.json | 2 +- rest/tls/tests/Makefile | 7 +- rest/tls/tls.graphql | 13 --- 6 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 rest/tls/README.md 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..026efdb --- /dev/null +++ b/rest/tls/README.md @@ -0,0 +1,151 @@ +# TLS + + +For more inforamtion 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). + +[Environment](https://www.ibm.com/docs/en/api-connect-graphql/saas?topic=environment-tls-configuration-properties). Check that the revision is newer than : 2025-11-18. + +## Using `@rest(tls:)` + +This examples demonstrates a number of StepZen 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. + +#### Steps +``` +stepzen service start +stepzen login --config ~/.stepzen/stepzen-config.local.yaml +(cd tests; make env) +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 + +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 <100, accept_8443: s.includes("-accept 8443")})} """ ) - - """ - will contact localhost using host.docker.internal and 9443 and mtls configuration - the ecmascript is used to repackage any content coming back (openssl s_server returns html) - """ - rest_self_mtls: JSON - @rest( - endpoint: "https://host.docker.internal:9443/" - tls: "selfsignedmtls" - ecmascript: """ - function transformREST(s) { return JSON.stringify({data100: s.length>100, accept_9443: s.includes("-accept 9443")})} - """ - ) } From 831badae225dd2476f407a702382164689e1f341 Mon Sep 17 00:00:00 2001 From: "Charlie C. Kim" <13868028+cckim@users.noreply.github.com> Date: Wed, 19 Nov 2025 14:30:16 -0800 Subject: [PATCH 3/5] Add dummy test to do deploy --- rest/tls/tests/Test.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 rest/tls/tests/Test.js 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); +}); From 7c3d0c1d9ddda874964f2945ad18dc9e87e70fe8 Mon Sep 17 00:00:00 2001 From: "Charlie C. Kim" <13868028+cckim@users.noreply.github.com> Date: Thu, 20 Nov 2025 08:55:42 -0800 Subject: [PATCH 4/5] Updates: ensure docker dependencies are clear and notes about other container toolsets --- rest/tls/README.md | 23 ++++++++++++++++++----- rest/tls/index.graphql | 1 - rest/tls/tls.graphql | 10 +++++++++- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/rest/tls/README.md b/rest/tls/README.md index 026efdb..5730f04 100644 --- a/rest/tls/README.md +++ b/rest/tls/README.md @@ -1,13 +1,13 @@ # TLS -For more inforamtion 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). +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). [Environment](https://www.ibm.com/docs/en/api-connect-graphql/saas?topic=environment-tls-configuration-properties). Check that the revision is newer than : 2025-11-18. ## Using `@rest(tls:)` -This examples demonstrates a number of StepZen capabilities: +This examples demonstrates a number of API Connect for GraphQL capabilities: - Use of `@rest(tls:)` - stepzen service - Simple ecmascript capability for reshaping data. @@ -20,7 +20,7 @@ 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 +- `cert` - the client certificate - `key` - the client certifcate key The data should be in PEM format. @@ -49,18 +49,21 @@ Two safe approaches are to set the environment variables from secrets or to have 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 @@ -77,7 +80,17 @@ stepzen service stop ``` -### Tricks +## 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. diff --git a/rest/tls/index.graphql b/rest/tls/index.graphql index 29590c7..233a241 100644 --- a/rest/tls/index.graphql +++ b/rest/tls/index.graphql @@ -1,4 +1,3 @@ schema @sdl(files: ["tls.graphql"]) { query: Query } - diff --git a/rest/tls/tls.graphql b/rest/tls/tls.graphql index e1e7b50..3a84243 100644 --- a/rest/tls/tls.graphql +++ b/rest/tls/tls.graphql @@ -6,9 +6,17 @@ type Query { 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")})} + function transformREST(s) { + return JSON.stringify( + {data100: s.length>100, + accept_8443: s.includes("-accept 8443") + }) + } """ ) } From 14bfb5f1c53573d4d14f0afd1a8c87abb3649e26 Mon Sep 17 00:00:00 2001 From: "Charlie C. Kim" <13868028+cckim@users.noreply.github.com> Date: Thu, 20 Nov 2025 09:17:21 -0800 Subject: [PATCH 5/5] remove dead comment --- rest/tls/README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/rest/tls/README.md b/rest/tls/README.md index 5730f04..eb7c6b4 100644 --- a/rest/tls/README.md +++ b/rest/tls/README.md @@ -3,8 +3,6 @@ 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). -[Environment](https://www.ibm.com/docs/en/api-connect-graphql/saas?topic=environment-tls-configuration-properties). Check that the revision is newer than : 2025-11-18. - ## Using `@rest(tls:)` This examples demonstrates a number of API Connect for GraphQL capabilities: