diff options
| author | Niall Sheridan <nsheridan@gmail.com> | 2017-01-09 10:07:56 +0000 | 
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-01-09 10:07:56 +0000 | 
| commit | fc190076448ebf1710910a89b584857cbc89d159 (patch) | |
| tree | b82061b8a2af25fbc13f6f9792d42ed7a2ed9d54 | |
| parent | a4b5776500b1250b61c3dafd17e464fdf3f3aae8 (diff) | |
| parent | 6dfe350cb5eefde3a99374d14c083d1906717ddd (diff) | |
Merge branch 'master' into opts2
| -rw-r--r-- | .travis.yml | 5 | ||||
| -rw-r--r-- | README.md | 9 | ||||
| -rw-r--r-- | client/client.go | 5 | ||||
| -rw-r--r-- | cmd/cashierd/main.go | 6 | ||||
| -rw-r--r-- | cmd/dbinit/dbinit.go | 126 | ||||
| -rw-r--r-- | db/seed.js | 3 | ||||
| -rw-r--r-- | db/seed.sql | 13 | ||||
| -rw-r--r-- | lib/proto.go (renamed from lib/const.go) | 6 | ||||
| -rw-r--r-- | lib/util.go (renamed from server/util/util.go) | 6 | ||||
| -rw-r--r-- | lib/util_test.go (renamed from server/util/util_test.go) | 2 | ||||
| -rw-r--r-- | server/store/store.go | 4 | ||||
| -rw-r--r-- | server/store/store_test.go | 31 | 
12 files changed, 55 insertions, 161 deletions
| diff --git a/.travis.yml b/.travis.yml index a377de1..5ca7233 100644 --- a/.travis.yml +++ b/.travis.yml @@ -16,14 +16,13 @@ matrix:  before_install:    - go get -v github.com/golang/lint/golint -  - go install ./cmd/dbinit  install:    - go version  before_script: -  - dbinit -db_user user -db_password passwd -  - dbinit -db_type mongo -admin_user '' -db_user user -db_password passwd +  - mysql < db/seed.sql +  - mongo db/seed.js  sudo: false  script: @@ -100,11 +100,11 @@ For any option that takes a file path as a parameter (e.g. SSH signing key, TLS  - A [Vault](https://www.vaultproject.io) path + key starting with `/vault/` e.g. `/vault/secret/cashier/ssh_signing_key`. You should add a [vault](#vault) config as needed.  ## server -- `use_tls` : boolean. If this is set then `tls_key` and `tls_cert` are required. +- `use_tls` : boolean. If this is set then either `tls_key` and `tls_cert` are required, or `letsencrypt_servername` is required.  - `tls_key` : string. Path to the TLS key. See the [note](#a-note-on-files) on files above.  - `tls_cert` : string. Path to the TLS cert. See the [note](#a-note-on-files) on files above.  - `letsencrypt_servername`: string. If set will request a certificate from LetsEncrypt. This should match the expected FQDN of the server. -- `letsencrypt_cachedir: string. Directory to cache the LetsEncrypt certificate. +- `letsencrypt_cachedir`: string. Directory to cache the LetsEncrypt certificate.  - `address` : string. IP address to listen on. If unset the server listens on all addresses.  - `port` : int. Port to listen on.  - `user` : string. User to which the server drops privileges to. @@ -151,8 +151,9 @@ server {  }  ``` -Prior to using MySQL, MongoDB or SQLite you need to create the database and tables using the [dbinit tool](cmd/dbinit/dbinit.go).   -dbinit hasn't been tested with mongo replica sets. +Prior to using MySQL, MongoDB or SQLite you need to create the database and tables using [one of the provided files](db).   +e.g. `mysql < db/seed.sql` or `mongo db/seed.js`.   +Obviously you should setup a role user for running in prodution.  ### datastore diff --git a/client/client.go b/client/client.go index e69f353..b13c4cb 100644 --- a/client/client.go +++ b/client/client.go @@ -79,11 +79,8 @@ func Sign(pub ssh.PublicKey, token string, conf *Config) (*ssh.Certificate, erro  	if err != nil {  		return nil, err  	} -	marshaled := ssh.MarshalAuthorizedKey(pub) -	// Remove the trailing newline. -	marshaled = marshaled[:len(marshaled)-1]  	s, err := json.Marshal(&lib.SignRequest{ -		Key:        string(marshaled), +		Key:        lib.GetPublicKey(pub),  		ValidUntil: time.Now().Add(validity),  	})  	if err != nil { diff --git a/cmd/cashierd/main.go b/cmd/cashierd/main.go index 31ee240..fb67a36 100644 --- a/cmd/cashierd/main.go +++ b/cmd/cashierd/main.go @@ -34,7 +34,6 @@ import (  	"github.com/nsheridan/cashier/server/static"  	"github.com/nsheridan/cashier/server/store"  	"github.com/nsheridan/cashier/server/templates" -	"github.com/nsheridan/cashier/server/util"  	"github.com/nsheridan/cashier/server/wkfs/vaultfs"  	"github.com/nsheridan/wkfs/s3"  	"github.com/sid77/drop" @@ -169,7 +168,7 @@ func signHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, er  	}  	json.NewEncoder(w).Encode(&lib.SignResponse{  		Status:   "ok", -		Response: util.GetPublicKey(cert), +		Response: lib.GetPublicKey(cert),  	})  	return http.StatusOK, nil  } @@ -358,6 +357,9 @@ func main() {  			}  			tlsConfig.GetCertificate = m.GetCertificate  		} else { +			if conf.Server.TLSCert == "" || conf.Server.TLSKey == "" { +				log.Fatal("TLS cert or key not specified in config") +			}  			tlsConfig.Certificates = make([]tls.Certificate, 1)  			tlsConfig.Certificates[0], err = loadCerts(conf.Server.TLSCert, conf.Server.TLSKey)  			if err != nil { diff --git a/cmd/dbinit/dbinit.go b/cmd/dbinit/dbinit.go deleted file mode 100644 index 4cf0834..0000000 --- a/cmd/dbinit/dbinit.go +++ /dev/null @@ -1,126 +0,0 @@ -package main - -import ( -	"database/sql" -	"flag" -	"fmt" -	"log" -	"strings" - -	mgo "gopkg.in/mgo.v2" - -	"github.com/go-sql-driver/mysql" -	_ "github.com/mattn/go-sqlite3" -) - -var ( -	host        = flag.String("host", "localhost", "host[:port]") -	adminUser   = flag.String("admin_user", "root", "Admin user") -	adminPasswd = flag.String("admin_password", "", "Admin password") -	dbUser      = flag.String("db_user", "user", "Database user") -	dbPasswd    = flag.String("db_password", "passwd", "Admin password") -	dbType      = flag.String("db_type", "mysql", "Database engine (\"mysql\", \"sqlite\" or \"mongo\")") -	sqliteDB    = flag.String("db_path", "certs.db", "Path to SQLite database") -	authDB      = flag.String("authdb", "admin", "Admin database (mongo)") - -	certsDB     = "certs" -	issuedTable = "issued_certs" -	createTable = `CREATE TABLE IF NOT EXISTS ` + issuedTable + ` ( -			key_id VARCHAR(255) NOT NULL, -			principals VARCHAR(255) NULL, -			created_at DATETIME NULL, -			expires_at DATETIME NULL, -			revoked BOOLEAN DEFAULT NULL, -			raw_key TEXT NULL, -			PRIMARY KEY (key_id) -		);` -) - -func initSQLite() { -	db, err := sql.Open("sqlite3", *sqliteDB) -	if err != nil { -		log.Fatal(err) -	} -	defer db.Close() - -	if _, err = db.Exec(createTable); err != nil { -		log.Fatal(err) -	} -} - -func initMySQL() { -	var createTableStmt = []string{ -		`CREATE DATABASE IF NOT EXISTS ` + certsDB + ` DEFAULT CHARACTER SET = 'utf8' DEFAULT COLLATE 'utf8_general_ci';`, -		`USE ` + certsDB + `;`, -		createTable, -		`GRANT ALL PRIVILEGES ON certs.* TO '` + *dbUser + `'@'%' IDENTIFIED BY '` + *dbPasswd + `';`, -	} - -	if len(strings.Split(*host, ":")) != 2 { -		*host = fmt.Sprintf("%s:3306", *host) -	} -	conn := &mysql.Config{ -		User:   *adminUser, -		Passwd: *adminPasswd, -		Net:    "tcp", -		Addr:   *host, -	} -	db, err := sql.Open("mysql", conn.FormatDSN()) -	if err != nil { -		log.Fatalf("Error connecting to database: %v", err) -	} -	defer db.Close() -	if err := db.Ping(); err != nil { -		log.Fatalf("Unable to connect to database.") -	} -	for _, stmt := range createTableStmt { -		_, err := db.Exec(stmt) -		if err != nil { -			log.Fatalf("Error running setup: %v", err) -		} -	} -} - -func initMongo() { -	di := &mgo.DialInfo{ -		Addrs:    strings.Split(*host, ","), -		Username: *adminUser, -		Password: *adminPasswd, -		Database: *authDB, -	} -	session, err := mgo.DialWithInfo(di) -	if err != nil { -		log.Fatalln(err) -	} -	defer session.Close() -	d := session.DB(certsDB) -	if err := d.UpsertUser(&mgo.User{ -		Username: *dbUser, -		Password: *dbPasswd, -		Roles:    []mgo.Role{mgo.RoleReadWrite}, -	}); err != nil { -		log.Fatalln(err) -	} -	c := d.C(issuedTable) -	i := mgo.Index{ -		Key:    []string{"keyid"}, -		Unique: true, -	} -	if err != c.EnsureIndex(i) { -		log.Fatalln(err) -	} -} - -func main() { -	flag.Parse() -	switch *dbType { -	case "mysql": -		initMySQL() -	case "mongo": -		initMongo() -	case "sqlite": -		initSQLite() -	default: -		log.Fatalf("Invalid database type") -	} -} diff --git a/db/seed.js b/db/seed.js new file mode 100644 index 0000000..c9d62fa --- /dev/null +++ b/db/seed.js @@ -0,0 +1,3 @@ +conn = new Mongo(); +db = conn.getDB("certs"); +db.issued_certs.createIndex({"keyid": 1}, {unique: true}); diff --git a/db/seed.sql b/db/seed.sql new file mode 100644 index 0000000..cf5e62a --- /dev/null +++ b/db/seed.sql @@ -0,0 +1,13 @@ +CREATE DATABASE IF NOT EXISTS `certs`; + +USE `certs`; + +CREATE TABLE `issued_certs` ( +  `key_id` varchar(255) NOT NULL, +  `principals` varchar(255) DEFAULT NULL, +  `created_at` datetime DEFAULT NULL, +  `expires_at` datetime DEFAULT NULL, +  `revoked` tinyint(1) DEFAULT NULL, +  `raw_key` text, +  PRIMARY KEY (`key_id`) +); diff --git a/lib/const.go b/lib/proto.go index 1ba2749..f3d7115 100644 --- a/lib/const.go +++ b/lib/proto.go @@ -9,9 +9,7 @@ type SignRequest struct {  }  // SignResponse is sent by the server. -// `Status' is "ok" or "error". -// `Response' contains a signed certificate or an error message.  type SignResponse struct { -	Status   string `json:"status"` -	Response string `json:"response"` +	Status   string `json:"status"`   // Status will be "ok" or "error". +	Response string `json:"response"` // Response will contain either the signed certificate or the error message.  } diff --git a/server/util/util.go b/lib/util.go index 10f5eca..b1c7b87 100644 --- a/server/util/util.go +++ b/lib/util.go @@ -1,10 +1,10 @@ -package util +package lib  import "golang.org/x/crypto/ssh"  // GetPublicKey marshals a ssh certificate to a string. -func GetPublicKey(cert *ssh.Certificate) string { -	marshaled := ssh.MarshalAuthorizedKey(cert) +func GetPublicKey(pub ssh.PublicKey) string { +	marshaled := ssh.MarshalAuthorizedKey(pub)  	// Strip trailing newline  	return string(marshaled[:len(marshaled)-1])  } diff --git a/server/util/util_test.go b/lib/util_test.go index d294d86..9e89297 100644 --- a/server/util/util_test.go +++ b/lib/util_test.go @@ -1,4 +1,4 @@ -package util +package lib  import (  	"testing" diff --git a/server/store/store.go b/server/store/store.go index a447e72..8af77e3 100644 --- a/server/store/store.go +++ b/server/store/store.go @@ -5,8 +5,8 @@ import (  	"golang.org/x/crypto/ssh" +	"github.com/nsheridan/cashier/lib"  	"github.com/nsheridan/cashier/server/config" -	"github.com/nsheridan/cashier/server/util"  )  // New returns a new configured database. @@ -54,6 +54,6 @@ func parseCertificate(cert *ssh.Certificate) *CertRecord {  		Principals: cert.ValidPrincipals,  		CreatedAt:  parseTime(cert.ValidAfter),  		Expires:    parseTime(cert.ValidBefore), -		Raw:        util.GetPublicKey(cert), +		Raw:        lib.GetPublicKey(cert),  	}  } diff --git a/server/store/store_test.go b/server/store/store_test.go index dbe2d95..281a614 100644 --- a/server/store/store_test.go +++ b/server/store/store_test.go @@ -3,9 +3,11 @@ package store  import (  	"crypto/rand"  	"crypto/rsa" +	"database/sql"  	"io/ioutil"  	"os" -	"os/exec" +	"os/user" +	"strings"  	"testing"  	"time" @@ -15,10 +17,6 @@ import (  	"golang.org/x/crypto/ssh"  ) -var ( -	dbConfig = map[string]string{"username": "user", "password": "passwd", "address": "localhost"} -) -  func TestParseCertificate(t *testing.T) {  	t.Parallel()  	a := assert.New(t) @@ -93,8 +91,8 @@ func TestMySQLStore(t *testing.T) {  	if os.Getenv("MYSQL_TEST") == "" {  		t.Skip("No MYSQL_TEST environment variable")  	} -	dbConfig["type"] = "mysql" -	db, err := NewSQLStore(dbConfig) +	u, _ := user.Current() +	db, err := NewSQLStore(map[string]string{"type": "mysql", "username": u.Username})  	if err != nil {  		t.Error(err)  	} @@ -106,8 +104,7 @@ func TestMongoStore(t *testing.T) {  	if os.Getenv("MONGO_TEST") == "" {  		t.Skip("No MONGO_TEST environment variable")  	} -	dbConfig["type"] = "mongo" -	db, err := NewMongoStore(dbConfig) +	db, err := NewMongoStore(map[string]string{"type": "mongo"})  	if err != nil {  		t.Error(err)  	} @@ -121,11 +118,21 @@ func TestSQLiteStore(t *testing.T) {  		t.Error(err)  	}  	defer os.Remove(f.Name()) -	// This is so jank. -	args := []string{"run", "../../cmd/dbinit/dbinit.go", "-db_type", "sqlite", "-db_path", f.Name()} -	if err := exec.Command("go", args...).Run(); err != nil { + +	seed, err := ioutil.ReadFile("../../db/seed.sql") +	if err != nil {  		t.Error(err)  	} +	stmts := strings.Split(string(seed), ";") +	d, _ := sql.Open("sqlite3", f.Name()) +	for _, stmt := range stmts { +		if !strings.Contains(stmt, "CREATE TABLE") { +			continue +		} +		d.Exec(stmt) +	} +	d.Close() +  	config := map[string]string{"type": "sqlite", "filename": f.Name()}  	db, err := NewSQLStore(config)  	if err != nil { | 
