{"id":50385093,"url":"https://github.com/stackhpc/zenith-client-example","last_synced_at":"2026-05-30T14:30:57.801Z","repository":{"id":250916820,"uuid":"835851673","full_name":"stackhpc/zenith-client-example","owner":"stackhpc","description":null,"archived":false,"fork":false,"pushed_at":"2024-07-30T16:56:08.000Z","size":14,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2024-07-30T21:07:14.067Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Smarty","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/stackhpc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-07-30T16:51:53.000Z","updated_at":"2024-07-30T21:07:16.680Z","dependencies_parsed_at":"2024-07-30T21:19:25.600Z","dependency_job_id":null,"html_url":"https://github.com/stackhpc/zenith-client-example","commit_stats":null,"previous_names":["stackhpc/zenith-client-example"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/stackhpc/zenith-client-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackhpc%2Fzenith-client-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackhpc%2Fzenith-client-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackhpc%2Fzenith-client-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackhpc%2Fzenith-client-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/stackhpc","download_url":"https://codeload.github.com/stackhpc/zenith-client-example/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/stackhpc%2Fzenith-client-example/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33696681,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-30T02:00:06.278Z","response_time":92,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-05-30T14:30:57.720Z","updated_at":"2026-05-30T14:30:57.788Z","avatar_url":"https://github.com/stackhpc.png","language":"Smarty","funding_links":[],"categories":[],"sub_categories":[],"readme":"# zenith-client-example\n\nThis repository contains manifests and configuration for setting up a Zenith server on a\nKubernetes cluster and an example Zenith client that forwards traffic that comes down the\ntunnel to an echo server, allowing the headers sent by Zenith to be inspected.\n\nThe server and client should be deployed on different Kubernetes clusters in order to properly\ndemonstrate the traversal of NAT and/or firewalls. The cluster that the client is deployed on\ndoes not need egress from the internet, however it must be able to pull the required images\nand to reach the SSHD server that is part of the Zenith server.\n\n## Installing the server\n\n### NGINX ingress controller\n\nFirst, install [ingress-nginx](https://kubernetes.github.io/ingress-nginx/) on the target\ncluster, as Zenith relies on it. Zenith uses snippet annotations, which are disabled by\ndefault:\n\n```sh\nhelm upgrade ingress-nginx ingress-nginx \\\n  --install \\\n  --repo https://kubernetes.github.io/ingress-nginx \\\n  --namespace ingress-nginx \\\n  --create-namespace \\\n  --set controller.allowSnippetAnnotations=true \\\n  --wait\n```\n\n### TLS using Let's Encrypt\n\nTo provide TLS using [Let's Encrypt](https://letsencrypt.org/), we use\n[cert-manager](https://cert-manager.io/):\n\n```sh\nhelm upgrade cert-manager cert-manager \\\n  --install \\\n  --repo https://charts.jetstack.io \\\n  --namespace cert-manager \\\n  --create-namespace \\\n  --set installCRDs=true \\\n  --wait\n```\n\n```sh\nkubectl apply -f - \u003c\u003cEOF\napiVersion: cert-manager.io/v1\nkind: ClusterIssuer\nmetadata:\n  name: letsencrypt\nspec:\n  acme:\n    server: https://acme-v02.api.letsencrypt.org/directory\n    privateKeySecretRef:\n      name: letsencrypt-privkey\n    solvers:\n      - http01:\n          ingress:\n            class: nginx\nEOF\n```\n\n\u003e Note that this only works when the IP for the ingress controller is public-facing.\n\n### Zenith server\n\nGet the external IP of the service for the ingress controller:\n\n```sh\nINGRESS_IP=\"$(\n  kubectl get svc ingress-nginx-controller \\\n    --namespace ingress-nginx \\\n    --output jsonpath='{.status.loadBalancer.ingress[0].ip}'\n)\"\n```\n\nNext, we will need a wildcard DNS record that points to this IP (Zenith expects to\nbe given control of a whole subdomain). For testing, an [sslip.io](https://sslip.io/)\ndomain (or similar) is sufficient:\n\n```sh\nZENITH_DOMAIN=\"zenith.$(tr . - \u003c\u003c\u003c \"$INGRESS_IP\").sslip.io\"\n```\n\nGenerate a random signing key for the registrar:\n\n```sh\nZENITH_SIGNING_KEY=\"$(openssl rand -hex 32)\"\n```\n\nThen install the Zenith server:\n\n```sh\nhelm upgrade zenith-server zenith-server \\\n  --install \\\n  --repo https://stackhpc.github.io/zenith \\\n  --namespace zenith \\\n  --create-namespace \\\n  --set common.ingress.baseDomain=$ZENITH_DOMAIN \\\n  --set common.ingress.annotations.\"nginx\\.ingress\\.kubernetes\\.io/proxy-buffer-size\"=16k \\\n  --set common.ingress.tls.annotations.\"cert-manager\\.io/cluster-issuer\"=letsencrypt \\\n  --set registrar.config.subdomainTokenSigningKey=$ZENITH_SIGNING_KEY \\\n  --wait\n```\n\n## Launching the client\n\nWhile still connected to the cluster on which the server is deployed, reserve a subdomain\nto use with the client and store the information in a file. The endpoint for reserving a\nsubdomain is internal to the Zenith cluster, so we use `kubectl port-forward` to access it:\n\n```sh\nkubectl -n zenith port-forward svc/zenith-server-registrar 8080:80 \u003e/dev/null \u0026\nuntil nc -z localhost 8080; do sleep 1; done\ncurl -X POST http://localhost:8080/admin/reserve | jq \u003e subdomain.json\nkill $(jobs -p)\n```\n\nExtract information about the endpoints to use and put it into a file:\n\n```sh\ncat \u003c\u003cEOF \u003e zenith-server.env\nexport ZENITH_REGISTRAR_HOST=\"$(\n  kubectl get ingress zenith-server-registrar \\\n     --namespace zenith \\\n     --output jsonpath='{.spec.rules[0].host}'\n)\"\nexport ZENITH_SSHD_IP=\"$(\n  kubectl get svc zenith-server-sshd \\\n     --namespace zenith \\\n     --output jsonpath='{.status.loadBalancer.ingress[0].ip}'\n)\"\nEOF\n```\n\nGenerate an SSH keypair (no passphrase) for use with the client:\n\n```sh\nssh-keygen -t rsa -b 4096 -f test-key\n```\n\nNow, switch to the cluster on which the client should be deployed, and launch it using\nthe following command:\n\n```sh\nsource zenith-server.env\nhelm upgrade zenith-client ./client/ \\\n  --install \\\n  --set zenithClient.config.registrarUrl=\"https://$ZENITH_REGISTRAR_HOST\" \\\n  --set zenithClient.config.serverAddress=\"$ZENITH_SSHD_IP\" \\\n  --set zenithClient.sshKey.public=\"$(cat test-key.pub)\" \\\n  --set zenithClient.sshKey.private=\"$(cat test-key)\" \\\n  --set zenithClient.config.token=\"$(jq -r '.token' subdomain.json)\" \\\n  --wait\n```\n\nOnce the Zenith client app has launched, you can then visit the service by getting the\nFQDN from the subdomain file written earlier:\n\n```sh\nopen \"https://$(jq -r '.fqdn' subdomain.json)\"\n```\n\n### Enabling OIDC\n\nZenith natively supports using OIDC to authenticate services. To do this for an individual\nservice, you must first create a client in your OIDC identity provider using\n`https://\u003cservice fqdn\u003e/_oidc/callback` as the callback URL. This will result in a client ID and\nsecret which are passed to Zenith along with the URL of the identity provider - Zenith\nwill use the\n[discovery endpoint](https://swagger.io/docs/specification/authentication/openid-connect-discovery/)\nto discover how to connect with the provider:\n\n```sh\nsource zenith-server.env\nhelm upgrade zenith-client ./client/ \\\n  --install \\\n  --set zenithClient.config.registrarUrl=\"https://$ZENITH_REGISTRAR_HOST\" \\\n  --set zenithClient.config.serverAddress=\"$ZENITH_SSHD_IP\" \\\n  --set zenithClient.sshKey.public=\"$(cat test-key.pub)\" \\\n  --set zenithClient.sshKey.private=\"$(cat test-key)\" \\\n  --set zenithClient.config.token=\"$(jq -r '.token' subdomain.json)\" \\\n  --set zenithClient.config.authOidcIssuer=\"https://my-idp.com\" \\\n  --set zenithClient.config.authOidcClientId=\"\u003cclient-id\u003e\" \\\n  --set zenithClient.config.authOidcClientSecret=\"\u003cclient-secret\u003e\" \\\n  --wait\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackhpc%2Fzenith-client-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstackhpc%2Fzenith-client-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstackhpc%2Fzenith-client-example/lists"}