Skip to content

Commit

Permalink
soft-server http git server
Browse files Browse the repository at this point in the history
Serves a dumb git server
  • Loading branch information
aymanbagabas committed Nov 18, 2021
1 parent b8a71d4 commit 7ea2837
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 34 deletions.
6 changes: 5 additions & 1 deletion config/config.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package config

import (
"crypto/tls"
"log"

"github.com/meowgorithm/babyenv"
Expand All @@ -16,10 +17,13 @@ type Callbacks interface {
// Config is the configuration for the soft-serve.
type Config struct {
Host string `env:"SOFT_SERVE_HOST" default:""`
Port int `env:"SOFT_SERVE_PORT" default:"23231"`
SSHPort int `env:"SOFT_SERVE_SSH_PORT" default:"23231"`
HTTPPort int `env:"SOFT_SERVE_HTTP_PORT" default:"23232"`
HTTPScheme string `env:"SOFT_SERVE_HTTP_SCHEME" default:"http"`
KeyPath string `env:"SOFT_SERVE_KEY_PATH" default:".ssh/soft_serve_server_ed25519"`
RepoPath string `env:"SOFT_SERVE_REPO_PATH" default:".repos"`
InitialAdminKey string `env:"SOFT_SERVE_INITIAL_ADMIN_KEY" default:""`
TLSConfig *tls.Config
Callbacks Callbacks
}

Expand Down
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ require (

require github.com/go-git/go-billy/v5 v5.3.1

require (
github.com/go-git/go-billy/v5 v5.3.1
goji.io v2.0.2+incompatible
)

require (
github.com/Microsoft/go-winio v0.4.16 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,8 @@ github.com/yuin/goldmark v1.3.3 h1:37BdQwPx8VOSic8eDSWee6QL9mRpZRm9VJp/QugNrW0=
github.com/yuin/goldmark v1.3.3/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
goji.io v2.0.2+incompatible h1:uIssv/elbKRLznFUy3Xj4+2Mz/qKhek/9aZQDUMae7c=
goji.io v2.0.2+incompatible/go.mod h1:sbqFwrtqZACxLBTQcdgVjFh54yGVCvwq8+w49MVMMIk=
golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
Expand Down
2 changes: 1 addition & 1 deletion internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ func NewConfig(cfg *config.Config) (*Config, error) {
var yamlUsers string
var displayHost string
host := cfg.Host
port := cfg.Port
port := cfg.SSHPort
pk := cfg.InitialAdminKey
rs := git.NewRepoSource(cfg.RepoPath)
c := &Config{
Expand Down
4 changes: 3 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"log"
"strings"

"github.com/charmbracelet/soft/config"
"github.com/charmbracelet/soft/server"
Expand All @@ -10,7 +11,8 @@ import (
func main() {
cfg := config.DefaultConfig()
s := server.NewServer(cfg)
log.Printf("Starting SSH server on %s:%d\n", cfg.Host, cfg.Port)
log.Printf("Starting SSH server on %s:%d\n", cfg.Host, cfg.SSHPort)
log.Printf("Starting %s server on %s:%d\n", strings.ToUpper(cfg.HTTPScheme), cfg.Host, cfg.HTTPPort)
err := s.Start()
if err != nil {
log.Fatalln(err)
Expand Down
67 changes: 67 additions & 0 deletions server/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package server

import (
"fmt"
"net/http"
"os"
"path/filepath"

"github.com/charmbracelet/soft/config"
appCfg "github.com/charmbracelet/soft/internal/config"
"github.com/charmbracelet/wish/git"
"goji.io"
"goji.io/pat"
"goji.io/pattern"
)

type HTTPServer struct {
server *http.Server
gitHandler http.Handler
cfg *config.Config
ac *appCfg.Config
}

func NewHTTPServer(cfg *config.Config, ac *appCfg.Config) *HTTPServer {
h := goji.NewMux()
s := &HTTPServer{
cfg: cfg,
ac: ac,
gitHandler: http.FileServer(http.Dir(cfg.RepoPath)),
server: &http.Server{
Addr: fmt.Sprintf(":%d", cfg.HTTPPort),
Handler: h,
TLSConfig: cfg.TLSConfig,
},
}
h.HandleFunc(pat.Get("/:repo"), s.handleGit)
h.HandleFunc(pat.Get("/:repo/*"), s.handleGit)
return s
}

func (s *HTTPServer) Start() error {
if s.cfg.HTTPScheme == "https" {
return s.server.ListenAndServeTLS("", "")
} else {
return s.server.ListenAndServe()
}
}

func (s *HTTPServer) handleGit(w http.ResponseWriter, r *http.Request) {
repo := pat.Param(r, "repo")
access := s.ac.AuthRepo(repo, nil)
if access < git.ReadOnlyAccess || !s.ac.AllowKeyless {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
path := pattern.Path(r.Context())
stat, err := os.Stat(filepath.Join(s.cfg.RepoPath, repo, path))
// Restrict access to files
if err != nil || stat.IsDir() {
http.NotFound(w, r)
return
}
w.Header().Set("Content-Type", "text/plain; charset=utf-8")
w.Header().Set("X-Content-Type-Options", "nosniff")
r.URL.Path = fmt.Sprintf("/%s/%s", repo, path)
s.gitHandler.ServeHTTP(w, r)
}
46 changes: 15 additions & 31 deletions server/server.go
Original file line number Diff line number Diff line change
@@ -1,24 +1,17 @@
package server

import (
"fmt"
"log"

"github.com/charmbracelet/soft/config"
appCfg "github.com/charmbracelet/soft/internal/config"
"github.com/charmbracelet/soft/internal/tui"

"github.com/charmbracelet/wish"
bm "github.com/charmbracelet/wish/bubbletea"
gm "github.com/charmbracelet/wish/git"
lm "github.com/charmbracelet/wish/logging"
"github.com/gliderlabs/ssh"
)

type Server struct {
SSHServer *ssh.Server
Config *config.Config
config *appCfg.Config
HTTPServer *HTTPServer
SSHServer *SSHServer
Config *config.Config
ac *appCfg.Config
}

// NewServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
Expand All @@ -31,32 +24,23 @@ func NewServer(cfg *config.Config) *Server {
if err != nil {
log.Fatal(err)
}
mw := []wish.Middleware{
bm.Middleware(tui.SessionHandler(ac)),
gm.Middleware(cfg.RepoPath, ac),
lm.Middleware(),
}
s, err := wish.NewServer(
ssh.PublicKeyAuth(ac.PublicKeyHandler),
ssh.PasswordAuth(ac.PasswordHandler),
wish.WithAddress(fmt.Sprintf("%s:%d", cfg.Host, cfg.Port)),
wish.WithHostKeyPath(cfg.KeyPath),
wish.WithMiddleware(mw...),
)
if err != nil {
log.Fatalln(err)
}
return &Server{
SSHServer: s,
Config: cfg,
config: ac,
HTTPServer: NewHTTPServer(cfg, ac),
SSHServer: NewSSHServer(cfg, ac),
Config: cfg,
ac: ac,
}
}

func (srv *Server) Reload() error {
return srv.config.Reload()
return srv.ac.Reload()
}

func (srv *Server) Start() error {
return srv.SSHServer.ListenAndServe()
go func() {
if err := srv.HTTPServer.Start(); err != nil {
log.Fatal(err)
}
}()
return srv.SSHServer.Start()
}
53 changes: 53 additions & 0 deletions server/ssh.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package server

import (
"fmt"
"log"

"github.com/charmbracelet/soft/config"
appCfg "github.com/charmbracelet/soft/internal/config"
"github.com/charmbracelet/soft/internal/tui"
"github.com/charmbracelet/wish"
bm "github.com/charmbracelet/wish/bubbletea"
gm "github.com/charmbracelet/wish/git"
lm "github.com/charmbracelet/wish/logging"
"github.com/gliderlabs/ssh"
)

type SSHServer struct {
s *ssh.Server
cfg *config.Config
ac *appCfg.Config
}

// NewSSHServer returns a new *ssh.Server configured to serve Soft Serve. The SSH
// server key-pair will be created if none exists. An initial admin SSH public
// key can be provided with authKey. If authKey is provided, access will be
// restricted to that key. If authKey is not provided, the server will be
// publicly writable until configured otherwise by cloning the `config` repo.
func NewSSHServer(cfg *config.Config, ac *appCfg.Config) *SSHServer {
mw := []wish.Middleware{
bm.Middleware(tui.SessionHandler(ac)),
gm.Middleware(cfg.RepoPath, ac),
lm.Middleware(),
}
s, err := wish.NewServer(
ssh.PublicKeyAuth(ac.PublicKeyHandler),
ssh.PasswordAuth(ac.PasswordHandler),
wish.WithAddress(fmt.Sprintf("%s:%d", cfg.Host, cfg.SSHPort)),
wish.WithHostKeyPath(cfg.KeyPath),
wish.WithMiddleware(mw...),
)
if err != nil {
log.Fatalln(err)
}
return &SSHServer{
s: s,
cfg: cfg,
ac: ac,
}
}

func (s *SSHServer) Start() error {
return s.s.ListenAndServe()
}

0 comments on commit 7ea2837

Please sign in to comment.