gitdir

package module
v0.0.0-...-8d48e90 Latest Latest
Warning

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

Go to latest
Published: Mar 13, 2024 License: MIT Imports: 20 Imported by: 0

README

go-gitdir

Go Report Card Build Status

This project makes it incredibly easy to host a secure git server with a config that can be easily rolled back.

It aims to solve a number of problems other git servers have:

  • Requires no external dependencies other than the binary and git
  • Stores its configuration in a repo managed by itself
  • Doesn't hook into the system's user accounts
  • No vendor lock-in - everything is just a bare git repository

Origins

The main goal of this project is to enable simple git hosting when a full solution like Bitbucket, Github, Gitlab, Gitea, etc is not needed.

This project was inspired by gitolite and gitosis, but also includes a built-in ssh server and some additional flexability. It is not considered stable, but should be usable enough to experiment with.

Thankfully because all the repos are simply stored as bare git repositories, it should be fairly simple to migrate to or from other git hosting solutions. There is no vendor lock-in.

Requirements

Build requirements:

  • Go >= 1.13

Runtime requirements:

  • git (for git-receive-pack and git-upload-pack)

Building

Clone the repository somewhere, outside the GOPATH. Then, from the root of the source tree, run:

go build ./cmd/gitdir

This will create a binary called gitdir.

Running

Server Config

There are a number of environment variables which can be used to configure your go-git-dir instance.

The following are required:

  • GITDIR_BASE_DIR - A directory to store all repositories in. This folder must exist when the service starts up.

The following are optional:

  • GITDIR_BIND_ADDR - The address and port to bind the service to. This defaults to :2222.
  • GITDIR_LOG_READABLE - A true value if the log should be human readable
  • GITDIR_LOG_DEBUG - A true value if debug logging should be enabled
  • GITDIR_ADMIN_USER - The name of an admin user which the server will ensure exists on startup.
  • GITHUB_ADMIN_PUBLIC_KEY - The contents of a public key which will be added to the admin user on startup.
Runtime Config

The runtime config is stored in the "admin" repository. It can be cloned and modified by any admin on the server. In it you can specify groups (groupings of users for config or convenience reasons), repos, and orgs (groupings of repos managed by a person).

Additionally, there are a number of options that can be specified in this file which change the behavior of the server.

  • implicit_repos - allows a user with admin access to that area to create repos by simply pushing to them.
  • user_config_keys - allows users to specify ssh keys in their own config, rather than relying on the main admin config.
  • user_config_repos - allows users to specify repos in their own config, rather than relying on the main admin config.
  • org_config_repos - allows org admins to specify repos in their own config, rather than relying on the main admin config.

Usage

Simply run the built binary with GITDIR_BASE_DIR set and start using it!

On first run, gitdir will push a commit to the admin repo with a sample config as well as generated server ssh keys. These can be updated at any time (even at runtime) but if the server restarts and the keys cannot be loaded, they will be re-generated.

If you set GITDIR_ADMIN_USER and GITHUB_ADMIN_PUBLIC_KEY an admin user will automatically be added to the config.

If you do not set those environment variables, you will need to manually clone the admin repository (at $GITDIR_BASE_DIR/admin/admin) to add a user to config.yml and set them as an admin.

Sample Config

Sample admin config.yml:

users:
  belak:
    is_admin: true
    keys:
      - ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIDeQfBUWIqpGXS8xCOg/0RKVOGTnzpIdL7r9wK1/xA52 belak@tmp
    repos:
      personal-gitdir: {}

groups:
  admins:
    - belak

repos:
  go-gitdir:
    public: true

    write:
      - $admins
    read:
      - some-other-user

orgs:
  vault:
    admins:
      - $admins
    write:
      - some-org-user
    read:
      - some-other-org-user

    repos:
      the-vault:
        write:
          - some-repo-access-user

options:
  implicit_repos: false
  user_config_keys: true
  user_config_repos: false
  org_config_repos: false

Repo Creation

All repos defined in the config are created when the config is loaded. At runtime, if implicit repos are enabled, trying to access a repo where you have admin access will implicitly create it.

Documentation

Index

Constants

This section is empty.

Variables

View Source
var AnonymousUser = &User{
	Username:    "<anonymous>",
	IsAnonymous: true,
	IsAdmin:     false,
}

AnonymousUser is the user that is returned when no user is available.

View Source
var ErrInvalidRepoFormat = errors.New("invalid repo format")

ErrInvalidRepoFormat is returned when a repo is looked up which cannot exist based on the parsed format.

View Source
var ErrRepoDoesNotExist = errors.New("repo does not exist")

ErrRepoDoesNotExist is returned when a repo is looked up which cannot exist based on the config.

View Source
var ErrUserNotFound = errors.New("user not found")

ErrUserNotFound is returned from LookupUser commands when the user is not found.

Functions

func CtxExtract

func CtxExtract(ctx context.Context) (*zerolog.Logger, *Config, *User)

CtxExtract is a convenience wrapper around the other context convenience methods to pull out everything you'd want from a request.

func CtxLogger

func CtxLogger(ctx context.Context) *zerolog.Logger

CtxLogger pulls the logger out of the context, or the default logger if not found.

func CtxPublicKey

func CtxPublicKey(ctx context.Context) *models.PublicKey

CtxPublicKey pulls the public key out of the context, or nil if not found.

func CtxSetConfig

func CtxSetConfig(parent ssh.Context, config *Config)

CtxSetConfig puts the given Config into the ssh.Context.

func CtxSetLogger

func CtxSetLogger(parent ssh.Context, logger *zerolog.Logger)

CtxSetLogger puts the given logger into the ssh.Context.

func CtxSetPublicKey

func CtxSetPublicKey(parent ssh.Context, pk *models.PublicKey)

CtxSetPublicKey puts the given public key into the ssh.Context.

func CtxSetUser

func CtxSetUser(parent ssh.Context, user *User)

CtxSetUser puts the given User into the ssh.Context.

func WithLogger

func WithLogger(parent context.Context, logger *zerolog.Logger) context.Context

WithLogger takes a parent context and a logger and returns a new context with that logger.

Types

type AccessLevel

type AccessLevel int

AccessLevel represents the level of access being requested and the level of access a user has.

const (
	AccessLevelNone AccessLevel = iota
	AccessLevelRead
	AccessLevelWrite
	AccessLevelAdmin
)

AccessLevel defaults to AccessLevelNone for security. A repo lookup returns the level of permissions a user has and if it's not explicitly set, they don't have any.

func (AccessLevel) String

func (a AccessLevel) String() string

String implements Stringer.

type Config

type Config struct {
	Invites     map[string]string
	Groups      map[string][]string
	Orgs        map[string]*models.OrgConfig
	Users       map[string]*models.AdminConfigUser
	Repos       map[string]*models.RepoConfig
	Options     models.AdminConfigOptions
	PrivateKeys []models.PrivateKey
	// contains filtered or unexported fields
}

Config represents the config which has been loaded from all repos.

func CtxConfig

func CtxConfig(ctx context.Context) *Config

CtxConfig pulls the current Config out of the context, or a blank Config if not set.

func NewConfig

func NewConfig(fs billy.Filesystem) *Config

NewConfig returns an empty config, attached to the given fs. In general, Load should be called after creating a new config at a bare minimum.

func (*Config) EnsureAdminUser

func (c *Config) EnsureAdminUser(username string, pubKey *models.PublicKey) error

EnsureUser will load the current admin config and ensure the given user exists.

func (*Config) EnsureConfig

func (c *Config) EnsureConfig() error

func (*Config) Load

func (c *Config) Load() error

Load will load the config from the given fs.

func (*Config) LookupRepoAccess

func (c *Config) LookupRepoAccess(user *User, path string) (*RepoLookup, error)

LookupRepoAccess checks to see if path points to a valid repo and attaches the access level this user has on that repository.

func (*Config) LookupUserFromInvite

func (c *Config) LookupUserFromInvite(invite string) (*User, error)

LookupUserFromInvite looks up a user object given an invite code.

func (*Config) LookupUserFromKey

func (c *Config) LookupUserFromKey(pk models.PublicKey, remoteUser string) (*User, error)

LookupUserFromKey looks up a user object given their PublicKey.

func (*Config) LookupUserFromUsername

func (c *Config) LookupUserFromUsername(username string) (*User, error)

LookupUserFromUsername looks up a user objects given their username.

func (*Config) RunHook

func (c *Config) RunHook(
	hook string,
	repoPath string,
	pk *models.PublicKey,
	args []string,
	stdin io.Reader,
) error

RunHook will run the given hook.

func (*Config) SetHash

func (c *Config) SetHash(hash string) error

SetHash will set the hash of the admin repo to use when loading.

func (*Config) SetOrgHash

func (c *Config) SetOrgHash(orgName, hash string) error

SetOrgHash will set the hash of the given org repo to use when loading.

func (*Config) SetUserHash

func (c *Config) SetUserHash(username, hash string) error

SetUserHash will set the hash of the given user repo to use when loading.

func (*Config) Validate

func (c *Config) Validate(user *User, pk *models.PublicKey) error

Validate will ensure the config is valid and return any errors.

type RepoLookup

type RepoLookup struct {
	Type      RepoType
	PathParts []string
	Access    AccessLevel
}

RepoLookup represents a repository that has been confirmed in the config and the access level the given user has.

func (RepoLookup) Path

func (repo RepoLookup) Path() string

Path returns the full path to this repository on disk. This is relative to the gitdir root.

type RepoType

type RepoType int

RepoType represents the different types of repositories that can be accessed.

const (
	RepoTypeAdmin RepoType = iota
	RepoTypeOrgConfig
	RepoTypeOrg
	RepoTypeUserConfig
	RepoTypeUser
	RepoTypeTopLevel
)

RepoType defaults to RepoTypeAdmin to make sure that if this is improperly set, the only way to access it is by being an admin.

func (RepoType) String

func (r RepoType) String() string

String implements Stringer.

type Server

type Server struct {
	Addr string
	// contains filtered or unexported fields
}

Server represents a gitdir server.

func NewServer

func NewServer(fs billy.Filesystem) (*Server, error)

NewServer configures a new gitdir server and attempts to load the config from the admin repo.

func (*Server) EnsureAdminUser

func (serv *Server) EnsureAdminUser(username string, pubKey *models.PublicKey) error

func (*Server) GetAdminConfig

func (serv *Server) GetAdminConfig() *Config

GetAdminConfig returns the current admin config in a thread-safe manner. The config should not be modified.

func (*Server) ListenAndServe

func (serv *Server) ListenAndServe() error

ListenAndServe listens on the Addr set on the server struct for new SSH connections.

func (*Server) Reload

func (serv *Server) Reload() error

Reload reloads the server config in a thread-safe way.

func (*Server) Serve

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

Serve listens on the given listener for new SSH connections.

type User

type User struct {
	Username    string
	IsAnonymous bool
	IsAdmin     bool
}

User is the internal representation of a user. This data is copied from the loaded config file.

func CtxUser

func CtxUser(ctx context.Context) *User

CtxUser pulls the current User out of the context, or AnonymousUser if not set.

Directories

Path Synopsis
cmd
internal
git

Jump to

Keyboard shortcuts

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