Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,13 @@ git config lfs.url http://localhost:9876/
# you can confirm the Endpoint that will be used by running
git lfs env | grep Endpoint
```

When running multiple instances of lfscache, you must use a shared hmac key so
signatures from one instance can be verified by the others with a shared key.
This key is not used for storage, so it's safe to rotate the key and restart all
instances.

```
dd if=/dev/urandom bs=1 count=64 > hmac-key
$ ./lfscache --hmac-key-file hmac-key ...
```
15 changes: 14 additions & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"errors"
"flag"
"fmt"
"io/ioutil"
"net"
"net/http"
"net/url"
Expand All @@ -30,6 +31,7 @@ func main() {
tlsCert = flag.String("tls-cert", "", "HTTPS TLS certificate filepath")
lfsServerURL = flag.String("url", "", "LFS server URL")
directory = flag.String("directory", "./objects", "cache directory")
hmacKeyFile = flag.String("hmac-key-file", "", "file containing 64 byte HMAC key for request signing")
printVersion = flag.Bool("v", false, "print version")
)

Expand All @@ -56,7 +58,18 @@ func main() {
os.Exit(1)
}

s, err := server.New(logger, addr.String(), *directory)
var hmacKey []byte = nil
if *hmacKeyFile != "" {
hmac, err := ioutil.ReadFile(*hmacKeyFile)
if err != nil {
level.Error(logger).Log("err", err)
os.Exit(1)
}
hmacKey = hmac
}


s, err := server.New(logger, addr.String(), *directory, hmacKey)
if err != nil {
panic(err)
}
Expand Down
20 changes: 12 additions & 8 deletions server/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,16 +98,16 @@ type Server struct {
}

// New returns a new LFS proxy caching server.
func New(logger log.Logger, upstream, directory string) (*Server, error) {
return newServer(logger, upstream, directory, true)
func New(logger log.Logger, upstream, directory string, hmacKey []byte) (*Server, error) {
return newServer(logger, upstream, directory, true, hmacKey)
}

// NewNoCache returns a new LFS proxy server, with no caching.
func NewNoCache(logger log.Logger, upstream string) (*Server, error) {
return newServer(logger, upstream, "", false)
return newServer(logger, upstream, "", false, nil)
}

func newServer(logger log.Logger, upstream, directory string, cacheEnabled bool) (*Server, error) {
func newServer(logger log.Logger, upstream, directory string, cacheEnabled bool, hmacKey []byte) (*Server, error) {
var fs *cache.FilesystemCache
var err error
if cacheEnabled {
Expand Down Expand Up @@ -135,10 +135,14 @@ func newServer(logger log.Logger, upstream, directory string, cacheEnabled bool)
ObjectBatchActionURLRewriter: DefaultObjectBatchActionURLRewriter,
}

_, err = rand.Read(s.hmacKey[:])
if err != nil {
return nil, err
}
if hmacKey != nil {
copy(s.hmacKey[:], hmacKey)
} else {
_, err = rand.Read(s.hmacKey[:])
if err != nil {
return nil, err
}
}
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aaronborden-rivian Can you run go fmt for this file?

go fmt automatically formats Go files to ensure that they're always consistent. I probably should add a CI linter job to detect this.


if s.upstream, err = url.Parse(upstream); err != nil {
return nil, err
Expand Down
19 changes: 18 additions & 1 deletion server/server_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package server

import (
"crypto/rand"
"encoding/json"
"fmt"
"io/ioutil"
Expand Down Expand Up @@ -47,11 +48,27 @@ func server() (*httptest.Server, *Server, string, error) {
return ts, nil, dir, err
}

s, err := New(log.NewNopLogger(), ts.URL, dir)
s, err := New(log.NewNopLogger(), ts.URL, dir, nil)

return ts, s, dir, err
}

func TestHmac(t *testing.T) {
var hmac [64]byte
_, err := rand.Read(hmac[:])
require.NoError(t, err)

s, err := New(log.NewNopLogger(), "http://example.com", "", hmac[:])
require.NoError(t, err)
assert.Equal(t, s.hmacKey, hmac)
}

func TestNoHmac(t *testing.T) {
s, err := New(log.NewNopLogger(), "http://example.com", "", nil)
require.NoError(t, err)
assert.NotEmpty(t, s.hmacKey)
}

func TestProxy(t *testing.T) {
ts, s, dir, err := server()
defer os.RemoveAll(dir)
Expand Down