Skip to content

Commit

Permalink
Merge pull request #24 from chrhlnd/support-https
Browse files Browse the repository at this point in the history
enable https, borrow some stdlib wrapping classes
  • Loading branch information
rubyist committed Apr 29, 2015
2 parents a3afb18 + 5e18ecc commit 4b6c5f7
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 1 deletion.
71 changes: 71 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,85 @@ There are few things that can be configured via environment variables:
LFS_CONTENTPATH # The path where LFS files are store, default: "lfs-content"
LFS_ADMINUSER # An administrator username, default: unset
LFS_ADMINPASS # An administrator password, default: unset
LFS_CERT # Certificate file for tls
LFS_KEY # tls key
LFS_SCHEME # set to 'https' to override default http

If the `LFS_ADMINUSER` and `LFS_ADMINPASS` variables are set, a
rudimentary admin interface can be accessed via
`http://$LFS_HOST/mgmt`. Here you can add and remove users.

To use the LFS test server with the Git LFS client, configure it in the repository's `.gitconfig` file:


```
[lfs]
url = "http://localhost:8080/janedoe/lfsrepo"
```

HTTPS:

NOTE: If using https with a self signed cert also disable cert checking in the client repo.

```
[lfs]
url = "https://localhost:8080/jimdoe/lfsrepo"
[http]
selfverify = false
```


An example usage:


Generate a key pair
```
openssl req -x509 -sha256 -nodes -days 2100 -newkey rsa:2048 -keyout mine.key -out mine.crt
```

Make yourself a run script

```
#!/bin/bash
set -eu
set -o pipefail
LFS_LISTEN="tcp://:9999"
LFS_HOST="127.0.0.1:9999"
LFS_CONTENTPATH="content"
LFS_ADMINUSER="<cool admin user name>"
LFS_ADMINPASS="<better admin password>"
LFS_CERT="mine.crt"
LFS_KEY="mine.key"
LFS_SCHEME="https"
export LFS_LISTEN LFS_HOST LFS_CONTENTPATH LFS_ADMINUSER LFS_ADMINPASS LFS_CERT LFS_KEY LFS_SCHEME
./lfs-test-server
```

Build the server

```
go build
```

Run

```
bash run.sh
```

Check the managment page

browser: https://localhost:9999/mgmt


7 changes: 7 additions & 0 deletions config.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ type Configuration struct {
ContentPath string `config:"lfs-content"`
AdminUser string `config:""`
AdminPass string `config:""`
Cert string `config:""`
Key string `config:""`
Scheme string `config:""`
}

func (c *Configuration) IsHTTPS() bool {
return strings.Contains(Config.Scheme, "https")
}

// Config is the global app configuration
Expand Down
55 changes: 54 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package main

import (
"crypto/tls"
"fmt"
"net"
"os"
"os/signal"
"syscall"
"time"
)

const (
Expand All @@ -18,17 +20,68 @@ var (
logger = NewKVLogger(os.Stdout)
)

// tcpKeepAliveListener sets TCP keep-alive timeouts on accepted
// connections. It's used by ListenAndServe and ListenAndServeTLS so
// dead TCP connections (e.g. closing laptop mid-download) eventually
// go away.
type tcpKeepAliveListener struct {
*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
tc, err := ln.AcceptTCP()
if err != nil {
return
}
tc.SetKeepAlive(true)
tc.SetKeepAlivePeriod(3 * time.Minute)
return tc, nil
}

func wrapHttps(l net.Listener, cert, key string) (net.Listener, error) {
var err error

config := &tls.Config{}

if config.NextProtos == nil {
config.NextProtos = []string{"http/1.1"}
}

config.Certificates = make([]tls.Certificate, 1)
config.Certificates[0], err = tls.LoadX509KeyPair(cert, key)
if err != nil {
return nil, err
}

netListener := l.(*TrackingListener).Listener

tlsListener := tls.NewListener(tcpKeepAliveListener{netListener.(*net.TCPListener)}, config)
return tlsListener, nil
}

func main() {
if len(os.Args) == 2 && os.Args[1] == "-v" {
fmt.Println(version)
os.Exit(0)
}

var listener net.Listener

tl, err := NewTrackingListener(Config.Listen)
if err != nil {
logger.Fatal(kv{"fn": "main", "err": "Could not create listener: " + err.Error()})
}

listener = tl

if Config.IsHTTPS() {
logger.Log(kv{"fn": "main", "msg": "Using https"})
listener, err = wrapHttps(tl, Config.Cert, Config.Key)
if err != nil {
logger.Fatal(kv{"fn": "main", "err": "Could not create https listener: " + err.Error()})
}
}

metaStore, err := NewMetaStore(Config.MetaDB)
if err != nil {
logger.Fatal(kv{"fn": "main", "err": "Could not open the meta store: " + err.Error()})
Expand All @@ -54,6 +107,6 @@ func main() {
logger.Log(kv{"fn": "main", "msg": "listening", "pid": os.Getpid(), "addr": Config.Listen, "version": version})

app := NewApp(contentStore, metaStore)
app.Serve(tl)
app.Serve(listener)
tl.WaitForChildren()
}
5 changes: 5 additions & 0 deletions server.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ type Representation struct {
// ObjectLink builds a URL linking to the object.
func (v *RequestVars) ObjectLink() string {
path := fmt.Sprintf("/%s/%s/objects/%s", v.User, v.Repo, v.Oid)

if Config.IsHTTPS() {
return fmt.Sprintf("%s://%s%s", Config.Scheme, Config.Host, path)
}

return fmt.Sprintf("http://%s%s", Config.Host, path)
}

Expand Down

0 comments on commit 4b6c5f7

Please sign in to comment.