{"id":19039916,"url":"https://github.com/cert-manager/print-your-cert","last_synced_at":"2026-03-11T20:02:07.028Z","repository":{"id":40247535,"uuid":"490724986","full_name":"cert-manager/print-your-cert","owner":"cert-manager","description":"Get your certificate printed at the cert-manager booth at KubeCon!","archived":false,"fork":false,"pushed_at":"2026-02-28T13:33:24.000Z","size":460,"stargazers_count":8,"open_issues_count":11,"forks_count":6,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-02-28T17:14:59.847Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://cert-manager.github.io/print-your-cert/","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cert-manager.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-05-10T14:08:39.000Z","updated_at":"2025-12-16T10:52:13.000Z","dependencies_parsed_at":"2025-04-17T17:13:29.891Z","dependency_job_id":"57e48c1d-c301-47e0-9d70-33952fdc1cfe","html_url":"https://github.com/cert-manager/print-your-cert","commit_stats":{"total_commits":137,"total_committers":6,"mean_commits":"22.833333333333332","dds":0.2846715328467153,"last_synced_commit":"4fd0a3027295ab0ca0ce54e890700b8c18330fe5"},"previous_names":["maelvls/print-your-cert"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/cert-manager/print-your-cert","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cert-manager%2Fprint-your-cert","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cert-manager%2Fprint-your-cert/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cert-manager%2Fprint-your-cert/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cert-manager%2Fprint-your-cert/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cert-manager","download_url":"https://codeload.github.com/cert-manager/print-your-cert/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cert-manager%2Fprint-your-cert/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30398174,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-11T18:46:22.935Z","status":"ssl_error","status_checked_at":"2026-03-11T18:46:17.045Z","response_time":84,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":"2024-11-08T22:19:29.473Z","updated_at":"2026-03-11T20:02:07.008Z","avatar_url":"https://github.com/cert-manager.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/cert-manager/cert-manager/d53c0b9270f8cd90d908460d69502694e1838f5f/logo/logo-small.png\" height=\"256\" width=\"256\" alt=\"cert-manager project logo\" /\u003e\n\u003c/p\u003e\n\n# The \"Print your certificate!\" experiment at the cert-manager booth at KubeCon\n\nThis experiment was run at the cert-manager booth at KubeCon EU 2022 in\nValencia, KubeCon NA 2022 in Detroit, KubeCon EU 2023 in Amsterdam,\nKubeCon NA 2023 in Chicago, and Kubecon EU 2024 in Paris.\n\n⚠️ Except for the URL \u003ccert-manager.github.io/print-your-cert/\u003e which should\nwork forever, the other URLs and IPs presented in this README are temporary.\n\n\u003cimg alt=\"Photo of the cert-manager booth when we were running the Print your certificate experiment. A participant can be seen typing their name and email on the keyboard.\" src=\"https://user-images.githubusercontent.com/2195781/170957591-0cfcfb4d-05d8-41ad-bfa6-f6162e36479f.jpeg\" width=\"300\"/\u003e \u003cimg alt=\"Liz Rice met with the cert-manager maintainers Charlie Egan, Josh van Leeuwen, and Jake Sanders with the cert-manager booth in the background. All credits for this image go to Liz Rice who shared this picture on Twitter at https://twitter.com/lizrice/status/1527585297743110145.\" src=\"https://user-images.githubusercontent.com/2195781/170959280-f78822a4-1ba8-416c-91dc-5e7dbc5da24b.png\" width=\"300\"/\u003e \u003cimg alt=\"Dovy came to the cert-manager booth and took a picture of the card on which a stamp of the cert-manager project is visible, as well as the label showing their X.509 certificate. All credits for this photo go to Dovy who shared this picture on Twitter at https://twitter.com/ddovys/status/1526890240568344576.\" src=\"https://user-images.githubusercontent.com/2195781/170959287-125e5fab-52ab-43f5-8781-94af0d3cbb83.png\" width=\"300\"/\u003e\n\n- [Video and slides](#video-and-slides)\n- [Description of the experiment](#description-of-the-experiment)\n- [What's the stack?](#whats-the-stack)\n- [Staff: test things](#staff-test-things)\n- [Running everything on the Raspberry Pi (on the booth)](#running-everything-on-the-raspberry-pi-on-the-booth)\n  - [Booth: Initial set up of the Raspberry Pi](#booth-initial-set-up-of-the-raspberry-pi)\n  - [Booth: Set Up Tailscale on the Raspberry Pi](#booth-set-up-tailscale-on-the-raspberry-pi)\n  - [Booth: Make sure you can SSH into the Rasberry Pi](#booth-make-sure-you-can-ssh-into-the-rasberry-pi)\n  - [Booth: Set Up Docker, Helm, K3d, and kubectl](#booth-set-up-docker-helm-k3d-and-kubectl)\n  - [Booth: Set up the tunnel between the Internet and the Raspberry Pi](#booth-set-up-the-tunnel-between-the-internet-and-the-raspberry-pi)\n  - [Prerequisite: install k3s on the Raspberry Pi](#prerequisite-install-k3s-on-the-raspberry-pi)\n  - [Booth: Configure kubectl on your laptop to access the Raspberry Pi's cluster](#booth-configure-kubectl-on-your-laptop-to-access-the-raspberry-pis-cluster)\n  - [Booth: Install cert-manager and the issuers on the Raspberry Pi](#booth-install-cert-manager-and-the-issuers-on-the-raspberry-pi)\n  - [Booth: Run the UI on the Raspberry Pi](#booth-run-the-ui-on-the-raspberry-pi)\n  - [Booth: Running the printer controller on the Raspberry Pi](#booth-running-the-printer-controller-on-the-raspberry-pi)\n- [Local development](#local-development)\n  - [Local development on the UI](#local-development-on-the-ui)\n  - [Local development on the controller (that creates PNGs and prints them)](#local-development-on-the-controller-that-creates-pngs-and-prints-them)\n    - [`cert-card`](#cert-card)\n    - [Testing the printer](#testing-the-printer)\n    - [Testing cert-card](#testing-cert-card)\n- [Troubleshooting](#troubleshooting)\n  - [From the CLI: `usb.core.USBError: [Errno 13] Access denied (insufficient permissions)`](#from-the-cli-usbcoreusberror-errno-13-access-denied-insufficient-permissions)\n  - [From the CLI: `usb.core.USBError: [Errno 16] Resource busy`](#from-the-cli-usbcoreusberror-errno-16-resource-busy)\n  - [From the web UI: `No such file or directory: '/dev/usb/lp1'`](#from-the-web-ui-no-such-file-or-directory-devusblp1)\n\n## Video and slides\n\nHere is a short video showing what the experiment looked like on Friday 20 May 2022\nat KubeCon Valencia:\n\n[![A minute at the cert-manager booth with the \"Print your certificate\" experiment at KubeCon 2022 in Valencia](https://user-images.githubusercontent.com/2195781/170956255-c7b4b36e-6405-431c-991c-8f1352aaf2a1.jpg)](https://www.youtube.com/watch?v=7Gyt4-yVTN8 \"A minute at the cert-manager booth (KubeCon EU 2022 in València)\")\n\nHere are the slides Mael presented after KubeCon:\n\n\u003cimg width=\"500\" alt=\"Print your cert, KubeCon 2022 Valencia\" src=\"https://user-images.githubusercontent.com/2195781/185626468-9c3f5857-cc2f-47c4-af0a-0d677fc64533.png\"/\u003e\u003cimg width=\"500\" alt=\"Print your cert, KubeCon 2022 Valencia (1)\" src=\"https://user-images.githubusercontent.com/2195781/185626527-d94824b6-e68a-4624-8fa0-ade370cb0701.png\"/\u003e\u003cimg width=\"500\" alt=\"Print your cert, KubeCon 2022 Valencia (2)\" src=\"https://user-images.githubusercontent.com/2195781/185626500-ba19f5e0-0bda-49aa-9972-717f820e509e.png\"/\u003e\u003cimg width=\"500\" alt=\"Print your cert, KubeCon 2022 Valencia (3)\" src=\"https://user-images.githubusercontent.com/2195781/185626540-b7794961-83ad-4c28-b3e5-7b357d24d7a4.png\"/\u003e\u003cimg width=\"500\" alt=\"Print your cert, KubeCon 2022 Valencia (4)\" src=\"https://user-images.githubusercontent.com/2195781/185626551-e690ff66-da3d-4fe1-8496-697530c277e8.png\"/\u003e\n\n## Description of the experiment\n\nWhen visiting the cert-manager booth, you will be welcomed and one of the staff\nmay suggest to visit a QR code from their phone to participate to the \"Print\nyour certificate!\" experiment, or to use the Raspberry Pi's keyboard and screen\navailable on the booth.\n\nUpon opening the QR code link (or on the Raspberry Pi's screen), the participant\nis shown a web page prompting for a name and email:\n\n\u003cimg alt=\"landing-1\" src=\"https://user-images.githubusercontent.com/2195781/170956946-2f39e7d9-2b02-4ff8-a77f-e731c6db4510.png\" width=\"500\"/\u003e\n\nThe issuance takes less than a second, and the participant is redirected to a new page where they\ncan see a receipt of their certificate. A button \"Print your certificate\" appears:\n\n\u003cimg alt=\"landing-4\" src=\"https://user-images.githubusercontent.com/2195781/170957142-4ee0ab2a-067f-41ff-9e80-20c3d9b14fb1.png\" width=\"500\"/\u003e\n\nWhen clicking on \"Print your certificate\", the participant is told that their\ncertificate will shortly be printed.\n\nThe printer, installed on the booth, starts printing two labels: one for the\nfront side, and one for the back side. The booth staff sticks the two printed\nlabels onto a black-colored card (format A7), and uses the wax gun and the wax\nstamp to stamp the card.\n\n\u003e Because the label is made of plastic, and the wax is hot, it is advised to the\n\u003e staff not to put stamp in contact of the label.\n\nThe front-side label looks like this:\n\n\u003cimg src=\"https://user-images.githubusercontent.com/2195781/168418627-0952377f-5a1d-4dbe-a41f-80cf99430b77.png\" width=\"300\" alt=\"front\"/\u003e\n\nThe back-side label looks like this:\n\n\u003cimg src=\"https://user-images.githubusercontent.com/2195781/168418632-8650a78a-d540-4831-9238-dd59b9994a2b.png\" width=\"200\" alt=\"back\"/\u003e\n\nThe person can choose the color of the card onto which the cert-manager booth\nstaff will put the two labels that were automatically printed on. I purchased\n200 cards of each color (1400 total), so it should be enough:\n\n\u003cimg alt=\"a7-sized-card-1\" src=\"https://user-images.githubusercontent.com/2195781/168466048-39aa8109-01cf-44f6-ac6a-3f90ec9e355c.jpg\" width=\"300\"/\u003e\u003cimg alt=\"a7-sized-card-2\" src=\"https://user-images.githubusercontent.com/2195781/168466050-d9f20184-8c96-4120-80cc-5b8b0fbd6936.jpg\" width=\"300\"/\u003e\n\nHere is what it may look like for real. Since I didn't have the above cards for\nthe prototype, I have cut a piece of cardboard with the A7 size (7.4 x 10.5 cm).\nThe label on the front is 6.2 x 8.7 cm, and the wax stamp is 4 cm large.\n\n\u003cimg alt=\"card-draft-front\" src=\"https://user-images.githubusercontent.com/2195781/168466186-4559cf12-ee44-42a1-bb24-0e991e09b287.jpeg\" width=\"300\"/\u003e\u003cimg alt=\"card-draft-back\" src=\"https://user-images.githubusercontent.com/2195781/168466187-5ffb4c96-cd70-4612-ac43-f4169b3ee427.jpeg\" width=\"300\"\u003e\n\nThe \"real\" colored cards will be smaller (5.4 x 9.0 cm) meaning that I will have\nto do a smaller label on both sides.\n\nThe back-side labels is a QR code containing the PEM-encoded certificate that\nwas issued. Since we didn't find any good use for TLS, we didn't include the\nprivate key.\n\nI wanted the smallest TLS certificate possible. After reading [Smallest possible\ncertificate for IoT\ndevice](https://crypto.stackexchange.com/questions/83719/smallest-possible-certificate-for-iot-device),\nit seems ECDSA is good for small signatures, and RSA is not good. The\nconfiguration for the ECDSA signature is shown below in\n[print-your-cert-ca](#print-your-cert-ca).\n\nThe QR code contains a URL of the form:\n\n```sh\nhttps://cert-manager.github.io/print-your-cert/?asn1=MIICXDCCAgOgAwIU...O7pAkqhQc%3D)\n\u003c---------------------------------\u003e       \u003c-------------------------\u003e\n      Hosted on GitHub Pages                   The base-64 encoded and\n                                               URL-encoded PEM-encoded\n                                               certificate without the headers.\n```\n\nFor example:\n\n\u003chttps://cert-manager.github.io/print-your-cert/?asn1=MIICXDCCAgOgAwIBAgIQdPaTuGSUDeosii4dbdLBgTAKBggqhkjOPQQDAjAnMSUwIwYDVQQDExxUaGUgY2VydC1tYW5hZ2VyIG1haW50YWluZXJzMB4XDTIyMDUxNjEzMDkwMFoXDTIyMDgxNDEzMDkwMFowLDEqMCgGA1UEAwwhZm9vIGJhciBmb28gYmFyIDxmb28uYmFyQGJhci5mb28%2BMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmGM5lil9Vw%2Fy5LhpgO8t5gSb5oUo%2BDp5vWw0Z5C7rjvifi0%2FeD9MbVFkxb%2B%2BhmOaaNCVgqDUio1OBOZyL90KzdnGW7nz1fRM2KCNrDF5Y1mO7uv1ZTZa8cVBjF67KjFuNkvvHp74m65bKwXeCHXJBmO3Z1FH8hudICU74%2BNl6tyjlMOsTHv%2BLY0jPfmAtO6eR%2BEf%2FHvgzwsjKds12vdlRCdHSS6u5zlrZZxF3zTO7YuAM7mN8Wbjq94YcpgsJ5ssNOtMu9FwZtPGQDHPaQyVQ86FfjhmMi1IUOUAAGwh%2FQRv8ksX%2BOupHTNdH06WmIDCaGBjWFgPkwicavMZgZG3QIDAQABo0EwPzAOBgNVHQ8BAf8EBAMCBaAwDAYDVR0TAQH%2FBAIwADAfBgNVHSMEGDAWgBQG5XQnDhOUa748L9H7TWZN2avluTAKBggqhkjOPQQDAgNHADBEAiBXmyJ24PTG76pEyq6AQtCo6TXEidqJhsmK9O5WjGBw7wIgaPbcFI5iMMgfPGEATH2AGGutZ6MlxBmwhEO7pAkqhQc%3D\u003e\n\n\u003e \u003ca id=asn1\u003e\u003c/a\u003e **⁉️ How do we get this URL?** First, take a PEM-encoded\n\u003e certificate. It will looks like this:\n\u003e\n\u003e ```text\n\u003e -----BEGIN CERTIFICATE-----\n\u003e MIIDBzCCAe+gAyPj/8QWMBQUAMIGLMQswCQYD\n\u003e wIBAgMIG+LMQswCQYDAOPj/8QAaDMBQEFAwUa\n\u003e ...\n\u003e -----END CERTIFICATE-----\n\u003e ```\n\u003e\n\u003e It takes three steps to turn this PEM-encoded certificate into something that\n\u003e can be given with the query parameter `?asn1=...`.\n\u003e\n\u003e 1. We remove the header and footer, i.e., we remove the lines `-----BEGIN CERTIFICATE-----` and `-----END CERTIFICATE-----`). The result looks like\n\u003e    this:\n\u003e\n\u003e    ```text\n\u003e    MIIDBzCCAe+gAyPj/8QWMBQUAMIGLMQswCQYD\n\u003e    wIBAgMIG+LMQswCQYDAOPj/8QAaDMBQEFAwUa\n\u003e    ```\n\u003e\n\u003e 2. (optional) We can save a few bytes by removing the newlines. The result is:\n\u003e\n\u003e    ```text\n\u003e    MIIDBzCCAe+gAyPj/8QWMBQUAMIGLMQswCQYDwIBAgMIG+LMQswCQYDAOPj/8QAaDMBQEFAwUa\n\u003e    ```\n\u003e\n\u003e 3. At this point, we have the ASN.1 certificate encoded in base 64. We have to\n\u003e    URL-encode it, which gives:\n\u003e\n\u003e    ```text\n\u003e    MIIDBzCCAe%2BgAyPj%2F8QWMBQUAMIGLMQswCQYDwIBAgMIG%2BLMQswCQYDAOPj%2F8QAaDMBQEFAwUa%0A\n\u003e    ```\n\u003e\n\u003e 4. Copy this into the URL:\n\u003e\n\u003e    ```text\n\u003e    https://cert-manager.github.io/print-your-cert?asn1=MIIDBzCCAe%2BgAyPj%2F8QWMBQUAMIGLMQswCQYDwIBAgMIG%2BLMQswCQYDAOPj%2F8QAaDMBQEFAwUa%0A\n\u003e    ```\n\u003e\n\u003e One-line that takes a PEM-encoded certificate and returns a URL:\n\u003e\n\u003e ```sh\n\u003e cat \u003c\u003cEOF | grep -v CERTIFICATE | tr -d $'\\n' | python3 -c \"import urllib.parse; print(urllib.parse.quote_plus(open(0).read()))\" | (printf \"https://cert-manager.github.io/print-your-cert?asn1=\"; cat)\n\u003e -----BEGIN CERTIFICATE-----\n\u003e MIIDBzCCAe+gAyPj/8QWMBQUAMIGLMQswCQYD\n\u003e wIBAgMIG+LMQswCQYDAOPj/8QAaDMBQEFAwUa\n\u003e ...\n\u003e -----END CERTIFICATE-----\n\u003e EOF\n\u003e ```\n\nOn the certificate page, the participant can also see their certificate by\nclicking on the button \"Print your certificate\". The PEM-encoded certificate is\nshown in the browser:\n\n\u003cimg alt=\"download\" src=\"https://user-images.githubusercontent.com/2195781/168419122-1bf3d0dd-c474-4d47-a55e-56980ed16441.png\" width=\"500\"/\u003e\n\nOn the booth, we have a 42-inch display showing the list of certificates\n(\u003chttps://print-your-cert.cert-manager.io/list\u003e):\n\n\u003cimg alt=\"list\" src=\"https://user-images.githubusercontent.com/2195781/168419219-fb3e5eb7-672e-4792-9ac3-40cf8e6b251d.png\" width=\"300\"/\u003e\n\nAnd that's it: you have a certificate that proves that you were at the KubeCon\ncert-manager booth! The CA used during the conference will be available at some\npoint so that people can verify the signature.\n\n## What's the stack?\n\n```text\nhttps://print-your-cert.cert-manager.io\n                |\n                |\n                v\n            VM on GCP\n                |\n                |  Caddy + Tailscale\n                |  (see section below)\n                |\n                v\n+---------------------------------+\n|               Pi                |\n|  K3s cluster                    |   USB   +-------------------+\n|    cert-manager                 | ------\u003e | Brother QL-820NWB |\n|    print-your-cert-ui (:8080)   |         +-------------------+\n|    print-your-cert-controller   |                (on the booth)\n+---------------------------------+\n                |    (on the booth)\n          HDMI  |\n                v\n     +-------------------+\n     | list of certs     |\n     | already printed   | 42\" display.\n     |                   |\n     +-------------------+\n            (on the booth)\n```\n\n## Staff: test things\n\nFor anyone who is authorized and wants to test or debug things:\n\n- [Install tailscale](https://tailscale.com/download/).\n- Run `tailscale up`, it should open something in your browser → \"Sign in\n  with GitHub\" → Authorize Tailscale → Multi-user Tailnet cert-manager.\n- If \u003chttp://print-your-cert.cert-manager.io/\u003e doesn't work, then the frontend UI\n  is at \u003chttp://100.121.173.5:8080/\u003e.\n- You can test that the printer works at \u003chttp://100.121.173.5:8013/\u003e.\n- You can SSH into the Pi (which runs a Kubernetes cluster) as long as\n  you are a member of the cert-manager org:\n\n  ```sh\n  ssh pi@100.121.173.5\n  ```\n\n## Running everything on the Raspberry Pi (on the booth)\n\nOnce on the booth, you will need to perform these ten tasks:\n\n1. [Booth: Initial set up of the Raspberry Pi](#booth-initial-set-up-of-the-raspberry-pi)\n1. [Booth: Set Up Tailscale on the Raspberry Pi](#booth-set-up-tailscale-on-the-raspberry-pi)\n1. [Booth: Make sure you can SSH into the Rasberry Pi](#booth-make-sure-you-can-ssh-into-the-rasberry-pi)\n1. [Booth: Set Up Docker, Helm, K3d, and kubectl](#booth-set-up-docker-helm-k3d-and-kubectl)\n1. [Booth: Set up the tunnel between the Internet and the Raspberry Pi](#booth-set-up-the-tunnel-between-the-internet-and-the-raspberry-pi)\n1. [Prerequisite: install k3s on the Raspberry Pi](#prerequisite-install-k3s-on-the-raspberry-pi)\n1. [Booth: Configure kubectl on your laptop to access the Raspberry Pi's cluster](#booth-configure-kubectl-on-your-laptop-to-access-the-raspberry-pis-cluster)\n1. [Booth: Install cert-manager and the issuers on the Raspberry Pi](#booth-install-cert-manager-and-the-issuers-on-the-raspberry-pi)\n1. [Booth: Run the UI on the Raspberry Pi](#booth-run-the-ui-on-the-raspberry-pi)\n1. [Booth: Running the printer controller on the Raspberry Pi](#booth-running-the-printer-controller-on-the-raspberry-pi)\n\n### Booth: Initial set up of the Raspberry Pi\n\n\u003e [!WARNING]\n\u003e If you need to upgrade Debian on the Raspberry Pi (`apt upgrade`),\n\u003e please upgrade it at least a week before KubeCon so that any breakage (e.g.,\n\u003e the Raspberry UI) can be fixed before the venue! We mistakenly ran `sudo apt\nupgrade` on the first day of KubeCon in Amsterdam and ended up spending half\n\u003e of the day fixing it!\n\nFirst, unplug the micro SD card from the Raspberry Pi and plug it into your\nlaptop using a micro-SD-to-SD card adaptor.\n\nThen, install Raspberry OS on the microSD card using the Imager program.\nIn the Imager program settings, change the username to `certmanager` and the password\nto something secret.\n\nBe sure also to enable SSH with password authentication.\n\nYou can also use the imager to setup a WiFi connection, which might be helpful\nwith initial setup at home.\n\nAt the conference, you should be able to use the GUI to set up a WiFi connection.\n\n\u003e If the Wifi doesn't work, somehow SSH into the Pi and run `wpa_cli`:\n\u003e\n\u003e ```console\n\u003e $ sudo wpa_cli status\n\u003e Selected interface 'p2p-dev-wlan0'\n\u003e wpa_state=DISCONNECTED\n\u003e p2p_device_address=e6:5f:01:a6:66:00\n\u003e address=e6:5f:01:a6:66:00\n\u003e uuid=0fb4e5b4-b372-5253-93e9-fa6f2c4d8037\n\u003e ```\n\u003e\n\u003e To look for the right SSID, run on the Pi:\n\u003e\n\u003e ```sh\n\u003e wpa_cli scan \u0026\u0026 wpa_cli scan_results\n\u003e ```\n\u003e\n\u003e Then edit the file `/etc/wpa_supplicant/wpa_supplicant.conf` and run:\n\u003e\n\u003e ```sh\n\u003e sudo wpa_cli -i wlan0 reconfigure\n\u003e sudo ifconfig wlan0 down\n\u003e sudo ifconfig wlan0 up\n\u003e ```\n\nThen, unmount the micro SD card from your laptop and plug it into the Raspberry Pi.\n\n### Booth: Set Up Tailscale on the Raspberry Pi\n\nPlug a keyboard and mouse to the Raspberry and install Tailscale:\n\n```bash\n# From the Raspberry Pi:\ncurl -fsSL https://tailscale.com/install.sh | sh\n```\n\nThen, login with the following command. It will open a browser window, allowing\nyou to log in. Use your GitHub account to log in (Sign In with GitHub -\u003e\nAuthorize Tailscale -\u003e Multi-user Tailnet -\u003e select tailnet\n`cert-manager.org.github`).\n\n```sh\ntailscale up --accept-dns=false\n```\n\n\u003e [!IMPORTANT]\n\u003e\n\u003e Make sure to disable Tailscale's DNS resolution with `--accept-dns=false`. We\n\u003e have seen a ton of problems with Tailscale's DNS resolution.\n\nIf you prefer staying logged in to your personal tailnet, feel free to share the\nmachine with your Tailnet in \u003chttps://login.tailscale.com/admin/machines\u003e. That\nway, you can stay logged to your tailnet but still be able to access the\nRaspberry Pi.\n\n#### Allowing SSH Access to cert-manager org members\n\nIf desired, the script below will allow SSH access to cert-manager org members.\n\nThis might be overkill; you can also just download any GitHub user's SSH keys\nusing `curl -LO https://github.com/\u003cuser\u003e.keys` and then append the contents of\nthat file to `.ssh/authorized_keys`.\n\n```bash\ncurl -sH \"Authorization: token $(lpass show github.com -p)\" https://api.github.com/orgs/cert-manager/members \\\n  | jq '.[] | .login' -r \\\n  | ssh -t pi@100.85.65.38 \\\n    'set -xe; while read -r i; do curl -LsS https://github.com/$i.keys | tee -a $HOME/.ssh/authorized_keys; done; cat $HOME/.ssh/authorized_keys | sort | sed -re 's/\\s+$//' | uniq \u003ea; mv a $HOME/.ssh/authorized_keys'\n```\n\n### Booth: Make sure you can SSH into the Rasberry Pi\n\nFirst, make sure you have Tailscale installed and running. Make sure to be\n[login](https://login.tailscale.com/) using your GitHub account, and select the\nTailnet `cert-manager.org.github`. The Pi is shared to that Tailnet.\n\nThen, edit your `~/.ssh/config` to add the following:\n\n```text\nHost pi\n  HostName 100.85.65.38\n  User pi\n```\n\nAll the commands below assume that you have configured your `~/.ssh/config` as\nabove.\n\n### Booth: Set Up Docker, Helm, K3d, and kubectl\n\nMake sure you have configured `~/.ssh.config` in the section above. Then, SSH\ninto the Pi:\n\n```bash\nssh pi\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e You may see the following error message when SSHing into the Pi:\n\u003e\n\u003e ```text\n\u003e bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8)\n\u003e ```\n\u003e\n\u003e To fix this, you can run the following command:\n\u003e\n\u003e ```sh\n\u003e # From the Raspberry Pi.\n\u003e sudo tee -a /etc/environment \u003c\u003c\u003c\"LC_ALL=en_US.UTF-8\"\n\u003e sudo tee /etc/locale.gen \u003c\u003c\u003c\"en_US.UTF-8 UTF-8\"\n\u003e sudo tee /etc/locale.conf \u003c\u003c\u003c\"LANG=en_US.UTF-8\"\n\u003e sudo locale-gen en_US.UTF-8\n\u003e EOF\n\u003e ```\n\nThen, install Docker with the command:\n\n```bash\n# From the Raspberry Pi:\ncurl -fsSL https://get.docker.com | sudo bash\nsudo groupadd docker\nsudo usermod -aG docker $USER\nnewgrp docker\n```\n\n`k3s` [requires the memory cgroup\nv2](https://github.com/k3s-io/k3s-ansible/issues/179#issuecomment-1065685291).\nTo enable it, add the following flags to `/boot/cmdline.txt`:\n\n```text\ncgroup_memory=1 cgroup_enable=memory\n```\n\nAlso install `vim` and `jq`:\n\n```bash\n# From the Raspberry Pi:\nsudo apt install -y vim jq\n```\n\nFinally, install `k3d`, `helm`, and `kubectl`:\n\n```bash\n# From the Raspberry Pi:\ncurl -Ls https://raw.githubusercontent.com/rancher/k3d/main/install.sh | bash\ncurl -Ls https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash\ncurl -LO \"https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl\"\nsudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl\n```\n\n### Booth: Set up the tunnel between the Internet and the Raspberry Pi\n\nWe want to expose the print-your-cert UI on the Internet at\n\u003chttps://print-your-cert.cert-manager.io\u003e. To do that, we use a f1-micro VM on\nGCP and use Caddy to terminate the TLS connections and to forward the\nconnections to the Raspberry Pi's Tailscale IP.\n\n```text\nhttps://print-your-cert.cert-manager.io\n              |\n              |\n              v  35.241.231.131 (eth0)\n    +------------------------+\n    |  VM \"print-your-cert\"  |\n    |    Caddy + Tailscale   |\n    +------------------------+\n              |  100.106.168.42 (tailscale0)\n              |\n              |\n              |\n              v  100.85.65.38 (tailscale0)\n    +-------------------+\n    |       Pi          |\n    |                   |\n    |     :8080 (UI)    |\n    +-------------------+\n```\n\nBefore creating the VM, you will need to get access to the GCP project\n`cert-manager-general`. You can get access by being a maintainer on the\ncert-manager project and requesting to be added to the GCP project.\n\nThen, you will need to make sure you are logged into Tailscale (see one of the\nprevious sections).\n\nTo create the VM `print-your-cert`, you can use the following command:\n\n\u003e [!NOTE]\n\u003e\n\u003e Use the GCP zone closest to the KubeCon venue. The examples below use\n\u003e `europe-west1-c` (Belgium). Try to pick a zone with low CO2 emissions.\n\n\u003e [!NOTE]\n\u003e\n\u003e Find out the latest debian image by running:\n\u003e\n\u003e ```bash\n\u003e gcloud compute images list | grep debian\n\u003e ```\n\n```shell\n# From your laptop:\ngcloud compute firewall-rules create allow-tailscale \\\n    --project cert-manager-general \\\n    --network default \\\n    --action allow \\\n    --direction ingress \\\n    --rules udp:41641 \\\n    --source-ranges 0.0.0.0/0\ngcloud compute instances create print-your-cert \\\n    --project cert-manager-general \\\n    --network default \\\n    --machine-type=f1-micro \\\n    --image-family=debian-12 \\\n    --image-project=debian-cloud \\\n    --can-ip-forward \\\n    --boot-disk-size=10GB \\\n    --zone=europe-west1-c\n```\n\nThen, copy-paste the IP into the print-your-cert.cert-manager.io zone:\n\n1. Copy the IP:\n\n   ```bash\n   IP=$(gcloud compute instances describe print-your-cert \\\n       --project cert-manager-general \\\n       --zone=europe-west1-c --format json \\\n         | jq -r '.networkInterfaces[].accessConfigs[] | select(.type==\"ONE_TO_ONE_NAT\") | .natIP')\n   ```\n\n2. The zone `print-your-cert.cert-manager.io` is a delegated zone meant for print-your-cert.\n   Anyone in the Google group \u003cteam-cert-manager@jetstack.io\u003e can update the `A` record:\n\n   ```bash\n   gcloud dns record-sets update --project cert-manager-io \\\n     --zone print-your-cert-cert-manager-io \\\n     --type=A --ttl=300 print-your-cert.cert-manager.io --rrdatas=$IP\n   ```\n\nThen, install Tailscale and make sure IP forwarding is enabled on the VM:\n\n```sh\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c print-your-cert -- 'curl -fsSL https://tailscale.com/install.sh | sh'\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c print-your-cert -- \\\n    \"sudo perl -ni -e 'print if \\!/^net.ipv4.ip_forward=1/d' /etc/sysctl.conf; \\\n     sudo tee -a /etc/sysctl.conf \u003c\u003c\u003cnet.ipv4.ip_forward=1; \\\n     sudo sysctl -w net.ipv4.ip_forward=1\"\n```\n\nWe found that the default-http and default-https now require tags to be set:\n\n```bash\ngcloud compute instances add-tags print-your-cert --project cert-manager-general --zone europe-west1-c --tags http-server\ngcloud compute instances add-tags print-your-cert --project cert-manager-general --zone europe-west1-c --tags https-server\n```\n\nThen, run the following. Click the link that shows and log into Tailscale using\n\"Login with GitHub\", and then select the Tailnet `cert-manager.org.github`.\n\n```sh\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c print-your-cert -- sudo tailscale up\n```\n\nFinally, install Caddy as a systemd unit (these commands are inspired from [the\nofficial guide](https://caddyserver.com/docs/install#debian-ubuntu-raspbian)):\n\n```sh\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c print-your-cert -- bash \u003c\u003c'EOF'\nsudo apt install -y debian-keyring debian-archive-keyring apt-transport-https\ncurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg\ncurl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list\nsudo apt update\nsudo apt install caddy\nEOF\n```\n\n```sh\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c print-your-cert -- bash \u003c\u003c'EOF'\nsudo tee /etc/caddy/Caddyfile \u003c\u003cCADDY\nprint-your-cert.cert-manager.io:443 {\n        reverse_proxy 100.85.65.38:8080\n}\nCADDY\nsudo systemctl restart caddy.service\nEOF\n```\n\nFinally, you'll need to ensure that the tailscale ACLs allow traffic to the pi.\n\nVisit [the Tailscale admin panel](https://login.tailscale.com/admin/acls/file) and set the \"hosts\" key\nto include the Pi, and ensure that there's an ACL allowing traffic:\n\n```text\n\"acls\": [\n    {\"action\": \"accept\", \"users\": [\"*\"], \"ports\": [\"raspberrypi:*\"]},\n]\n```\n\n### Prerequisite: install k3s on the Raspberry Pi\n\nThis prerequisite is useful both for local development and for running the\nexperiment on the Raspberry Pi.\n\nFirst, install the following tools on the Raspberry Pi:\n\n- [Docker](https://docs.docker.com/engine/install/debian/),\n- [K3d](https://k3d.io/stable/#install-current-latest-release),\n- [Helm](https://helm.sh/docs/intro/install/).\n\nThe first step is to create a cluster with a cert-manager issuer:\n\n\u003ca id=\"print-your-cert-ca\"\u003e\u003c/a\u003e\n\n```sh\n# From the Raspberry Pi:\nk3d cluster create --k3s-arg=\"--tls-san=$(tailscale ip -4)@server:*\"\n```\n\n### Booth: Configure kubectl on your laptop to access the Raspberry Pi's cluster\n\nFirst, make sure you can SSH to the Raspberry Pi over Tailscale in one of the\nabove sections.\n\nThen, run the following:\n\n```sh\nssh pi 'k3d kubeconfig get -a | sed \"s/0.0.0.0/$(tailscale ip -4)/g\"' \u003e/tmp/kc \\\n  \u0026\u0026 KUBECONFIG=/tmp/kc:$HOME/.kube/config k config view --flatten \u003ekc \\\n  \u0026\u0026 mv kc ~/.kube/config\n```\n\n### Booth: Install cert-manager and the issuers on the Raspberry Pi\n\nFirst install [`age`](https://age-encryption.org/) to be able to decrypt the\nsecrets:\n\n```sh\n# From your laptop:\nbrew install age\n```\n\nInstall cert-manager:\n\n```sh\n# From the Raspberry Pi:\nhelm repo add jetstack https://charts.jetstack.io --force-update\nhelm upgrade --install -n cert-manager cert-manager jetstack/cert-manager \\\n  --create-namespace --set installCRDs=true\n```\n\nThen, apply the ClusterIssuer:\n\n```sh\nkubectl apply -f root_issuer_prod.yaml\nkubectl apply -f cluster_issuer.yaml\n```\n\nThen, decrypt the root CA. The passphrase is available in the Venafi 1Password\nin the `cert-manager-team` vault.\n\n```bash\n# From your laptop:\nage -d root-print-your-cert-ca.yaml.age \u003eroot-print-your-cert-ca.yaml\nscp root-print-your-cert-ca.yaml pi:\n```\n\nFinally, apply the decrypted root CA secret:\n\n```bash\n# From the Raspberry Pi:\nkubectl apply -f root-print-your-cert-ca.yaml\n```\n\n### Booth: Run the UI on the Raspberry Pi\n\nThe UI doesn't run in Kubernetes (just because...). It runs as a container. It\nis a simple Go binary that serves an HTML site. Its container image name is\n`ghcr.io/cert-manager/print-your-cert-ui:latest`.\n\nThe following command will build the image on your laptop (faster than on the\nPi) and then load the image on the Pi:\n\n```sh\n# From your laptop:\nKO_DOCKER_REPO=ghcr.io/cert-manager/print-your-cert-ui ko build . --platform linux/arm64 --tarball print-your-cert-ui.tar --push=false --bare\nssh pi docker load \u003cprint-your-cert-ui.tar\n```\n\nNow, ssh into the Raspberry Pi and launch the UI:\n\n```sh\n# From your laptop.\nssh pi docker rm -f print-your-cert-ui\nssh pi docker run -d --restart=always --name print-your-cert-ui --net=host \\\n  -v '/home/certmanager/.kube/config:/home/nonroot/.kube/config' \\\n  ghcr.io/cert-manager/print-your-cert-ui:latest \\\n  --issuer print-your-cert-ca \\\n  --issuer-kind ClusterIssuer \\\n  --listen 0.0.0.0:8080 \\\n  --guestbook-ca=\"\"\n```\n\nSet `--guestbook-ca` to `\"\"` because we manually issued a Let's Encrypt\ncertificate for the guestbook.\n\n\u003e [!NOTE]\n\u003e\n\u003e We don't actually push the image to GHCR. We just load it directly to the Raspberry Pi.\n\n\u003e [!NOTE]\n\u003e\n\u003e Why not skip buildx and use `ko` instead? That's because the base images that\n\u003e `ko` relies on don't support the Rasberry Pi's `arm64/v8` architecture:\n\u003e\n\u003e ```console\n\u003e $ crane manifest cgr.dev/chainguard/static | jq -r '.manifests[].platform | \"\\(.os)/\\(.architecture)\"'\n\u003e linux/amd64\n\u003e linux/arm\n\u003e linux/arm64\n\u003e linux/ppc64le\n\u003e linux/s390x\n\u003e ```\n\n### Booth: Running the printer controller on the Raspberry Pi\n\nThe printer controller is a simple Bash script (yeah, not Go). It doesn't run in\nKubernetes just because it makes it easier to hot-reload everything on the\nbooth. `ghcr.io/cert-manager/print-your-cert-controller:latest` is the container\nimage name.\n\nMake sure that the k3s cluster is running that cert-manager is installed. If\nnot, follow the section [Prerequisite: install k3s on the Raspberry\nPi](#prerequisite-install-k3s-on-the-raspberry-pi).\n\nYou may need to install Qemu if you are on Linux:\n\n```bash\n# From your laptop, only on Linux:\nsudo apt install -y qemu qemu-user-static\n```\n\nThen, create a buildx builder:\n\n```bash\ndocker buildx create --name mybuilder --use\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e If it says \"docker: 'buildx' is not a docker command\", you may need to install\n\u003e `buildx` manually. On macOS, you can do it with the following command:\n\u003e\n\u003e ```bash\n\u003e brew install docker-buildx\n\u003e mkdir -p ~/.docker/cli-plugins/\n\u003e ln -sfn $(brew --prefix)/opt/docker-buildx/bin/docker-buildx ~/.docker/cli-plugins/docker-buildx\n\u003e ```\n\nThen, build the image on your desktop (faster than on the Pi) and then push it\nto the Pi.\n\n```sh\n# From your laptop:\ndocker buildx build -f Dockerfile.controller --platform linux/arm64 \\\n  -t ghcr.io/cert-manager/print-your-cert-controller:latest \\\n  -o type=docker,dest=print-your-cert-controller.tar .\nssh pi docker load \u003cprint-your-cert-controller.tar\n```\n\n\u003e [!NOTE]\n\u003e\n\u003e We don't push the image to GHCR. We just load it directly on the Pi.\n\nNow, SSH into the Raspberry Pi and launch the controller:\n\n```sh\nssh pi sudo chmod a+r ~/.kube/config\nssh pi docker rm -f print-your-cert-controller\nssh pi docker run -d --restart=always --name print-your-cert-controller --privileged -v /dev/bus/usb:/dev/bus/usb -v /home/certmanager/.kube/config:/root/.kube/config --net=host ghcr.io/cert-manager/print-your-cert-controller:latest\n```\n\nYou can also run the \"debug\" printer UI (brother_ql_web) if you want to make\nsure that the printer works:\n\n```sh\nssh pi docker run -d --restart=always --name brother_ql_web \\\n  --privileged -v /dev/bus/usb:/dev/bus/usb \\\n  -p 0.0.0.0:8013:8013 ghcr.io/cert-manager/print-your-cert-controller:latest brother_ql_web\n```\n\n### Booth: Running the guestbook on a VM\n\nThe VM that runs the guestbook is managed by `tofu` and is defined in\n[`booth.tf`](https://github.com/cert-manager/infrastructure/blob/main/gcp/booth.tf).\n\n\u003e [!NOTE]\n\u003e\n\u003e TBD: document \u003chttps://litestream.io/\u003e that we use for the sqlite backups.\n\nFirst, you will need to connect to the Raspberry Pi's cluster to be able to\ncreate the guestbook certificate. You can do that by running the following:\n\n```sh\n# From your laptop:\nssh pi kubectl apply -f - --wait \u003cguestbook/certificate.yaml\nssh pi kubectl get secret -n cert-manager root-print-your-cert-ca -ojson \\\n  | jq -r '.data.\"tls.crt\" | @base64d' \u003eca.crt\nssh pi kubectl get secret -n cert-manager guestbook-tls -ojson \\\n  | jq -r '.data.\"tls.crt\" | @base64d' \u003etls.crt\nssh pi kubectl get secret -n cert-manager guestbook-tls -ojson \\\n  | jq -r '.data.\"tls.key\" | @base64d' \u003etls.key\n```\n\nCopy the root CA that you decrypted in one of the previous steps:\n\n```bash\ngcloud compute scp --project cert-manager-general --zone=europe-west1-c  \\\n  ca.crt tls.crt tls.key guestbook:.\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c guestbook -- \\\n  sudo mkdir -p /var/guestbook\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c guestbook -- \\\n  sudo mv ca.crt tls.crt tls.key /var/guestbook\n```\n\nFinally, build and push:\n\n```bash\nGOARCH=amd64 GOOS=linux go build -C guestbook .\ngcloud compute scp --project cert-manager-general --zone=europe-west1-c  \\\n  guestbook/guestbook test:.\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c guestbook -- \\\n  sudo install guestbook /usr/bin\n```\n\nThen, run the following to create the systemd service:\n\n\u003e [!NOTE]\n\u003e\n\u003e If guestbook has never run on this machine, you will first need to run:\n\u003e\n\u003e ```bash\n\u003e gcloud compute ssh --project cert-manager-general --zone=europe-west1-c guestbook -- \\\n\u003e   mkdir /var/guestbook\n\u003e gcloud compute ssh --project cert-manager-general --zone=europe-west1-c guestbook -- \\\n\u003e   guestbook -init-db -db-path /var/guestbook/guestbook.sqlite\n\u003e ```\n\nFinally, run the following to create the systemd service:\n\n```bash\ngcloud compute ssh --project cert-manager-general --zone=europe-west1-c guestbook -- bash \u003c\u003c'EOF'\nsudo tee /usr/lib/systemd/system/guestbook.service \u003c\u003c'SVC'\n[Unit]\nDescription=cert-manager Booth Guestbook\nAfter=network.target\n\n[Service]\nExecStart=/usr/bin/guestbook -ca-cert /var/guestbook/ca.crt -tls-chain /var/guestbook/tls.crt -tls-key /var/guestbook/tls.key -db-path /var/guestbook/guestbook.sqlite -listen :443 -readonly-listen-insecure :80 -autocert-dir /var/guestbook -prod\n\nStandardOutput=journal\nStandardError=journal\nType=simple\nRestart=always\n\n[Install]\nWantedBy=multi-user.target\nSVC\nsudo systemctl daemon-reload\nEOF\n```\n\n## Local development\n\n### Local development on the UI\n\nYou will need Go.\n\nFirst, follow the steps in [Prerequisite: install k3s on the\nRaspberry](#prerequisite-install-k3s-on-the-raspberry-pi) to install k3s on your\nlocal machine (it is the same as for the Raspberry Pi).\n\nThen, you will need to create a ClusterIssuer:\n\n```sh\nkubectl apply -f root_issuer_dev.yaml --wait\nkubectl apply -f cluster_issuer.yaml --wait\nkubectl get secret -n cert-manager root-print-your-cert-ca -ojson | jq -r '.data.\"tls.crt\" | @base64d' \u003eca.crt\n```\n\nThen, you can run the UI:\n\n```sh\ngo run . --issuer=print-your-cert-ca --issuer-kind=ClusterIssuer \\\n  -guestbook-ca=ca.crt -guestbook-url=guestbook.print-your-cert.cert-manager.io:9090\n```\n\n### Local development on the guestbook\n\nFirst, you will need to make sure the domains\n`guestbook.print-your-cert.cert-manager.io` and\n`readonly-guestbook.print-your-cert.cert-manager.io` point to your machine:\n\n```bash\nsudo perl -ni -e 'print if !/ guestbook.print-your-cert.cert-manager.io$/' /etc/hosts\nsudo tee -a /etc/hosts \u003c\u003c\u003c\"127.0.0.1 guestbook.print-your-cert.cert-manager.io\"\nsudo perl -ni -e 'print if !/ readonly-guestbook.print-your-cert.cert-manager.io$/' /etc/hosts\nsudo tee -a /etc/hosts \u003c\u003c\u003c\"127.0.0.1 readonly-guestbook.print-your-cert.cert-manager.io\"\n```\n\nThen:\n\n```bash\nkubectl apply -f root_issuer_dev.yaml --wait\nkubectl apply -f cluster_issuer.yaml --wait\nkubectl apply -f guestbook/certificate.yaml --wait\n```\n\nGrab the root CA and the certificate to serve the guestbook:\n\n```bash\nkubectl get secret -n cert-manager root-print-your-cert-ca -ojson | jq -r '.data.\"tls.crt\" | @base64d' \u003eca.crt\nkubectl get secret -n cert-manager guestbook-tls -ojson | jq -r '.data.\"tls.crt\" | @base64d' \u003etls.crt\nkubectl get secret -n cert-manager guestbook-tls -ojson | jq -r '.data.\"tls.key\" | @base64d' \u003etls.key\ngo run -C guestbook . -init-db\ngo run -C guestbook . -ca-cert ../ca.crt -tls-chain ../tls.crt -tls-key ../tls.key -listen :9090\n```\n\nTo use the guestbook, make sure the UI is running locally too (see above).\n\n1. Go to the UI at \u003chttp://localhost:8080\u003e.\n2. Submit an email.\n3. Wait for the cert to be ready and click \"Sign the guestbook\".\n4. Go to the guestbook at \u003chttps://readonly-guestbook.print-your-cert.cert-manager.io:9090\u003e. To get a \"star\" instead of a red cross, you need to use curl (or any HTTP client) to sign the book.\n5. To get a star instead of a cross, go back to the page in (1), click \"Download cert bundle tar\".\n6. Open a shell session and go to your `~/Downloads` folder.\n7. Run:\n   ```bash\n   tar xf cert-manager-bundle.tar\n   curl -k https://guestbook.print-your-cert.cert-manager.io/write \\\n     --cacert ca.crt --cert chain.pem --key pkey.pem \\\n     -X POST --data-urlencode message@/dev/stdin \\\n   \u003c\u003cEOF\n   Excellent job, Ash!\n   EOF\n   ```\n8. Go back to \u003chttps://readonly-guestbook.print-your-cert.cert-manager.io:9090\u003e\n   to see the guestbook. Now, you should see a ⭐!\n\n### Local development on the controller (that creates PNGs and prints them)\n\nThe controller is made in two pieces: `cert-card` which produces a PNG with the label(s),\nand `print-your-cert-controller` that runs `cert-card` every time a\ncertificate object in Kubernetes becomes ready.\n\n#### `cert-card`\n\n`cert-card` which produces a PNG with the label(s)\n\n```sh\nbrew install imagemagick qrencode step\nbrew install homebrew/cask-fonts/font-open-sans\nbrew install homebrew/cask-fonts/font-dejavu\n```\n\nTo run it, for example:\n\n```sh\n./cert-card \u003c\u003cEOF\n-----BEGIN CERTIFICATE-----\nMIICXDCCAgOgAwIBAgIQdPaTuGSUDeosii4dbdLBgTAKBggqhkjOPQQDAjAnMSUw\nIwYDVQQDExxUaGUgY2VydC1tYW5hZ2VyIG1haW50YWluZXJzMB4XDTIyMDUxNjEz\nMDkwMFoXDTIyMDgxNDEzMDkwMFowLDEqMCgGA1UEAwwhZm9vIGJhciBmb28gYmFy\nIDxmb28uYmFyQGJhci5mb28+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC\nAQEAtmGM5lil9Vw/y5LhpgO8t5gSb5oUo+Dp5vWw0Z5C7rjvifi0/eD9MbVFkxb+\n+hmOaaNCVgqDUio1OBOZyL90KzdnGW7nz1fRM2KCNrDF5Y1mO7uv1ZTZa8cVBjF6\n7KjFuNkvvHp74m65bKwXeCHXJBmO3Z1FH8hudICU74+Nl6tyjlMOsTHv+LY0jPfm\nAtO6eR+Ef/HvgzwsjKds12vdlRCdHSS6u5zlrZZxF3zTO7YuAM7mN8Wbjq94Ycpg\nsJ5ssNOtMu9FwZtPGQDHPaQyVQ86FfjhmMi1IUOUAAGwh/QRv8ksX+OupHTNdH06\nWmIDCaGBjWFgPkwicavMZgZG3QIDAQABo0EwPzAOBgNVHQ8BAf8EBAMCBaAwDAYD\nVR0TAQH/BAIwADAfBgNVHSMEGDAWgBQG5XQnDhOUa748L9H7TWZN2avluTAKBggq\nhkjOPQQDAgNHADBEAiBXmyJ24PTG76pEyq6AQtCo6TXEidqJhsmK9O5WjGBw7wIg\naPbcFI5iMMgfPGEATH2AGGutZ6MlxBmwhEO7pAkqhQc=\n-----END CERTIFICATE-----\nEOF\n```\n\n#### Testing the printer\n\nTest that `brother_lp` works over USB on Pi:\n\n```shell=sh\nconvert -size 230x30 -background white -font /usr/share/fonts/TTF/OpenSans-Regular.ttf -pointsize 25 -fill black -gravity NorthWest caption:\"OK.\" -flatten example.png\nbrother_ql --model QL-820NWB --printer usb://0x04f9:0x209d print --label 62 example.png\n```\n\n#### Testing cert-card\n\n```shell=sh\nopenssl genrsa -out ca.key 2048\nopenssl req -x509 -new -nodes -key ca.key -utf8 -subj \"/CN=Maël Valais \u003cmael@vls.dev\u003e/O=Jetstack\" -reqexts v3_req -extensions v3_ca -out ca.crt\nstep certificate create \"CN=Foo Bar \u003cfoo@bar.com\u003e\" foo.crt foo.key --ca ca.crt --ca-key ca.key --password-file /dev/null\ncert-card \u003cfoo.crt\ntimg cert-card.png\nread\nbrother_ql --model QL-820NWB --printer usb://0x04f9:0x209d print --label 62 cert-card.png\n```\n\n## Troubleshooting\n\n### From the CLI: `usb.core.USBError: [Errno 13] Access denied (insufficient permissions)`\n\nRun:\n\n```bash\n# From the Raspberry Pi.\nsudo tee /etc/udev/rules.d/99-brother-ql.rules \u003c\u003cEOF\nSUBSYSTEM==\"usb\", ATTR{idVendor}==\"04f9\", ATTR{idProduct}==\"209d\", MODE=\"0666\"\nEOF\n```\n\nThen, reload the udev rules:\n\n```bash\n# From the Raspberry Pi.\nsudo udevadm trigger\n```\n\nThen, unplug and replug the printer.\n\n### From the CLI: `usb.core.USBError: [Errno 16] Resource busy`\n\nOn the Pi (over SSH), when running `brother_ql` with the following command:\n\n```bash\ndocker run --privileged -v /dev/bus/usb:/dev/bus/usb -it --rm ghcr.io/cert-manager/print-your-cert-ui:latest brother_ql\n```\n\nyou may hit the following message:\n\n```text\nusb.core.USBError: [Errno 16] Resource busy\n```\n\nI found that two reasons lead to this message:\n\n1. The primary reason is that libusb-1.0 is installed on the host (on the\n   Pi, that's Debian) and needs to be removed, and replaced with\n   libusb-0.1. You can read more about this in\n   \u003chttps://github.com/pyusb/pyusb/issues/391\u003e.\n2. A second reason is that the label settings aren't correct (e.g., you\n   have select the black/red tape but the black-only tape is installed in\n   the printer).\n\n### From the web UI: `No such file or directory: '/dev/usb/lp1'`\n\nThis happened when the printer was disconnected.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcert-manager%2Fprint-your-cert","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcert-manager%2Fprint-your-cert","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcert-manager%2Fprint-your-cert/lists"}