Lindenii Project Forge
Login
Commit info
ID78ef3b19d40aa6f63412ba961ab5c8bf0939237e
AuthorRunxi Yu<me@runxiyu.org>
Author dateWed, 12 Feb 2025 17:08:27 +0800
CommitterRunxi Yu<me@runxiyu.org>
Committer dateWed, 12 Feb 2025 17:08:27 +0800
Actions
Get patch
ssh.go: Add anonymous SSH cloning
package main

import (
	"bufio"
	"context"
	"errors"
	"os"

	"github.com/jackc/pgx/v5/pgxpool"
	"go.lindenii.runxiyu.org/lindenii-common/scfg"
)

var database *pgxpool.Pool

var err_unsupported_database_type = errors.New("Unsupported database type")

var config struct {
	HTTP struct {
		Net  string `scfg:"net"`
		Addr string `scfg:"addr"`
	} `scfg:"http"`
	SSH struct {
		Net  string `scfg:"net"`
		Addr string `scfg:"addr"`
		Key  string `scfg:"key"`
	} `scfg:"ssh"`
	Git struct {
		Root string `scfg:"root"`
	} `scfg:"git"`
	DB struct {
		Type string `scfg:"type"`
		Conn string `scfg:"conn"`
	} `scfg:"db"`
}

func load_config(path string) (err error) {
	config_file, err := os.Open(path)
	if err != nil {
		return err
	}
	defer config_file.Close()

	decoder := scfg.NewDecoder(bufio.NewReader(config_file))
	err = decoder.Decode(&config)
	if err != nil {
		return err
	}

	if config.DB.Type != "postgres" {
		return err_unsupported_database_type
	}
	database, err = pgxpool.New(context.Background(), config.DB.Conn)
	if err != nil {
		return err
	}

	return nil
}
http {
	net tcp
	addr :8080
}

ssh {
	net ssh
	addr :2222
	key /etc/ssh/ssh_host_ed25519_key
}

db {
	type postgres
	conn postgresql:///lindenii-forge?host=/var/run/postgresql
}

git {
	root /srv/git
}
module go.lindenii.runxiyu.org/forge

go 1.23.5

require (
	github.com/alecthomas/chroma/v2 v2.15.0
	github.com/gliderlabs/ssh v0.3.8
	github.com/go-git/go-git/v5 v5.13.2
	github.com/jackc/pgx/v5 v5.7.2
	github.com/microcosm-cc/bluemonday v1.0.27
	github.com/niklasfasching/go-org v1.7.0
	github.com/yuin/goldmark v1.7.8
	go.lindenii.runxiyu.org/lindenii-common v0.0.0-20250211153243-8946fae17bd0
	golang.org/x/crypto v0.33.0
)

require (
	dario.cat/mergo v1.0.1 // indirect
	github.com/Microsoft/go-winio v0.6.2 // indirect
	github.com/ProtonMail/go-crypto v1.1.5 // indirect
	github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
	github.com/aymerick/douceur v0.2.0 // indirect
	github.com/cloudflare/circl v1.6.0 // indirect
	github.com/cyphar/filepath-securejoin v0.4.1 // indirect
	github.com/dlclark/regexp2 v1.11.4 // indirect
	github.com/emirpasic/gods v1.18.1 // indirect
	github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
	github.com/go-git/go-billy/v5 v5.6.2 // indirect
	github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
	github.com/gorilla/css v1.0.1 // indirect
	github.com/jackc/pgpassfile v1.0.0 // indirect
	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
	github.com/jackc/puddle/v2 v2.2.2 // indirect
	github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
	github.com/kevinburke/ssh_config v1.2.0 // indirect
	github.com/pjbgf/sha1cd v0.3.2 // indirect
	github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
	github.com/skeema/knownhosts v1.3.1 // indirect
	github.com/xanzy/ssh-agent v0.3.3 // indirect
	golang.org/x/crypto v0.33.0 // indirect
	golang.org/x/net v0.35.0 // indirect
	golang.org/x/sync v0.11.0 // indirect
	golang.org/x/sys v0.30.0 // indirect
	golang.org/x/text v0.22.0 // indirect
	gopkg.in/warnings.v0 v0.1.2 // indirect
)
package main

import (
	"flag"
	"net"
	"net/http"

	"go.lindenii.runxiyu.org/lindenii-common/clog"
)

func main() {
	config_path := flag.String(
		"config",
		"/etc/lindenii/forge.scfg",
		"path to configuration file",
	)
	flag.Parse()

	err := load_config(*config_path)
	if err != nil {
		clog.Fatal(1, "Loading configuration: "+err.Error())
	}

	err = load_templates()
	if err != nil {
		clog.Fatal(1, "Loading templates: "+err.Error())
	}

	listener, err := net.Listen(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		clog.Fatal(1, "Listening: "+err.Error())
	}

	err = serve_ssh()
	if err != nil {
		clog.Fatal(1, "Listening SSH: "+err.Error())
	}

	err = http.Serve(listener, &http_router_t{})
	if err != nil {
		clog.Fatal(1, "Serving: "+err.Error())
	}
}
package main

import (
	"fmt"
	"net"
	"os"
	"os/exec"

	glider_ssh "github.com/gliderlabs/ssh"
	"go.lindenii.runxiyu.org/lindenii-common/clog"
	go_ssh "golang.org/x/crypto/ssh"
)

func serve_ssh() error {
	hostKeyBytes, err := os.ReadFile(config.SSH.Key)
	if err != nil {
		return err
	}

	hostKey, err := go_ssh.ParsePrivateKey(hostKeyBytes)
	if err != nil {
		return err
	}

	server := &glider_ssh.Server{
		Handler: func(session glider_ssh.Session) {
			client_public_key := session.PublicKey()
			var client_public_key_string string
			if client_public_key != nil {
				client_public_key_string = string(go_ssh.MarshalAuthorizedKey(client_public_key))
			}
			_ = client_public_key_string

			cmd := session.Command()

			if len(cmd) < 2 {
				fmt.Fprintln(session.Stderr(), "Insufficient arguments")
				return
			}

			if cmd[0] != "git-upload-pack" {
				fmt.Fprintln(session.Stderr(), "Unsupported command")
				return
			}

			proc := exec.CommandContext(session.Context(), cmd[0], "/home/runxiyu/git/forge.git")
			proc.Stdin = session
			proc.Stdout = session
			proc.Stderr = session.Stderr()

			err := proc.Start()
			if err != nil {
				fmt.Fprintln(session.Stderr(), "Error while starting process:", err)
				return
			}
			err = proc.Wait()
			if exitError, ok := err.(*exec.ExitError); ok {
				fmt.Fprintln(session.Stderr(), "Process exited with error", exitError.ExitCode())
			} else if err != nil {
				fmt.Fprintln(session.Stderr(), "Error while waiting for process:", err)
			}
		},
		PublicKeyHandler:           func(ctx glider_ssh.Context, key glider_ssh.PublicKey) bool { return true },
		KeyboardInteractiveHandler: func(ctx glider_ssh.Context, challenge go_ssh.KeyboardInteractiveChallenge) bool { return true },
	}

	server.AddHostKey(hostKey)

	listener, err := net.Listen("tcp", ":2222")
	if err != nil {
		return err
	}

	go func() {
		err = server.Serve(listener)
		if err != nil {
			clog.Fatal(1, "Serving SSH: "+err.Error())
		}
	}()

	return nil
}