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

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

Download raw body.

Thread
Stefan Sperling <stsp@stsp.name> wrote:
> Add options to gotwebd.conf which set SSH host key fingeprints to
> be displayed beneath the clone URL.
> 
> In gotwebd.conf the clone URL is arbitrary, which means the admin needs to
> be able to specify key fingerprints per clone-URL, rather than per server.
> So, for now, ssh fingerprints must be set per repository. If this causes
> too much copy-pasting we can introduce equivalent server-scope options later.
> 
> gotsysd will be able to make assumptions about how the server is set
> up and generate appropriate gotwebd.conf stanzas automatically.
> A patch for gotsysd will follow.
> 
> ok?

okay op@

> M  gotwebd/gotweb.c        |  21+  0-
> M  gotwebd/gotwebd.conf.5  |  12+  0-
> M  gotwebd/gotwebd.h       |   8+  0-
> M  gotwebd/pages.tmpl      |  12+  0-
> M  gotwebd/parse.y         |  70+  0-
> 
> 5 files changed, 123 insertions(+), 0 deletions(-)
> 
> commit - 8e075c1ec8542ad827b525234bea2eba76a011ad
> commit + 896f2e7025bdfa07698b31229ccfdd7de5984ba4
> blob - f4dc9ee62c67b484e6013abf9d57672569a9f3b9
> blob + 2dcdf486c002a5a2a948db88611b6912fc1de984
> --- gotwebd/gotweb.c
> +++ gotwebd/gotweb.c
> @@ -1124,11 +1124,15 @@ gotweb_free_repo_commit(struct repo_commit *rc)
>  static void
>  gotweb_free_repo_dir(struct repo_dir *repo_dir)
>  {
> +	size_t i;
> +
>  	if (repo_dir != NULL) {
>  		free(repo_dir->name);
>  		free(repo_dir->owner);
>  		free(repo_dir->description);
>  		free(repo_dir->url);
> +		for (i = 0; i < nitems(repo_dir->sshfp); i++)
> +			free(repo_dir->sshfp[i]);
>  		free(repo_dir->path);
>  	}
>  	free(repo_dir);
> @@ -1708,6 +1712,23 @@ gotweb_load_got_path(struct repo_dir **rp, const char 
>  		error = gotweb_get_clone_url(&repo_dir->url, srv,
>  		    repo_dir->path, dirfd(dt));
>  	}
> +
> +	if (srv->show_repo_cloneurl && repo) {
> +		size_t i;
> +
> +		for (i = 0; i < nitems(repo->clone_url_hostkey); i++) {
> +			if (repo->clone_url_hostkey[i][0] == '\0')
> +				continue;
> +
> +			repo_dir->sshfp[i] = strdup(repo->clone_url_hostkey[i]);
> +			if (repo_dir->sshfp[i] == NULL) {
> +				error = got_error_from_errno("strdup");
> +				goto err;
> +			}
> +		}
> +
> +	}
> +
>  err:
>  	free(dir_test);
>  	if (dt != NULL && closedir(dt) == EOF && error == NULL)
> blob - dd2a181f36cae86843ad2bd1da24552cfd704f34
> blob + 213c5b4be700f2eab0b7e9c32341ac7d848a4123
> --- gotwebd/gotwebd.conf.5
> +++ gotwebd/gotwebd.conf.5
> @@ -497,6 +497,18 @@ then URLs stored in the repository's
>  .Pa cloneurl
>  file will be shown instead.
>  This file may contain multiple URLs, one per line.
> +.It Ic ssh_hostkey_ecdsa Ar string
> +Set the server's SSH ECDSA host key fingerprint to be displayed beneath
> +the clone URL.
> +Should be set when the clone URL uses the SSH protocol.
> +.It Ic ssh_hostkey_ed25519 Ar string
> +Set the server's SSH ED25519 host key fingerprint to be displayed beneath
> +the clone URL.
> +Should be set when the clone URL uses the SSH protocol.
> +.It Ic ssh_hostkey_rsa Ar string
> +Set the server's SSH RSA host key fingerprint to be displayed beneath
> +the clone URL.
> +Should be set when the clone URL uses the SSH protocol.
>  .It Ic description Ar string
>  Sets the repository description shown on the repository listing page.
>  The
> blob - af216fb97464e9ca56a9e377fdd5fc49502ebd0f
> blob + 69c2896f72735357062dc4f09af598567a4aa339
> --- gotwebd/gotwebd.h
> +++ gotwebd/gotwebd.h
> @@ -54,7 +54,13 @@
>  #define GOTWEBD_MAXPORT		 6
>  #define GOTWEBD_NUMPROC		 3
>  #define GOTWEBD_SOCK_FILENO	 3
> +#define GOTWEBD_MAX_SSHFP	 64
>  
> +#define GOTWEBD_SSHFP_ECDSA	0
> +#define GOTWEBD_SSHFP_ED25519	1
> +#define GOTWEBD_SSHFP_RSA	2
> +#define GOTWEBD_NUM_SSHFP	3
> +
>  #define PROC_MAX_INSTANCES	 32
>  
>  /* GOTWEB DEFAULTS */
> @@ -206,6 +212,7 @@ struct repo_dir {
>  	char			*owner;
>  	char			*description;
>  	char			*url;
> +	char			*sshfp[GOTWEBD_NUM_SSHFP];
>  	time_t			 age;
>  	char			*path;
>  };
> @@ -398,6 +405,7 @@ struct gotwebd_repo {
>  	char name[NAME_MAX];
>  	char description[GOTWEBD_MAXDESCRSZ];
>  	char clone_url[GOTWEBD_MAXCLONEURLSZ];
> +	char clone_url_hostkey[GOTWEBD_NUM_SSHFP][GOTWEBD_MAX_SSHFP];
>  
>  	enum gotwebd_auth_config	auth_config;
>  	struct gotwebd_access_rule_list access_rules;
> blob - cebcb17ddb34bf214026aa7e215c0e0f358e0824
> blob + e146964ba1ffd06404dc161bd891095107b8ecc8
> --- gotwebd/pages.tmpl
> +++ gotwebd/pages.tmpl
> @@ -1133,7 +1133,19 @@ nextsep(char *s, char **t)
>    {{ if srv->show_repo_cloneurl }}
>      <dt>Clone URL:</dt>
>      <dd><pre class="clone-url">{{ t->repo_dir->url }}</pre></dd>
> +  {{ if t->repo_dir->sshfp[GOTWEBD_SSHFP_ECDSA] }}
> +    <dt>ECDSA:</dt>
> +    <dd><pre class="clone-url">{{ t->repo_dir->sshfp[GOTWEBD_SSHFP_ECDSA] }}</pre></dd>
>    {{ end }}
> +  {{ if t->repo_dir->sshfp[GOTWEBD_SSHFP_ED25519] }}
> +    <dt>ED25519:</dt>
> +    <dd><pre class="clone-url">{{ t->repo_dir->sshfp[GOTWEBD_SSHFP_ED25519] }}</pre></dd>
> +  {{ end }}
> +  {{ if t->repo_dir->sshfp[GOTWEBD_SSHFP_RSA] }}
> +    <dt>RSA:</dt>
> +    <dd><pre class="clone-url">{{ t->repo_dir->sshfp[GOTWEBD_SSHFP_RSA] }}</pre></dd>
> +  {{ end }}
> +  {{ end }}
>  </dl>
>  <div class="summary-briefs">
>    {{ render gotweb_render_briefs(tp) }}
> blob - 03821d0d02b8707a48377d243fee6f91c949727b
> blob + 06d0fa8ac5221e0906bb1adb03a35e49c67c6f35
> --- gotwebd/parse.y
> +++ gotwebd/parse.y
> @@ -157,6 +157,7 @@ mediatype_ok(const char *s)
>  %token	ENABLE DISABLE INSECURE REPOSITORY REPOSITORIES PERMIT DENY HIDE
>  %token	WEBSITE PATH BRANCH REPOS_URL_PATH DESCRIPTION
>  %token	TYPES INCLUDE GOTWEBD_CONTROL
> +%token	SSH_HOSTKEY_ECDSA SSH_HOSTKEY_ED25519 SSH_HOSTKEY_RSA
>  
>  %token	<v.string>	STRING
>  %token	<v.number>	NUMBER
> @@ -978,6 +979,72 @@ repoopts1	: DISABLE AUTHENTICATION {
>  				YYERROR;
>  			}
>  		}
> +		| SSH_HOSTKEY_ECDSA STRING {
> +			int i = GOTWEBD_SSHFP_ECDSA;
> +
> +			if (*$2 == '\0') {
> +				yyerror("ssh host key fingerprint cannot be "
> +				    "an empty string");
> +				free($2);
> +				YYERROR;
> +			}
> +
> +			if (strlcpy(new_repo->clone_url_hostkey[i], $2,
> +			    sizeof(new_repo->clone_url_hostkey[i])) >=
> +			    sizeof(new_repo->clone_url_hostkey[i])) {
> +				yyerror("ssh host key fingerprint too long, "
> +				    "exceeds " "%zd bytes: %s",
> +				    sizeof(new_repo->clone_url_hostkey[i]), $2);
> +				free($2);
> +				YYERROR;
> +			}
> +
> +			free($2);
> +		}
> +		| SSH_HOSTKEY_ED25519 STRING {
> +			int i = GOTWEBD_SSHFP_ED25519;
> +
> +			if (*$2 == '\0') {
> +				yyerror("ssh host key fingerprint cannot be "
> +				    "an empty string");
> +				free($2);
> +				YYERROR;
> +			}
> +
> +			if (strlcpy(new_repo->clone_url_hostkey[i], $2,
> +			    sizeof(new_repo->clone_url_hostkey[i])) >=
> +			    sizeof(new_repo->clone_url_hostkey[i])) {
> +				yyerror("ssh host key fingerprint too long, "
> +				    "exceeds " "%zd bytes: %s",
> +				    sizeof(new_repo->clone_url_hostkey[i]), $2);
> +				free($2);
> +				YYERROR;
> +			}
> +
> +			free($2);
> +		}
> +		| SSH_HOSTKEY_RSA STRING {
> +			int i = GOTWEBD_SSHFP_RSA;
> +
> +			if (*$2 == '\0') {
> +				yyerror("ssh host key fingerprint cannot be "
> +				    "an empty string");
> +				free($2);
> +				YYERROR;
> +			}
> +
> +			if (strlcpy(new_repo->clone_url_hostkey[i], $2,
> +			    sizeof(new_repo->clone_url_hostkey[i])) >=
> +			    sizeof(new_repo->clone_url_hostkey[i])) {
> +				yyerror("ssh host key fingerprint too long, "
> +				    "exceeds " "%zd bytes: %s",
> +				    sizeof(new_repo->clone_url_hostkey[i]), $2);
> +				free($2);
> +				YYERROR;
> +			}
> +
> +			free($2);
> +		}
>  		;
>  
>  types		: TYPES	'{' optnl mediaopts_l '}'
> @@ -1129,6 +1196,9 @@ lookup(char *s)
>  		{ "site_name",			SITE_NAME },
>  		{ "site_owner",			SITE_OWNER },
>  		{ "socket",			SOCKET },
> +		{ "ssh_hostkey_ecdsa",		SSH_HOSTKEY_ECDSA},
> +		{ "ssh_hostkey_ed25519",	SSH_HOSTKEY_ED25519},
> +		{ "ssh_hostkey_rsa",		SSH_HOSTKEY_RSA},
>  		{ "summary_commits_display",	SUMMARY_COMMITS_DISPLAY },
>  		{ "summary_tags_display",	SUMMARY_TAGS_DISPLAY },
>  		{ "types",			TYPES },