"GOT", but the "O" is a cute, smiling pufferfish. Index | Thread | Search

From:
"Omar Polo" <op@omarpolo.com>
Subject:
Re: gotsysd: gotwebd ssh host key fingerprints
To:
Stefan Sperling <stsp@stsp.name>
Cc:
gameoftrees@openbsd.org
Date:
Fri, 06 Feb 2026 18:32:07 +0100

Download raw body.

Thread
Stefan Sperling <stsp@stsp.name> wrote:
> Have gotsysd activate the SSH key fingerprint feature in gotwebd.
> Depends on the previous patch I sent for gotwebd.
> 
> ok?

sure, ok op@

> make gotsys-write-conf add ssh host key fingerprints to /etc/gotwebd.conf
> 
> M  gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c  |  194+  0-
> M  regress/gotsysd/test_gotwebd.sh                        |   25+  0-
> 
> 2 files changed, 219 insertions(+), 0 deletions(-)
> 
> commit - 896f2e7025bdfa07698b31229ccfdd7de5984ba4
> commit + dfc1f484212cc1f9f103669c27aa655112b738eb
> blob - 72e14ac4a5e991c08b11d9829c9912bdc689c94d
> blob + da235fcbeb5a5685f3a0f08e020159821903eb27
> --- gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c
> +++ gotsysd/libexec/gotsys-write-conf/gotsys-write-conf.c
> @@ -20,9 +20,13 @@
>  #include <sys/tree.h>
>  #include <sys/stat.h>
>  
> +#include <netinet/in.h>
> +
>  #include <stddef.h>
> +#include <errno.h>
>  #include <err.h>
>  #include <event.h>
> +#include <resolv.h>
>  #include <imsg.h>
>  #include <limits.h>
>  #include <sha1.h>
> @@ -64,6 +68,21 @@ static size_t *num_notif_refs_cur;
>  static size_t num_notif_refs_needed;
>  static size_t num_notif_refs_received;
>  
> +static char ssh_hostkeys[GOTWEBD_NUM_SSHFP][GOTWEBD_MAX_SSHFP];
> +static const char *ssh_hostkey_paths[GOTWEBD_NUM_SSHFP];
> +
> +#ifndef GOTSYS_SSHFP_ECDSA_PATH
> +#define GOTSYS_SSHFP_ECDSA_PATH "/etc/ssh/ssh_host_ecdsa_key.pub"
> +#endif
> +
> +#ifndef GOTSYS_SSHFP_ED25519_PATH
> +#define GOTSYS_SSHFP_ED25519_PATH "/etc/ssh/ssh_host_ed25519_key.pub"
> +#endif
> +
> +#ifndef GOTSYS_SSHFP_RSA_PATH
> +#define GOTSYS_SSHFP_RSA_PATH "/etc/ssh/ssh_host_rsa_key.pub"
> +#endif
> +
>  enum writeconf_state {
>  	WRITECONF_STATE_EXPECT_GOTWEB_CFG,
>  	WRITECONF_STATE_EXPECT_GOTWEB_ADDRS,
> @@ -1069,6 +1088,48 @@ write_webrepo(int *show_repo_description, int *login_h
>  			    "short write to %s", path);
>  		}
>  		free(clone_url);
> +
> +		if (ssh_hostkeys[GOTWEBD_SSHFP_ECDSA][0]) {
> +			ret = dprintf(fd, "\t\tssh_hostkey_ecdsa \"%s\"\n",
> +			    ssh_hostkeys[GOTWEBD_SSHFP_ECDSA]);
> +			if (ret == -1)  {
> +				return got_error_from_errno2("dprintf", path);
> +			}
> +			if (ret != 22 +
> +			    strlen(ssh_hostkeys[GOTWEBD_SSHFP_ECDSA]) + 1) {
> +				return got_error_fmt(GOT_ERR_IO,
> +				    "short write to %s", path);
> +			}
> +			
> +		}
> +
> +		if (ssh_hostkeys[GOTWEBD_SSHFP_ED25519][0]) {
> +			ret = dprintf(fd, "\t\tssh_hostkey_ed25519 \"%s\"\n",
> +			    ssh_hostkeys[GOTWEBD_SSHFP_ED25519]);
> +			if (ret == -1)  {
> +				return got_error_from_errno2("dprintf", path);
> +			}
> +			if (ret != 24 +
> +			    strlen(ssh_hostkeys[GOTWEBD_SSHFP_ED25519]) + 1) {
> +				return got_error_fmt(GOT_ERR_IO,
> +				    "short write to %s", path);
> +			}
> +			
> +		}
> +
> +		if (ssh_hostkeys[GOTWEBD_SSHFP_RSA][0]) {
> +			ret = dprintf(fd, "\t\tssh_hostkey_rsa \"%s\"\n",
> +			    ssh_hostkeys[GOTWEBD_SSHFP_RSA]);
> +			if (ret == -1)  {
> +				return got_error_from_errno2("dprintf", path);
> +			}
> +			if (ret != 20 +
> +			    strlen(ssh_hostkeys[GOTWEBD_SSHFP_RSA]) + 1) {
> +				return got_error_fmt(GOT_ERR_IO,
> +				    "short write to %s", path);
> +			}
> +			
> +		}
>  	}
>  
>  	ret = dprintf(fd, "\t}\n");
> @@ -2456,12 +2517,123 @@ fatal:
>  	}
>  }
>  
> +static const struct got_error *
> +parse_hostkey(const char *line, char *outbuf, size_t outsize)
> +{
> +	const struct got_error *err = NULL;
> +	char *space;
> +	char *b64;
> +	uint8_t *hostkey = NULL;
> +	size_t hostkey_len;
> +	size_t len;
> +	uint8_t digest[SHA256_DIGEST_LENGTH];
> +	SHA2_CTX ctx;
> +	uint8_t *b64digest = NULL;
> +	size_t b64digest_len;
> +	int ret;
> +
> +	space = strchr(line, ' ');
> +	if (space == NULL)
> +		return got_error(GOT_ERR_NOT_FOUND);
> +
> +	b64 = space + 1;
> +	space = strchr(b64, ' ');
> +	if (space == NULL)
> +		return got_error(GOT_ERR_NOT_FOUND);
> +
> +	*space = '\0';
> +
> +	len = strlen(b64);
> +	if (len == 0)
> +		return got_error(GOT_ERR_NOT_FOUND);
> +
> +	hostkey = calloc(1, len);
> +	if (hostkey == NULL)
> +		return got_error_from_errno("calloc");
> +
> +	hostkey_len = b64_pton(b64, hostkey, len);
> +	if (hostkey_len <= 0) {
> +		err = got_error(GOT_ERR_NOT_FOUND);
> +		goto done;
> +	}
> +
> +	SHA256Init(&ctx);
> +	SHA256Update(&ctx, hostkey, hostkey_len);
> +	SHA256Final(digest, &ctx);
> +
> +	b64digest_len = ((len + 2) / 3) * 4 + 1;
> +	b64digest = calloc(1, b64digest_len);
> +	if (b64digest == NULL) {
> +		err = got_error_from_errno("calloc");
> +		goto done;
> +	}
> +
> +	if (b64_ntop(digest, SHA256_DIGEST_LENGTH,
> +	    b64digest, b64digest_len) == -1) {
> +		err = got_error_from_errno("b64_ntop");
> +		goto done;
> +	}
> +
> +	b64digest[strcspn(b64digest, "=")] = '\0';
> +	ret = snprintf(outbuf, outsize, "SHA256:%s\n", b64digest);
> +	if (ret == -1) {
> +		err = got_error_from_errno("snprintf");
> +		goto done;
> +	}
> +	if ((size_t)ret >= outsize) {
> +		err = got_error_fmt(GOT_ERR_NO_SPACE, "SSH host key "
> +		    "fingerprint too long, exceeds %zu bytes: SHA256:%s",
> +		    outsize, b64digest);
> +		goto done;
> +	}
> +done:
> +	free(hostkey);
> +	free(b64digest);
> +	return err;
> +}
> +
> +static const struct got_error *
> +load_ssh_hostkey(int key_type)
> +{
> +	const struct got_error *err = NULL;
> +	const char *path = ssh_hostkey_paths[key_type];
> +	FILE *f;
> +	char *line = NULL;
> +	size_t linesize = 0;
> +	ssize_t linelen;
> +
> +	f = fopen(path, "r");
> +	if (f == NULL) {
> +		/* Don't care why it failed. Skip this file. */
> +		return NULL;
> +	}
> +
> +	while ((linelen = getline(&line, &linesize, f)) != -1) {
> +		if (linelen == 0)
> +			continue;
> +
> +		err = parse_hostkey(line, ssh_hostkeys[key_type],
> +		    sizeof(ssh_hostkeys[key_type]));
> +		if (err == NULL || err->code != GOT_ERR_NOT_FOUND)
> +			break;
> +		err = NULL;
> +	}
> +
> +	free(line);
> +
> +	if (fclose(f) == EOF && ferror(f) && err == NULL)
> +		err = got_ferror(f, GOT_ERR_IO);
> +
> +	return err;
> +}
> +
>  int
>  main(int argc, char *argv[])
>  {
>  	const struct got_error *err = NULL;
>  	struct gotsysd_imsgev iev;
>  	struct event evsigint, evsigterm, evsighup, evsigusr1;
> +	size_t i;
>  #if 0
>  	static int attached;
>  
> @@ -2539,11 +2711,33 @@ main(int argc, char *argv[])
>  		goto done;
>  	}
>  
> +	ssh_hostkey_paths[GOTWEBD_SSHFP_ECDSA] = GOTSYS_SSHFP_ECDSA_PATH;
> +	ssh_hostkey_paths[GOTWEBD_SSHFP_ED25519] = GOTSYS_SSHFP_ED25519_PATH;
> +	ssh_hostkey_paths[GOTWEBD_SSHFP_RSA] = GOTSYS_SSHFP_RSA_PATH;
> +
> +	for (i = 0; i < nitems(ssh_hostkey_paths); i++) {
> +		if (unveil(ssh_hostkey_paths[i], "r") == -1) {
> +			err = got_error_from_errno2("unveil r",
> +			    ssh_hostkey_paths[i]);
> +			goto done;
> +		}
> +	}
> +
>  	if (unveil(NULL, NULL) == -1) {
>  		err = got_error_from_errno("unveil");
>  		goto done;
>  	}
>  
> +	err = load_ssh_hostkey(GOTWEBD_SSHFP_ECDSA);
> +	if (err)
> +		goto done;
> +	err = load_ssh_hostkey(GOTWEBD_SSHFP_ED25519);
> +	if (err)
> +		goto done;
> +	err = load_ssh_hostkey(GOTWEBD_SSHFP_RSA);
> +	if (err)
> +		goto done;
> +
>  	iev.handler = dispatch_event;
>  	iev.events = EV_READ;
>  	iev.handler_arg = NULL;
> blob - 937cf1405022eebf9e32c315d2eeb5823f51d4db
> blob + b8c0500e50a21e234c5a8ec5f7dd2688296be03e
> --- regress/gotsysd/test_gotwebd.sh
> +++ regress/gotsysd/test_gotwebd.sh
> @@ -645,6 +645,19 @@ EOF
>  test_summary_page() {
>  	local testroot=`test_init summary_page 1`
>  
> +	GOTSYS_ECDSA_HOST_FP=$(ssh -i ${GOTSYSD_SSH_KEY} \
> +		${GOTSYSD_TEST_USER}@${VMIP} \
> +		ssh-keygen -lf /etc/ssh/ssh_host_ecdsa_key.pub | \
> +		cut -d' ' -f2)
> +	GOTSYS_ED25519_HOST_FP=$(ssh -i ${GOTSYSD_SSH_KEY} \
> +		${GOTSYSD_TEST_USER}@${VMIP} \
> +		ssh-keygen -lf /etc/ssh/ssh_host_ed25519_key.pub | \
> +		cut -d' ' -f2)
> +	GOTSYS_RSA_HOST_FP=$(ssh -i ${GOTSYSD_SSH_KEY} \
> +		${GOTSYSD_TEST_USER}@${VMIP} \
> +		ssh-keygen -lf /etc/ssh/ssh_host_rsa_key.pub | \
> +		cut -d' ' -f2)
> +
>  	got checkout -q $testroot/${GOTSYS_REPO} $testroot/wt >/dev/null
>  	ret=$?
>  	if [ $ret -ne 0 ]; then
> @@ -779,6 +792,18 @@ Clone URL:
>  
>      ssh://${GOTSYSD_TEST_USER}@${VMIP}/public.git
>  
> +ECDSA:
> +
> +    ${GOTSYS_ECDSA_HOST_FP}
> +
> +ED25519:
> +
> +    ${GOTSYS_ED25519_HOST_FP}
> +
> +RSA:
> +
> +    ${GOTSYS_RSA_HOST_FP}
> +
>  Commit Briefs
>  
>  right now $short_commit_id Flan Hacker