grim/hgkeeper

Parents d6f63e8dc215
Children df734a54a97c
Added the ability to integrate a long running hgkeeper with openssh

This added two new arguments to the server command, --external-hostname and
--external port.

These are used in the new `/ssh/authorized_keys` endpoint that will return the
appropriate authorized_keys format for the key whose finger print is passed in
in the fp query parameter.
--- a/hgweb/hgweb.go Sun Mar 06 06:32:24 2022 -0600
+++ b/hgweb/hgweb.go Sun Mar 06 22:08:49 2022 -0600
@@ -13,6 +13,7 @@
"keep.imfreedom.org/grim/hgkeeper/access"
"keep.imfreedom.org/grim/hgkeeper/hg"
+ hgkHttp "keep.imfreedom.org/grim/hgkeeper/http"
)
type Server struct {
@@ -23,15 +24,20 @@
cgiPath string
cacheSize int
+
+ externalHostname string
+ externalPort string
}
-func NewServer(listenAddr string, cacheSize int) (*Server, error) {
+func NewServer(listenAddr string, cacheSize int, externalHostname, externalPort string) (*Server, error) {
return &Server{
listenAddr: listenAddr,
cacheSize: cacheSize,
server: &http.Server{
Addr: listenAddr,
},
+ externalHostname: externalHostname,
+ externalPort: externalPort,
}, nil
}
@@ -110,6 +116,14 @@
fileServer := http.FileServer(http.Dir(staticPath))
mux := http.NewServeMux()
+
+ if s.externalHostname != "" {
+ mux.Handle("/ssh/authorized_keys", hgkHttp.AuthorizedKeysHandler(s.externalHostname, s.externalPort))
+ log.Infof("added /ssh/authorized_keys endpoint with external hostname %s and export port %s", s.externalHostname, s.externalPort)
+ } else {
+ log.Infof("no external hostname specified, not adding /ssh/authorized_keys endpoint")
+ }
+
mux.Handle("/static/", http.StripPrefix("/static", fileServer))
mux.Handle("/", &cgi.Handler{Path: s.cgiPath})
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/http/authorized_keys.go Sun Mar 06 22:08:49 2022 -0600
@@ -0,0 +1,46 @@
+package http
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ log "github.com/sirupsen/logrus"
+
+ "keep.imfreedom.org/grim/hgkeeper/access"
+)
+
+func AuthorizedKeysHandler(externalHostname, externalPort string) http.Handler {
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ fp := r.URL.Query().Get("fp")
+ if fp == "" {
+ w.WriteHeader(http.StatusBadRequest)
+ fmt.Fprintf(w, "missing fp parameter")
+
+ return
+ }
+
+ pubkey, err := access.PubkeyFromFingerprint(fp)
+ if err != nil {
+ w.WriteHeader(http.StatusNotFound)
+ fmt.Fprintf(w, "failed to find fingerprint %q", fp)
+
+ log.Errorf("failed to find fingerprint for %s: %v", fp, err)
+
+ return
+ }
+
+ options := []string{
+ fmt.Sprintf(
+ "command=\"ssh -T %s -p %s $SSH_ORIGINAL_COMMAND\"",
+ externalHostname,
+ externalPort,
+ ),
+ "restrict",
+ "agent-forwarding",
+ }
+
+ w.WriteHeader(http.StatusOK)
+ fmt.Fprintf(w, fmt.Sprintf("%s %s", strings.Join(options, ","), pubkey))
+ })
+}
--- a/serve/command.go Sun Mar 06 06:32:24 2022 -0600
+++ b/serve/command.go Sun Mar 06 22:08:49 2022 -0600
@@ -15,12 +15,14 @@
)
type Command struct {
- SSHAddr string `kong:"flag,name='ssh-listen-addr',env='HGK_SSH_LISTEN_ADDR',short='l',help='what address to listen on',default=':22222'"`
- SSHHostKeysPath string `kong:"flag,name='ssh-host-keys-path',env='HGK_SSH_HOST_KEYS_PATH',short='H',help='the path where host keys are kept',default='host-keys'"`
- HTTPAddr string `kong:"flag,name='http-listen-addr',env='HGK_HTTP_LISTEN_ADDR',help='what address the http server listens on',default=':8080'"`
- CacheSize int `kong:"flag,name='cache-size',env='HGK_HTTP_CACHE_SIZE',help='number of pages to cache',default='1000'"`
- DisableSSH bool `kong:"flag,name='disable-ssh',env='HGK_DISABLE_SSH',help='disable the SSH server',default='false'"`
- DisableHTTP bool `kong:"flag,name='disable-http',env='HGK_DISABLE_HTTP',help='disable the HTTP server',default='false'"`
+ SSHAddr string `kong:"flag,name='ssh-listen-addr',env='HGK_SSH_LISTEN_ADDR',short='l',help='what address to listen on',default=':22222'"`
+ SSHHostKeysPath string `kong:"flag,name='ssh-host-keys-path',env='HGK_SSH_HOST_KEYS_PATH',short='H',help='the path where host keys are kept',default='host-keys'"`
+ HTTPAddr string `kong:"flag,name='http-listen-addr',env='HGK_HTTP_LISTEN_ADDR',help='what address the http server listens on',default=':8080'"`
+ CacheSize int `kong:"flag,name='cache-size',env='HGK_HTTP_CACHE_SIZE',help='number of pages to cache',default='1000'"`
+ DisableSSH bool `kong:"flag,name='disable-ssh',env='HGK_DISABLE_SSH',help='disable the SSH server',default='false'"`
+ DisableHTTP bool `kong:"flag,name='disable-http',env='HGK_DISABLE_HTTP',help='disable the HTTP server',default='false'"`
+ ExternalHostname string `kong:"flag,name='external-hostname',env='HGK_EXTERNAL_HOSTNAME',help='The external hostname of the hgkeeper instance. This is used to integrate with other ssh servers.'"`
+ ExternalPort string `kong:"flag,name='external-port',env='HGK_EXTERNAL_PORT',help='The external port of the hgkeeper instance. This is used to itegrate with other ssh servers.',default='22222'"`
}
func (c *Command) Run(g *globals.Globals) error {
@@ -56,7 +58,7 @@
log.Info("HTTP server has been disabled")
} else {
var err error
- hgwebServer, err = hgweb.NewServer(c.HTTPAddr, c.CacheSize)
+ hgwebServer, err = hgweb.NewServer(c.HTTPAddr, c.CacheSize, c.ExternalHostname, c.ExternalPort)
if err != nil {
return err
}