aboutsummaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorNiall Sheridan <nsheridan@gmail.com>2016-06-19 23:44:25 +0100
committerNiall Sheridan <nsheridan@gmail.com>2016-07-03 18:01:24 +0100
commitdee5a19d36554a8f9a365efd65d13b134889bf63 (patch)
tree41103a2d3665d604fe22dcd16d110ed56c466f6d /cmd
parent6e7dfa0df6b102219817e26095f2ba636cd9288c (diff)
first pass at a certificate store
Diffstat (limited to 'cmd')
-rw-r--r--cmd/cashierd/main.go78
-rw-r--r--cmd/dbinit/dbinit.go61
2 files changed, 127 insertions, 12 deletions
diff --git a/cmd/cashierd/main.go b/cmd/cashierd/main.go
index 8f08122..1db7d30 100644
--- a/cmd/cashierd/main.go
+++ b/cmd/cashierd/main.go
@@ -1,6 +1,7 @@
package main
import (
+ "bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
@@ -24,9 +25,11 @@ import (
"github.com/nsheridan/cashier/server/auth"
"github.com/nsheridan/cashier/server/auth/github"
"github.com/nsheridan/cashier/server/auth/google"
+ "github.com/nsheridan/cashier/server/certutil"
"github.com/nsheridan/cashier/server/config"
"github.com/nsheridan/cashier/server/fs"
"github.com/nsheridan/cashier/server/signer"
+ "github.com/nsheridan/cashier/server/store"
"github.com/nsheridan/cashier/templates"
)
@@ -34,12 +37,13 @@ var (
cfg = flag.String("config_file", "cashierd.conf", "Path to configuration file.")
)
-// appContext contains local context - cookiestore, authprovider, authsession, templates etc.
+// appContext contains local context - cookiestore, authprovider, authsession etc.
type appContext struct {
cookiestore *sessions.CookieStore
authprovider auth.Provider
authsession *auth.Session
sshKeySigner *signer.KeySigner
+ certstore store.CertStorer
}
// getAuthTokenCookie retrieves a cookie from the request.
@@ -124,13 +128,16 @@ func signHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, er
if err != nil {
return http.StatusInternalServerError, err
}
- signed, err := a.sshKeySigner.SignUserKey(req)
+ cert, err := a.sshKeySigner.SignUserKey(req)
if err != nil {
return http.StatusInternalServerError, err
}
+ if err := a.certstore.SetCert(cert); err != nil {
+ log.Printf("Error recording cert: %v", err)
+ }
json.NewEncoder(w).Encode(&lib.SignResponse{
Status: "ok",
- Response: signed,
+ Response: certutil.GetPublicKey(cert),
})
return http.StatusOK, nil
}
@@ -174,6 +181,38 @@ func rootHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, er
return http.StatusOK, nil
}
+func revokedCertsHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, error) {
+ var out bytes.Buffer
+ revoked, err := a.certstore.List()
+ if err != nil {
+ return http.StatusInternalServerError, err
+ }
+ for _, c := range revoked {
+ if c.Revoked {
+ out.WriteString(c.Raw)
+ out.WriteString("\n")
+ }
+ }
+ w.Header().Set("Content-Type", "text/plain; charset=utf-8")
+ w.Write(out.Bytes())
+ return http.StatusOK, nil
+}
+
+func revokeCertHandler(a *appContext, w http.ResponseWriter, r *http.Request) (int, error) {
+ if r.Method == "GET" {
+ return http.StatusMethodNotAllowed, errors.New(http.StatusText(http.StatusMethodNotAllowed))
+ }
+ r.ParseForm()
+ id := r.FormValue("cert_id")
+ if id == "" {
+ return http.StatusBadRequest, errors.New(http.StatusText(http.StatusBadRequest))
+ }
+ if err := a.certstore.Revoke(r.FormValue("cert_id")); err != nil {
+ return http.StatusInternalServerError, err
+ }
+ return http.StatusOK, nil
+}
+
// appHandler is a handler which uses appContext to manage state.
type appHandler struct {
*appContext
@@ -184,14 +223,8 @@ type appHandler struct {
func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
status, err := ah.h(ah.appContext, w, r)
if err != nil {
- switch status {
- case http.StatusNotFound:
- http.NotFound(w, r)
- case http.StatusInternalServerError:
- http.Error(w, http.StatusText(status), status)
- default:
- http.Error(w, http.StatusText(status), status)
- }
+ log.Printf("HTTP %d: %q", status, err)
+ http.Error(w, err.Error(), status)
}
}
@@ -213,6 +246,21 @@ func readConfig(filename string) (*config.Config, error) {
return config.ReadConfig(f)
}
+func certStore(config string) (store.CertStorer, error) {
+ var cstore store.CertStorer
+ var err error
+ engine := strings.Split(config, ":")[0]
+ switch engine {
+ case "mysql":
+ cstore, err = store.NewMySQLStore(config)
+ case "mem":
+ cstore = store.NewMemoryStore()
+ default:
+ cstore = store.NewMemoryStore()
+ }
+ return cstore, err
+}
+
func main() {
flag.Parse()
config, err := readConfig(*cfg)
@@ -234,15 +282,19 @@ func main() {
default:
log.Fatalln("Unknown provider %s", config.Auth.Provider)
}
-
if err != nil {
log.Fatal(err)
}
+ certstore, err := certStore(config.Server.Datastore)
+ if err != nil {
+ log.Fatal(err)
+ }
ctx := &appContext{
cookiestore: sessions.NewCookieStore([]byte(config.Server.CookieSecret)),
authprovider: authprovider,
sshKeySigner: signer,
+ certstore: certstore,
}
ctx.cookiestore.Options = &sessions.Options{
MaxAge: 900,
@@ -256,6 +308,8 @@ func main() {
r.Handle("/auth/login", appHandler{ctx, loginHandler})
r.Handle("/auth/callback", appHandler{ctx, callbackHandler})
r.Handle("/sign", appHandler{ctx, signHandler})
+ r.Handle("/revoked", appHandler{ctx, revokedCertsHandler})
+ r.Handle("/revoke", appHandler{ctx, revokeCertHandler})
logfile := os.Stderr
if config.Server.HTTPLogFile != "" {
logfile, err = os.OpenFile(config.Server.HTTPLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660)
diff --git a/cmd/dbinit/dbinit.go b/cmd/dbinit/dbinit.go
new file mode 100644
index 0000000..4431ced
--- /dev/null
+++ b/cmd/dbinit/dbinit.go
@@ -0,0 +1,61 @@
+package main
+
+import (
+ "database/sql"
+ "flag"
+ "fmt"
+ "log"
+ "strings"
+
+ "github.com/go-sql-driver/mysql"
+)
+
+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", "root", "Database user")
+ dbPasswd = flag.String("db_password", "", "Admin password")
+)
+
+func main() {
+ flag.Parse()
+ var createTableStmt = []string{
+ `CREATE DATABASE IF NOT EXISTS certs DEFAULT CHARACTER SET = 'utf8' DEFAULT COLLATE 'utf8_general_ci';`,
+ `USE certs;`,
+ `CREATE TABLE IF NOT EXISTS issued_certs (
+ key_id VARCHAR(255) NOT NULL,
+ principals VARCHAR(255) NULL,
+ created_at INT(11) NULL,
+ expires_at INT(11) NULL,
+ revoked BOOLEAN DEFAULT NULL,
+ raw_key TEXT NULL,
+ PRIMARY KEY (key_id)
+ );`,
+ `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)
+ }
+ }
+}