From 9e9a7d50970f1424245d88169de82988fd57e112 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Thu, 5 Jan 2017 23:28:26 +0000 Subject: Move GetPublicKey to the shared `lib` package --- client/client.go | 5 +---- cmd/cashierd/main.go | 3 +-- lib/const.go | 17 ----------------- lib/proto.go | 15 +++++++++++++++ lib/util.go | 10 ++++++++++ lib/util_test.go | 16 ++++++++++++++++ server/store/store.go | 4 ++-- server/util/util.go | 10 ---------- server/util/util_test.go | 16 ---------------- 9 files changed, 45 insertions(+), 51 deletions(-) delete mode 100644 lib/const.go create mode 100644 lib/proto.go create mode 100644 lib/util.go create mode 100644 lib/util_test.go delete mode 100644 server/util/util.go delete mode 100644 server/util/util_test.go 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..52b6a8b 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 } diff --git a/lib/const.go b/lib/const.go deleted file mode 100644 index 1ba2749..0000000 --- a/lib/const.go +++ /dev/null @@ -1,17 +0,0 @@ -package lib - -import "time" - -// SignRequest represents a signing request sent to the server. -type SignRequest struct { - Key string `json:"key"` - ValidUntil time.Time `json:"valid_until"` -} - -// 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"` -} diff --git a/lib/proto.go b/lib/proto.go new file mode 100644 index 0000000..f3d7115 --- /dev/null +++ b/lib/proto.go @@ -0,0 +1,15 @@ +package lib + +import "time" + +// SignRequest represents a signing request sent to the server. +type SignRequest struct { + Key string `json:"key"` + ValidUntil time.Time `json:"valid_until"` +} + +// SignResponse is sent by the server. +type SignResponse struct { + 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/lib/util.go b/lib/util.go new file mode 100644 index 0000000..b1c7b87 --- /dev/null +++ b/lib/util.go @@ -0,0 +1,10 @@ +package lib + +import "golang.org/x/crypto/ssh" + +// GetPublicKey marshals a ssh certificate to a string. +func GetPublicKey(pub ssh.PublicKey) string { + marshaled := ssh.MarshalAuthorizedKey(pub) + // Strip trailing newline + return string(marshaled[:len(marshaled)-1]) +} diff --git a/lib/util_test.go b/lib/util_test.go new file mode 100644 index 0000000..9e89297 --- /dev/null +++ b/lib/util_test.go @@ -0,0 +1,16 @@ +package lib + +import ( + "testing" + + "github.com/nsheridan/cashier/testdata" + "golang.org/x/crypto/ssh" +) + +func TestGetPublicKey(t *testing.T) { + t.Parallel() + c, _, _, _, _ := ssh.ParseAuthorizedKey(testdata.Cert) + if GetPublicKey(c.(*ssh.Certificate)) != string(testdata.Cert) { + t.Fail() + } +} 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/util/util.go b/server/util/util.go deleted file mode 100644 index 10f5eca..0000000 --- a/server/util/util.go +++ /dev/null @@ -1,10 +0,0 @@ -package util - -import "golang.org/x/crypto/ssh" - -// GetPublicKey marshals a ssh certificate to a string. -func GetPublicKey(cert *ssh.Certificate) string { - marshaled := ssh.MarshalAuthorizedKey(cert) - // Strip trailing newline - return string(marshaled[:len(marshaled)-1]) -} diff --git a/server/util/util_test.go b/server/util/util_test.go deleted file mode 100644 index d294d86..0000000 --- a/server/util/util_test.go +++ /dev/null @@ -1,16 +0,0 @@ -package util - -import ( - "testing" - - "github.com/nsheridan/cashier/testdata" - "golang.org/x/crypto/ssh" -) - -func TestGetPublicKey(t *testing.T) { - t.Parallel() - c, _, _, _, _ := ssh.ParseAuthorizedKey(testdata.Cert) - if GetPublicKey(c.(*ssh.Certificate)) != string(testdata.Cert) { - t.Fail() - } -} -- cgit v1.2.3 From 9aeb1445549ec9a5b890f6df9bcf2952ef94ee03 Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Fri, 6 Jan 2017 21:00:21 +0000 Subject: Check that tls cert/key are set if use_tls is true --- README.md | 4 ++-- cmd/cashierd/main.go | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a9d68a6..db846d8 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/cmd/cashierd/main.go b/cmd/cashierd/main.go index 52b6a8b..fb67a36 100644 --- a/cmd/cashierd/main.go +++ b/cmd/cashierd/main.go @@ -357,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 { -- cgit v1.2.3 From 6dfe350cb5eefde3a99374d14c083d1906717ddd Mon Sep 17 00:00:00 2001 From: Niall Sheridan Date: Sun, 8 Jan 2017 19:39:51 +0000 Subject: Remove dbinit and use sql/js seed files --- .travis.yml | 5 +- README.md | 5 +- cmd/dbinit/dbinit.go | 126 --------------------------------------------- db/seed.js | 3 ++ db/seed.sql | 13 +++++ server/store/store_test.go | 31 ++++++----- 6 files changed, 40 insertions(+), 143 deletions(-) delete mode 100644 cmd/dbinit/dbinit.go create mode 100644 db/seed.js create mode 100644 db/seed.sql 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: diff --git a/README.md b/README.md index db846d8..b4f356d 100644 --- a/README.md +++ b/README.md @@ -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/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/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 { -- cgit v1.2.3