Add config

This commit is contained in:
Torjus Håkestad 2021-12-04 03:25:09 +01:00
parent d53235d8ef
commit 377962440c
13 changed files with 326 additions and 128 deletions

4
.gitignore vendored
View File

@ -1,2 +1,2 @@
certs/*.pem
certs/*.key
tmp/*
ezshare.toml

View File

@ -18,4 +18,4 @@ FROM alpine:latest
COPY --from=builder-base /app/ezshare /usr/bin/ezshare
EXPOSE 50051
EXPOSE 8088
CMD ["/usr/bin/ezshare", "serve"]
CMD ["/usr/bin/ezshare","--config", "/data/ezshare.toml" "serve"]

View File

@ -1,25 +0,0 @@
//go:build allcerts
package certs
import (
_ "embed"
)
//go:embed ca.key
var CAKey []byte
//go:embed ca.pem
var CACert []byte
//go:embed srv.key
var SrvKey []byte
//go:embed srv.pem
var SrvCert []byte
//go:embed client.key
var ClientKey []byte
//go:embed client.pem
var ClientCert []byte

View File

@ -1,23 +0,0 @@
//go:build clientcerts
package certs
import (
_ "embed"
)
var CAKey []byte
//go:embed ca.pem
var CACert []byte
var SrvKey []byte
//go:embed srv.pem
var SrvCert []byte
//go:embed client.key
var ClientKey []byte
//go:embed client.key
var ClientCert []byte

View File

@ -1,19 +0,0 @@
//go:build !allcerts && !clientcerts
package certs
import (
_ "embed"
)
var CAKey []byte
var CACert []byte
var SrvKey []byte
var SrvCert []byte
var ClientKey []byte
var ClientCert []byte

View File

@ -11,6 +11,7 @@ import (
"fmt"
"math/big"
"os"
"path/filepath"
"time"
)
@ -88,16 +89,16 @@ func GenCACert() (priv []byte, pub []byte, err error) {
return caPrivKeyBytes, caBytes, nil
}
func GenCerts() error {
func GenAllCerts(path string) error {
// Create CA certs
caPriv, caPub, err := GenCACert()
if err != nil {
return err
}
if err := WriteKey(caPriv, "certs/ca.key"); err != nil {
if err := WriteKey(caPriv, filepath.Join(path, "ca.key")); err != nil {
return err
}
if err := WriteCert(caPub, "certs/ca.pem"); err != nil {
if err := WriteCert(caPub, filepath.Join(path, "ca.pem")); err != nil {
return err
}
@ -106,10 +107,10 @@ func GenCerts() error {
if err != nil {
return err
}
if err := WriteKey(srvKey, "certs/srv.key"); err != nil {
if err := WriteKey(srvKey, filepath.Join(path, "srv.key")); err != nil {
return err
}
if err := WriteCert(srvCrt, "certs/srv.pem"); err != nil {
if err := WriteCert(srvCrt, filepath.Join(path, "srv.pem")); err != nil {
return err
}
@ -117,10 +118,10 @@ func GenCerts() error {
if err != nil {
return err
}
if err := WriteKey(clientKey, "certs/client.key"); err != nil {
if err := WriteKey(clientKey, filepath.Join(path, "client.key")); err != nil {
return err
}
if err := WriteCert(clientCrt, "certs/client.pem"); err != nil {
if err := WriteCert(clientCrt, filepath.Join(path, "client.pem")); err != nil {
return err
}

View File

@ -1,17 +0,0 @@
package client
import (
"context"
"gitea.benny.dog/torjus/ezshare/pb"
"google.golang.org/grpc"
)
func NewClient(ctx context.Context, addr string) (pb.FileServiceClient, error) {
conn, err := grpc.DialContext(ctx, addr, grpc.WithInsecure())
if err != nil {
return nil, err
}
client := pb.NewFileServiceClient(conn)
return client, nil
}

View File

@ -15,6 +15,7 @@ import (
"time"
"gitea.benny.dog/torjus/ezshare/certs"
"gitea.benny.dog/torjus/ezshare/config"
"gitea.benny.dog/torjus/ezshare/pb"
"gitea.benny.dog/torjus/ezshare/server"
"gitea.benny.dog/torjus/ezshare/store"
@ -26,6 +27,12 @@ import (
func main() {
app := cli.App{
Name: "ezshare",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "config",
Usage: "Path to config-file.",
},
},
Commands: []*cli.Command{
{
Name: "serve",
@ -79,8 +86,12 @@ func main() {
},
},
{
Name: "gencerts",
Usage: "Generate certificates",
Name: "cert",
Usage: "Certificate commands",
Subcommands: []*cli.Command{
{
Name: "gen-all",
Usage: "Generate CA, Server and Client certificates",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "out-dir",
@ -90,6 +101,8 @@ func main() {
Action: ActionGencerts,
},
},
},
},
}
err := app.Run(os.Args)
@ -100,6 +113,25 @@ func main() {
}
func ActionServe(c *cli.Context) error {
cfg, err := getConfig(c)
if err != nil {
return err
}
// Read certificates
srvCertBytes, err := cfg.Server.GRPC.Certs.GetCertBytes()
if err != nil {
return err
}
srvKeyBytes, err := cfg.Server.GRPC.Certs.GetKeyBytes()
if err != nil {
return err
}
caCertBytes, err := cfg.Server.GRPC.CACerts.GetCertBytes()
if err != nil {
return err
}
fileStore := store.NewMemoryFileStore()
// Setup shutdown-handling
rootCtx, rootCancel := signal.NotifyContext(context.Background(), os.Interrupt)
@ -115,7 +147,7 @@ func ActionServe(c *cli.Context) error {
// Start grpc server
go func() {
grpcAddr := ":50051"
grpcAddr := cfg.Server.GRPC.ListenAddr
if c.IsSet("grpc-addr") {
grpcAddr = c.String("grpc-addr")
}
@ -130,14 +162,13 @@ func ActionServe(c *cli.Context) error {
log.Printf("Unable to setup grpc listener: %s\n", err)
rootCancel()
}
srvCert, err := tls.X509KeyPair(certs.SrvCert, certs.SrvKey)
srvCert, err := tls.X509KeyPair(srvCertBytes, srvKeyBytes)
if err != nil {
log.Printf("%d %d", len(certs.SrvCert), len(certs.SrvKey))
log.Printf("Unable load server certs: %s\n", err)
rootCancel()
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certs.CACert) {
if !certPool.AppendCertsFromPEM(caCertBytes) {
log.Println("Unable to load CA cert")
rootCancel()
}
@ -242,8 +273,17 @@ func ActionClientGet(c *cli.Context) error {
}
func ActionClientUpload(c *cli.Context) error {
addr := c.String("addr")
clientCreds, err := getClientCreds()
cfg, err := getConfig(c)
if err != nil {
return err
}
addr := cfg.Client.DefaultServer
if c.IsSet("addr") {
addr = c.String("addr")
}
clientCreds, err := cfg.Client.Creds()
if err != nil {
return err
}
@ -279,22 +319,22 @@ func ActionClientUpload(c *cli.Context) error {
}
func ActionGencerts(c *cli.Context) error {
return certs.GenCerts()
outDir := "."
if c.IsSet("out-dir") {
outDir = c.String("out-dir")
}
return certs.GenAllCerts(outDir)
}
func getClientCreds() (credentials.TransportCredentials, error) {
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(certs.CACert) {
return nil, fmt.Errorf("unable to load ca cert")
func getConfig(c *cli.Context) (*config.Config, error) {
if c.IsSet("config") {
cfgPath := c.String("config")
return config.FromFile(cfgPath)
}
clientCert, err := tls.X509KeyPair(certs.ClientCert, certs.ClientKey)
if err != nil {
return nil, fmt.Errorf("unable to load client cert: %s", err)
cfg, err := config.FromDefaultLocations()
if err == nil {
fmt.Printf("Config loaded from %s\n", cfg.Location())
fmt.Printf("Config: %+v\n", cfg)
}
config := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: certPool,
}
return credentials.NewTLS(config), nil
return cfg, err
}

175
config/config.go Normal file
View File

@ -0,0 +1,175 @@
package config
import (
"crypto/tls"
"crypto/x509"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"github.com/pelletier/go-toml"
"google.golang.org/grpc/credentials"
)
type Config struct {
LogLevel string `toml:"LogLevel"`
Server *ServerConfig `toml:"Server"`
Client *ClientConfig `toml:"Client"`
location string
}
type CertificatePaths struct {
CertificatePath string `toml:"CertificatePath"`
CertificateKeyPath string `toml:"CertificateKeyPath"`
}
type ServerConfig struct {
GRPC *ServerGRPCConfig `toml:"GRPC"`
HTTP *ServerHTTPConfig `toml:"HTTP"`
}
type ServerStoreConfig struct {
Type string `toml:"Type"`
FSStoreConfig *FSStoreConfig `toml:"Filesystem"`
}
type FSStoreConfig struct {
Dir string `toml:"Dir"`
}
type ServerGRPCConfig struct {
ListenAddr string `toml:"ListenAddr"`
CACerts *CertificatePaths `toml:"CACerts"`
Certs *CertificatePaths `toml:"Certs"`
}
type ServerHTTPConfig struct {
ListenAddr string `toml:"ListenAddr"`
}
type ClientConfig struct {
DefaultServer string `toml:"DefaultServer"`
ServerCertPath string `toml:"ServerCertPath"`
Certs *CertificatePaths `toml:"Certs"`
}
func FromDefault() *Config {
cfg := &Config{
LogLevel: "INFO",
Server: &ServerConfig{
GRPC: &ServerGRPCConfig{
ListenAddr: ":50051",
CACerts: &CertificatePaths{},
Certs: &CertificatePaths{},
},
HTTP: &ServerHTTPConfig{
ListenAddr: ":8089",
},
},
Client: &ClientConfig{},
}
return cfg
}
func FromReader(r io.Reader) (*Config, error) {
decoder := toml.NewDecoder(r)
c := FromDefault()
if err := decoder.Decode(c); err != nil {
return nil, fmt.Errorf("unable to read config: %w", err)
}
return c, nil
}
func FromFile(path string) (*Config, error) {
f, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("unable to open config-file: %w", err)
}
defer f.Close()
cfg, err := FromReader(f)
if err == nil {
cfg.location = path
}
return cfg, err
}
func FromDefaultLocations() (*Config, error) {
defaultLocations := []string{
"ezshare.toml",
}
userConfigDir, err := os.UserConfigDir()
if err != nil {
defaultLocations = append(defaultLocations, filepath.Join(userConfigDir, "ezshare", "ezshare.toml"))
}
for _, location := range defaultLocations {
if _, err := os.Stat(location); err == nil {
return FromFile(location)
}
}
return nil, fmt.Errorf("config not found")
}
func (c *Config) Location() string {
return c.location
}
func (cp *CertificatePaths) GetCertBytes() ([]byte, error) {
f, err := os.Open(cp.CertificatePath)
if err != nil {
return nil, err
}
return ioutil.ReadAll(f)
}
func (cp *CertificatePaths) GetKeyBytes() ([]byte, error) {
f, err := os.Open(cp.CertificateKeyPath)
if err != nil {
return nil, err
}
return ioutil.ReadAll(f)
}
func (cc *ClientConfig) ServerCertBytes() ([]byte, error) {
f, err := os.Open(cc.ServerCertPath)
if err != nil {
return nil, fmt.Errorf("unable to open server certificate: %w", err)
}
defer f.Close()
data, err := ioutil.ReadAll(f)
if err != nil {
return nil, fmt.Errorf("unable to read client server certificate: %w", err)
}
return data, nil
}
func (cc *ClientConfig) Creds() (credentials.TransportCredentials, error) {
srvCertBytes, err := cc.ServerCertBytes()
if err != nil {
return nil, err
}
clientCertBytes, err := cc.Certs.GetCertBytes()
if err != nil {
return nil, fmt.Errorf("unable to read client cert: %w", err)
}
clientKeyBytes, err := cc.Certs.GetKeyBytes()
if err != nil {
return nil, fmt.Errorf("unable to read client cert: %w", err)
}
certPool := x509.NewCertPool()
if !certPool.AppendCertsFromPEM(srvCertBytes) {
return nil, fmt.Errorf("unable to load ca cert")
}
clientCert, err := tls.X509KeyPair(clientCertBytes, clientKeyBytes)
if err != nil {
return nil, fmt.Errorf("unable to load client cert: %s", err)
}
config := &tls.Config{
Certificates: []tls.Certificate{clientCert},
RootCAs: certPool,
}
return credentials.NewTLS(config), nil
}

63
ezshare.example.toml Normal file
View File

@ -0,0 +1,63 @@
########################
# Server configuration #
########################
[Server]
# Set server log-level
# Must be one of: DEBUG, INFO, WARN, ERROR
# Default: INFO
LogLevel = "INFO"
# Storage configuration
[Server.Store]
# How server stores file
# Must be one of: filesystem, memory
# Required
Type = "filesystem"
[Server.Store.Filesystem]
# Where files are stored
# Required if store-type is filesystem
Dir = "/data"
# GRPC Configuration
[Server.GRPC]
# Address to listen to
# Default: :50051
ListenAddr = ":50051"
# GRPC Certificate Configuration
[Server.GRPC.CACerts]
# Path of PEM-encoded certificate file
CertificatePath = ""
# Path of PEM-encoded private key
# Must be of type ecdsa
CertificateKeyPath = ""
[Server.GRPC.Certs]
# Path of PEM-encoded certificate file
CertificatePath = ""
# Path of PEM-encoded private key
# Must be of type ecdsa
CertificateKeyPath = ""
[Server.HTTP]
# Address to listen to
# Default: :8089
ListenAddr = ":8089"
########################
# Client configuration #
########################
[Client]
# Server used if not specified using command-line
DefaultServer = "localhost:50051"
# Path to PEM-encoder server-certificate
ServerCertPath = ""
[Client.Certs]
# Path of PEM-encoded certificate file
CertificatePath = ""
# Path of PEM-encoded private key
# Must be of type ecdsa
CertificateKeyPath = ""

5
go.mod
View File

@ -5,6 +5,7 @@ go 1.17
require (
github.com/go-chi/chi/v5 v5.0.7
github.com/google/uuid v1.3.0
github.com/pelletier/go-toml v1.9.4
github.com/urfave/cli/v2 v2.3.0
google.golang.org/grpc v1.42.0
google.golang.org/protobuf v1.27.1
@ -14,8 +15,8 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c // indirect
golang.org/x/net v0.0.0-20211203184738-4852103109b8 // indirect
golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 // indirect
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9 // indirect
)

10
go.sum
View File

@ -55,6 +55,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
github.com/pelletier/go-toml v1.9.4 h1:tjENF6MfZAg8e4ZmZTeWaWiT2vXtsoO6+iuOjFhECwM=
github.com/pelletier/go-toml v1.9.4/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
@ -83,8 +85,8 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c h1:WtYZ93XtWSO5KlOMgPZu7hXY9WhMZpprvlm5VwvAl8c=
golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211203184738-4852103109b8 h1:PFkPt/jI9Del3hmFplBtRp8tDhSRpFu7CyRs7VmEC0M=
golang.org/x/net v0.0.0-20211203184738-4852103109b8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -122,8 +124,8 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12 h1:DN5b3HU13J4sMd/QjDx34U6afpaexKTDdop+26pdjdk=
google.golang.org/genproto v0.0.0-20211129164237-f09f9a12af12/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9 h1:fU3FNfL/oBU2D5DvGqiuyVqqn40DdxvaTFHq7aivA3k=
google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=

View File

@ -1,3 +1,3 @@
package ezshare
const Version = "v0.1.0"
const Version = "v0.1.1"