daemon

package module
v0.2.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 11, 2023 License: MIT Imports: 16 Imported by: 1

README

Git Daemon

Latest Release Go Docs Go Report Card

This is a Go implementation of the Git Transport protocol and git-daemon binary. It is meant to be used as a Go library to replace git-daemon, but can also be used as a binary to serve repositories using git transport.

Dependencies

The default handler uses the git binary underneath. Specifically, git-upload-pack, git-upload-archive, and git-receive-pack. The git binary is optional if you're using a pure-go implementation, for example go-git, to handle these requests.

Usage

To import this package as a library

go get github.com/aymanbagabas/go-git-daemon

And then import it in your project

import (
    daemon "github.com/aymanbagabas/go-git-daemon"
)

To install the git-daemon binary

go install github.com/aymanbagabas/go-git-daemon/cmd/git-daemon@latest

Binary usage

Usage of git-daemon:
      --access-hook string       run external command to authorize access
      --base-path string         base path for all repositories
      --disable stringArray      disable a service
      --enable stringArray       enable a service
      --export-all               export all repositories
      --host string              the server hostname to advertise (default "localhost")
      --idle-timeout int         timeout (in seconds) between each read/write request
      --init-timeout int         timeout (in seconds) between the moment the connection is established and the client request is received
      --listen string            listen on the given address (default ":9418")
      --log-destination string   log destination (default "stderr")
      --max-connections int      maximum number of simultaneous connections (default 32)
      --max-timeout int          timeout (in seconds) the total time the connection is allowed to be open
      --strict-paths             match paths exactly
      --timeout int              timeout (in seconds) for specific client sub-requests
      --verbose                  enable verbose logging

Examples

To use this as a server

package main

import (
    "log"

    daemon "github.com/aymanbagabas/go-git-daemon"
)

func main() {
    // Enable upload-archive
    daemon.Enable(daemon.UploadArchiveService)

    // Start server on the default port :9418
    if err := daemon.ListenAndServe(":9418"); err != nil {
        log.Fatal(err)
    }
}

Or create a new server instance with custom options and access hook

package main

import (
    "log"

    daemon "github.com/aymanbagabas/go-git-daemon"
)

func accessHook(service Service, path string, host string, canoHost string, ipAdd string, port string, remoteAddr string) error {
    log.Printf("[%s] Requesting access to %s from %s (%s)", service, path, remoteAddr, host)

    // deny any push access except to "public.git"
    if service == daemon.ReceivePackService && path != "/srv/git/public.git" {
        return fmt.Errorf("access denied")
    }

    // deny all push/fetch access to "private.git"
    if path == "/srv/git/private.git" {
        return fmt.Errorf("access denied")
    }

    return nil // allow access
}

func main() {
    logger := log.Default()
    logger.SetPrefix("[git-daemon] ")

    // Create a new server instance
    server := daemon.Server{
        MaxConnections:       0,                                  // unlimited concurrent connections
        Timeout:              3,                                  // 3 seconds timeout
        BasePath:             "/srv/git",                         // base path for all repositories
        ExportAll:            true,                               // export all repositories
        AccessHook:           accessHook,                         // use a custom access hook
        Logger:               logger,                             // use logger
        //Verbose:              true,                             // enable verbose logging
    }

    // Enable all services 💃
    server.Enable(daemon.UploadPackService)
    server.Enable(daemon.UploadArchiveService)
    server.Enable(daemon.ReceivePackService)

    // Start server on the default port (9418)
    if err := server.ListenAndServe("0.0.0.0:9418"); err != nil {
        log.Fatal(err)
    }
}

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

View Source
var (
	// GitBinPath is the path to the Git binary.
	GitBinPath = "git"

	// DefaultAddr is the default Git daemon address.
	DefaultAddr = ":9418"

	// DefaultServer is the default Git daemon configuration.
	DefaultServer = Server{
		MaxConnections:       32,
		UploadPackHandler:    DefaultUploadPackHandler,
		UploadArchiveHandler: DefaultUploadArchiveHandler,
		ReceivePackHandler:   DefaultReceivePackHandler,
		Logger:               log.Default(),
		// contains filtered or unexported fields
	}

	// DefaultServiceHandler is the default Git transport service handler.
	// It uses the git binary to run the requested service.
	//
	// Use this to implement custom service handlers that encapsulates the Git
	// binary. For example:
	//
	//  myHandler := func(service daemon.Service, gitBinPath string) daemon.ServiceHandler {
	//      return func(path string, conn net.Conn, cmdFunc func(*exec.Cmd)) error {
	//          customFunc := func(cmd *exec.Cmd) {
	//          cmd.Env = append(cmd.Env, "CUSTOM_ENV_VAR=foo")
	//          if cmdFunc != nil {
	//            cmdFunc(cmd)
	//          }
	//
	//          return daemon.DefaultServiceHandler(service, gitBinPath)(path, conn, customFunc)
	//      }
	//  }
	//
	//  daemon.HandleUploadPack(myHandler(daemon.UploadPack, "/path/to/my/git"))
	//  daemon.DefaultServer.ReceivePackHandler = myHandler(daemon.ReceivePack, "/path/to/my/git")
	//
	DefaultServiceHandler = func(service Service, gitBinPath string) ServiceHandler {
		return func(ctx context.Context, path string, conn net.Conn, cmdFunc func(*exec.Cmd)) error {
			cmd := exec.CommandContext(ctx, gitBinPath, service.Name(), ".")
			cmd.Dir = path
			if cmdFunc != nil {
				cmdFunc(cmd)
			}

			stdin, err := cmd.StdinPipe()
			if err != nil {
				return err
			}

			stdout, err := cmd.StdoutPipe()
			if err != nil {
				return err
			}

			stderr, err := cmd.StderrPipe()
			if err != nil {
				return err
			}

			if err := cmd.Start(); err != nil {
				return err
			}

			errg, ctx := errgroup.WithContext(ctx)

			errg.Go(func() error {
				defer stdin.Close()
				_, err := io.Copy(stdin, conn)
				return err
			})

			errg.Go(func() error {
				_, err := io.Copy(conn, stdout)
				return err
			})

			errg.Go(func() error {
				_, err := io.Copy(conn, stderr)
				return err
			})

			if err := errg.Wait(); err != nil {
				return errors.Join(err, cmd.Wait())
			}

			return cmd.Wait()
		}
	}

	// DefaultUploadPackHandler is the default upload-pack service handler.
	DefaultUploadPackHandler = DefaultServiceHandler(UploadPackService, GitBinPath)

	// DefaultUploadArchiveHandler is the default upload-archive service handler.
	DefaultUploadArchiveHandler = DefaultServiceHandler(UploadArchiveService, GitBinPath)

	// DefaultReceivePackHandler is the default receive-pack service handler.
	DefaultReceivePackHandler = DefaultServiceHandler(ReceivePackService, GitBinPath)
)
View Source
var (
	// ErrServerClosed indicates that the server has been closed.
	ErrServerClosed = errors.New("server closed")

	// ErrTimeout is returned when the maximum read timeout is exceeded.
	ErrTimeout = errors.New("I/O timeout reached")

	// ErrInvalidRequest represents an invalid request.
	ErrInvalidRequest = errors.New("invalid request")

	// ErrAccessDenied represents an access denied error.
	ErrAccessDenied = errors.New("access denied")

	// ErrNotFound represents a repository not found error.
	ErrNotFound = errors.New("repository not found")

	// ErrSystemMalfunction represents a system malfunction error.
	ErrSystemMalfunction = errors.New("something went wrong")
)

Functions

func Close

func Close() error

Close force closes the Git daemon server.

func Disable added in v0.2.0

func Disable(s Service)

Disable disables the given Git transport service.

func Enable added in v0.2.0

func Enable(s Service)

Enable enables the given Git transport service.

func HandleReceivePack

func HandleReceivePack(h ServiceHandler)

HandleReceivePack registers and enables the handler for the ReceivePack service. If h is nil, the ReceivePack service is disabled.

The default handler (DefaultReceivePackHandler) uses the Git command from (GitBinPath) and "receive-pack" to handle the service.

To use a different Git binary path, either set GitBinPath or use a custom service handler.

func HandleUploadArchive

func HandleUploadArchive(h ServiceHandler)

HandleUploadArchive registers and enables the handler for the UploadArchive service. If h is nil, the UploadArchive service is disabled.

The default handler (DefaultUploadArchiveHandler) uses the Git command from (GitBinPath) and "upload-archive" to handle the service.

To use a different Git binary path, either set GitBinPath or use a custom service handler.

func HandleUploadPack

func HandleUploadPack(h ServiceHandler)

HandleUploadPack registers and enables the handler for the UploadPack service. If h is nil, the UploadPack service is disabled.

The default handler (DefaultUploadPackHandler) uses the Git command from (GitBinPath) and "upload-pack" to handle the service.

To use a different Git binary path, either set GitBinPath or use a custom service handler.

func ListenAndServe

func ListenAndServe(addr string) error

ListenAndServe listens on the TCP network address s.Addr and then calls Serve to handle requests on incoming connections.

func Serve

func Serve(l net.Listener) error

Serve serves the Git daemon server on the given listener l. Each incoming connection is handled in a separate goroutine.

func Shutdown

func Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the Git daemon server without interrupting any active connections.

Types

type AccessHook

type AccessHook func(service Service, path string, host string, canoHost string, ipAdd string, port string, remoteAddr string) error

AccessHook is a git-daemon access hook. It takes a service name, repository path, and a client address as arguments.

type Server

type Server struct {
	// StrictPaths match paths exactly (i.e. don’t allow "/foo/repo" when the
	// real path is "/foo/repo.git" or "/foo/repo/.git").
	//
	// Default is false.
	StrictPaths bool

	// BasePath is the base path for all repositories.
	BasePath string

	// ExportAll is whether or not to export all repositories. Allow pulling
	// from all directories that look like Git repositories (have the objects
	// and refs subdirectories), even if they do not have the
	// git-daemon-export-ok file.
	//
	// Default is false.
	ExportAll bool

	// InitTimeout is the timeout (in seconds) between the moment the
	// connection is established and the client request is received
	//
	// Zero means no timeout.
	InitTimeout int

	// IdleTimeout is the timeout (in seconds) between successive client
	// requests and responses.
	//
	// Zero means no timeout.
	IdleTimeout int

	// MaxTimeout is the timeout (in seconds) is the total time the connection
	// is allowed to be open.
	//
	// Zero means no timeout.
	MaxTimeout int

	// Timeout is the timeout (in seconds) for specific client sub-requests.
	// This includes the time it takes for the server to process the
	// sub-request and the time spent waiting for the next client’s request.
	//
	// This timeout is applied to the following sub-requests:
	//  - upload-pack
	//
	// Zero means no timeout.
	Timeout int

	// MaxConnections is the maximum number of simultaneous connections.
	//
	// Zero means no limit.
	MaxConnections int

	// Logger is where to write logs.
	// Use nil to disable logging.
	//
	// Default is log.Defualt.
	Logger *log.Logger

	// Verbose is whether or not to enable verbose logging.
	Verbose bool

	// UploadPackHandler is the upload-pack service handler.
	// If nil, the upload-pack service is disabled.
	UploadPackHandler ServiceHandler

	// UploadArchiveHandler is the upload-archive service handler.
	// If nil, the upload-archive service is disabled.
	UploadArchiveHandler ServiceHandler

	// ReceivePackHandler is the receive-pack service handler.
	// If nil, the receive-pack service is disabled.
	ReceivePackHandler ServiceHandler

	// AccessHook is the access hook.
	// This is called before the service is started every time a client tries
	// to access a repository.
	// If the hook returns an error, the client is denied access.
	//
	// Default is a no-op.
	AccessHook AccessHook
	// contains filtered or unexported fields
}

Server represents the Git daemon configuration.

func (*Server) Close

func (s *Server) Close() error

Close force closes the Git daemon server.

func (*Server) Disable added in v0.2.0

func (s *Server) Disable(service Service)

Disable disables the given service.

func (*Server) Enable added in v0.2.0

func (s *Server) Enable(service Service)

Enable enables the given service.

func (*Server) HandleReceivePack

func (s *Server) HandleReceivePack(handler ServiceHandler)

HandleReceivePack sets the receive-pack service handler.

func (*Server) HandleUploadArchive

func (s *Server) HandleUploadArchive(handler ServiceHandler)

HandleUploadArchive sets the upload-archive service handler.

func (*Server) HandleUploadPack

func (s *Server) HandleUploadPack(handler ServiceHandler)

HandleUploadPack sets the upload-pack service handler.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe(addr string) error

ListenAndServe listens on the TCP network address s.Addr and then calls Serve to handle requests on incoming connections.

func (*Server) Serve

func (s *Server) Serve(l net.Listener) error

Serve serves the Git daemon server on the given listener l. Each incoming connection is handled in a separate goroutine.

func (*Server) Shutdown

func (s *Server) Shutdown(ctx context.Context) error

Shutdown gracefully shuts down the Git daemon server without interrupting any active connections.

type Service

type Service string

Service is a Git daemon service.

const (
	// UploadPackService is the upload-pack service.
	UploadPackService Service = "git-upload-pack"
	// UploadArchiveService is the upload-archive service.
	UploadArchiveService Service = "git-upload-archive"
	// ReceivePackService is the receive-pack service.
	ReceivePackService Service = "git-receive-pack"
)

func (Service) Name added in v0.1.1

func (s Service) Name() string

Name returns the name of the service.

func (Service) String

func (s Service) String() string

String returns the string representation of the service.

type ServiceHandler

type ServiceHandler func(ctx context.Context, path string, conn net.Conn, cmdFunc func(*exec.Cmd)) error

ServiceHandler is a Git transport daemon service handler. It a repository path, a client connection as arguments, and an optional command function as arguments.

The command function can be used to modify the Git command before it is run (for example to add environment variables).

Directories

Path Synopsis
cmd
git-daemon Module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL