Hi… I am well aware that this diff view is very suboptimal. It will be fixed when the refactored server comes along!
git_hooks_handle.go, etc.: Listen for connections from hooks
package main
import (
"errors"
"fmt"
"net"
"os"
"syscall"
)
var err_not_unixconn = errors.New("Not a unix connection")
func hooks_handle_connection(conn net.Conn) (err error) {
defer conn.Close()
unix_conn, ok := conn.(*net.UnixConn)
if !ok {
return err_not_unixconn
}
fd, err := unix_conn.File()
if err != nil {
return err
}
defer fd.Close()
ucred, err := get_ucred(fd)
if err != nil {
return err
}
pid := ucred.Pid
_ = pid
conn.Write([]byte{0})
fmt.Fprintf(conn, "your PID is %d\n", pid)
return nil
}
func serve_git_hooks(listener net.Listener) error {
conn, err := listener.Accept()
if err != nil {
return err
}
return hooks_handle_connection(conn)
}
func get_ucred(fd *os.File) (*syscall.Ucred, error) {
ucred, err := syscall.GetsockoptUcred(int(fd.Fd()), syscall.SOL_SOCKET, syscall.SO_PEERCRED)
if err != nil {
return nil, fmt.Errorf("failed to get credentials: %v", err)
}
return ucred, nil
}
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 = deploy_hooks_to_filesystem()
if err != nil {
clog.Fatal(1, "Deploying hooks to filesystem: "+err.Error())
}
err = load_templates()
if err != nil {
clog.Fatal(1, "Loading templates: "+err.Error())
}
ssh_listener, err := net.Listen(config.SSH.Net, config.SSH.Addr)
if err != nil {
clog.Fatal(1, "Listening SSH: "+err.Error())
}
clog.Info("Listening SSH on " + config.SSH.Net + " " + config.SSH.Addr)
go func() {
err = serve_ssh(ssh_listener)
if err != nil {
clog.Fatal(1, "Serving SSH: "+err.Error())
}
}()
err = serve_ssh(ssh_listener)
hooks_listener, err := net.Listen("unix", config.Hooks.Socket)
if err != nil {
clog.Fatal(1, "Serving SSH: "+err.Error())
clog.Fatal(1, "Listening hooks: "+err.Error())
}
clog.Info("Listening SSH on " + config.SSH.Net + " " + config.SSH.Addr)
clog.Info("Listening hooks at " + config.Hooks.Socket)
go func() {
err = serve_git_hooks(hooks_listener)
if err != nil {
clog.Fatal(1, "Serving hooks: "+err.Error())
}
}()
listener, err := net.Listen(config.HTTP.Net, config.HTTP.Addr)
if err != nil {
clog.Fatal(1, "Listening HTTP: "+err.Error())
}
clog.Info("Listening HTTP on " + config.HTTP.Net + " " + config.HTTP.Addr)
err = http.Serve(listener, &http_router_t{})
if err != nil {
clog.Fatal(1, "Serving HTTP: "+err.Error())
}
go func() {
err = http.Serve(listener, &http_router_t{})
if err != nil {
clog.Fatal(1, "Serving HTTP: "+err.Error())
}
}()
select{}
}
package main
import (
"fmt"
"net"
"os"
"path"
"strings"
glider_ssh "github.com/gliderlabs/ssh"
"go.lindenii.runxiyu.org/lindenii-common/clog"
go_ssh "golang.org/x/crypto/ssh"
)
var (
server_public_key_string string
server_public_key_fingerprint string
server_public_key go_ssh.PublicKey
)
func serve_ssh(listener net.Listener) error {
host_key_bytes, err := os.ReadFile(config.SSH.Key)
if err != nil {
return err
}
host_key, err := go_ssh.ParsePrivateKey(host_key_bytes)
if err != nil {
return err
}
server_public_key = host_key.PublicKey()
server_public_key_string = string(go_ssh.MarshalAuthorizedKey(server_public_key))
server_public_key_fingerprint = string(go_ssh.FingerprintSHA256(server_public_key))
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 = strings.TrimSuffix(string(go_ssh.MarshalAuthorizedKey(client_public_key)), "\n")
}
clog.Info("Incoming SSH: " + session.RemoteAddr().String() + " " + client_public_key_string + " " + session.RawCommand())
fmt.Fprintln(session.Stderr(), "Lindenii Forge "+VERSION+", source at "+path.Join(config.HTTP.Root, "/:/source/\r"))
cmd := session.Command()
if len(cmd) < 2 {
fmt.Fprintln(session.Stderr(), "Insufficient arguments\r")
return
}
switch cmd[0] {
case "git-upload-pack":
if len(cmd) > 2 {
fmt.Fprintln(session.Stderr(), "Too many arguments\r")
return
}
err = ssh_handle_upload_pack(session, client_public_key_string, cmd[1])
case "git-receive-pack":
if len(cmd) > 2 {
fmt.Fprintln(session.Stderr(), "Too many arguments\r")
return
}
err = ssh_handle_receive_pack(session, client_public_key_string, cmd[1])
default:
fmt.Fprintln(session.Stderr(), "Unsupported command: "+cmd[0]+"\r")
return
}
if err != nil {
fmt.Fprintln(session.Stderr(), err.Error())
return
}
},
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 },
// It is intentional that we do not check any credentials and accept all connections.
// This allows all users to connect and clone repositories. However, the public key
// is passed to handlers, so e.g. the push handler could check the key and reject the
// push if it needs to.
}
server.AddHostKey(host_key)
go func() {
err = server.Serve(listener)
if err != nil {
clog.Fatal(1, "Serving SSH: "+err.Error())
}
}()
err = server.Serve(listener)
if err != nil {
clog.Fatal(1, "Serving SSH: "+err.Error())
}
return nil }