From 472958ea03523307726dc5ae28b83805895ef77e Mon Sep 17 00:00:00 2001 From: Arjan H Date: Sun, 18 Nov 2018 10:28:29 +0100 Subject: [PATCH 1/3] gofmt the .go files #2 --- gui/acme.go | 1141 +++++++------- gui/certificate.go | 796 +++++----- gui/dashboard.go | 567 ++++--- gui/main.go | 3655 ++++++++++++++++++++++---------------------- 4 files changed, 3078 insertions(+), 3081 deletions(-) diff --git a/gui/acme.go b/gui/acme.go index eee6ac9..3bacc2d 100644 --- a/gui/acme.go +++ b/gui/acme.go @@ -1,733 +1,732 @@ package main import ( - "database/sql" - "html/template" - "net" - "net/http" - "strconv" + "database/sql" + "html/template" + "net" + "net/http" + "strconv" ) type BaseList struct { - Title string - TableClass string - Header []template.HTML + Title string + TableClass string + Header []template.HTML } type Account struct { - Id int - Status string - Contact string - Agreement string - InitialIp net.IP - CreatedAt string + Id int + Status string + Contact string + Agreement string + InitialIp net.IP + CreatedAt string } type AccountList struct { - BaseList - Rows []Account + BaseList + Rows []Account } -func GetAccounts(w http.ResponseWriter, r *http.Request) (AccountList, error){ - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountList{}, err - } +func GetAccounts(w http.ResponseWriter, r *http.Request) (AccountList, error) { + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountList{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT id, status, contact, agreement, initialIP, createdAt FROM registrations") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountList{}, err - } + rows, err := db.Query("SELECT id, status, contact, agreement, initialIP, createdAt FROM registrations") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountList{}, err + } - Accounts := AccountList{ - BaseList: BaseList{ - Title: "Accounts", - TableClass: "accounts_list", - Header: []template.HTML{"ID", "Status", "Contact", "Agreement", "Initial IP", "Created"}, - }, - Rows: []Account{}, - } + Accounts := AccountList{ + BaseList: BaseList{ + Title: "Accounts", + TableClass: "accounts_list", + Header: []template.HTML{"ID", "Status", "Contact", "Agreement", "Initial IP", "Created"}, + }, + Rows: []Account{}, + } - for rows.Next() { - row := Account{} - err = rows.Scan(&row.Id, &row.Status, &row.Contact, &row.Agreement, &row.InitialIp, &row.CreatedAt) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountList{}, err - } - Accounts.Rows = append(Accounts.Rows, row) - } + for rows.Next() { + row := Account{} + err = rows.Scan(&row.Id, &row.Status, &row.Contact, &row.Agreement, &row.InitialIp, &row.CreatedAt) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountList{}, err + } + Accounts.Rows = append(Accounts.Rows, row) + } - return Accounts, nil + return Accounts, nil } type Order struct { - Id int - RegistrationId int - Expires string - CertSerial string - BeganProc bool - Created string + Id int + RegistrationId int + Expires string + CertSerial string + BeganProc bool + Created string } type OrderList struct { - BaseList - Rows []Order + BaseList + Rows []Order } type NameValue struct { - Name string - Value string + Name string + Value string } type BaseShow struct { - Title string - TableClass string - Rows []NameValue - Links []NameValHtml - Extra []template.HTML + Title string + TableClass string + Rows []NameValue + Links []NameValHtml + Extra []template.HTML } type AccountShow struct { - BaseShow - Related []CertificateList - Related2 []OrderList + BaseShow + Related []CertificateList + Related2 []OrderList } func GetAccount(w http.ResponseWriter, r *http.Request, id int) (AccountShow, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id WHERE registrationID=?", strconv.Itoa(id)) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } + rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id WHERE registrationID=?", strconv.Itoa(id)) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } - Certificates := CertificateList{ - BaseList: BaseList{ - Title: "Certificates", - TableClass: "rel_certificates_list", - Header: []template.HTML{"ID", "Account ID", "Serial", "Status", "Issued", "Expires"}, - }, - Rows: []Certificate{}, - } + Certificates := CertificateList{ + BaseList: BaseList{ + Title: "Certificates", + TableClass: "rel_certificates_list", + Header: []template.HTML{"ID", "Account ID", "Serial", "Status", "Issued", "Expires"}, + }, + Rows: []Certificate{}, + } - for rows.Next() { - row := Certificate{} - err = rows.Scan(&row.Id, &row.RegistrationId, &row.Serial, &row.Status, &row.Issued, &row.Expires) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } - Certificates.Rows = append(Certificates.Rows, row) - } + for rows.Next() { + row := Certificate{} + err = rows.Scan(&row.Id, &row.RegistrationId, &row.Serial, &row.Status, &row.Issued, &row.Expires) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } + Certificates.Rows = append(Certificates.Rows, row) + } - rows, err = db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders WHERE registrationID=?", strconv.Itoa(id)) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } + rows, err = db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders WHERE registrationID=?", strconv.Itoa(id)) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } - Orders := OrderList{ - BaseList: BaseList{ - Title: "Orders", - TableClass: "rel_orders_list", - Header: []template.HTML{"ID", "Account ID", "Expires", "Certificate Serial", "Began Processing?", "Created"}, - }, - Rows: []Order{}, - } + Orders := OrderList{ + BaseList: BaseList{ + Title: "Orders", + TableClass: "rel_orders_list", + Header: []template.HTML{"ID", "Account ID", "Expires", "Certificate Serial", "Began Processing?", "Created"}, + }, + Rows: []Order{}, + } - for rows.Next() { - row := Order{} - err = rows.Scan(&row.Id, &row.RegistrationId, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } - Orders.Rows = append(Orders.Rows, row) - } + for rows.Next() { + row := Order{} + err = rows.Scan(&row.Id, &row.RegistrationId, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } + Orders.Rows = append(Orders.Rows, row) + } - rows, err = db.Query("SELECT id, status, contact, agreement, initialIP, createdAt FROM registrations WHERE id=?", strconv.Itoa(id)) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } + rows, err = db.Query("SELECT id, status, contact, agreement, initialIP, createdAt FROM registrations WHERE id=?", strconv.Itoa(id)) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } - AccountDetails := AccountShow{ - BaseShow: BaseShow{ - Title: "Account", - TableClass: "account_show", - Rows: []NameValue{}, - }, - Related: []CertificateList{Certificates}, - Related2: []OrderList{Orders}, - } + AccountDetails := AccountShow{ + BaseShow: BaseShow{ + Title: "Account", + TableClass: "account_show", + Rows: []NameValue{}, + }, + Related: []CertificateList{Certificates}, + Related2: []OrderList{Orders}, + } - for rows.Next() { - row := Account{} - err = rows.Scan(&row.Id, &row.Status, &row.Contact, &row.Agreement, &row.InitialIp, &row.CreatedAt) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AccountShow{}, err - } - AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) - AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Status", row.Status}) - AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Contact", row.Contact}) - AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Agreement", row.Agreement}) - AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Initial IP", row.InitialIp.String()}) - AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Created At", row.CreatedAt}) - } + for rows.Next() { + row := Account{} + err = rows.Scan(&row.Id, &row.Status, &row.Contact, &row.Agreement, &row.InitialIp, &row.CreatedAt) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AccountShow{}, err + } + AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) + AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Status", row.Status}) + AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Contact", row.Contact}) + AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Agreement", row.Agreement}) + AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Initial IP", row.InitialIp.String()}) + AccountDetails.Rows = append(AccountDetails.Rows, NameValue{"Created At", row.CreatedAt}) + } - return AccountDetails, nil + return AccountDetails, nil } func GetOrders(w http.ResponseWriter, r *http.Request) (OrderList, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderList{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderList{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderList{}, err - } + rows, err := db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderList{}, err + } - Orders := OrderList{ - BaseList: BaseList{ - Title: "Orders", - TableClass: "orders_list", - Header: []template.HTML{"ID", "Account ID", "Expires", "Certificate Serial", "Began Processing?", "Created"}, - }, - Rows: []Order{}, - } + Orders := OrderList{ + BaseList: BaseList{ + Title: "Orders", + TableClass: "orders_list", + Header: []template.HTML{"ID", "Account ID", "Expires", "Certificate Serial", "Began Processing?", "Created"}, + }, + Rows: []Order{}, + } - for rows.Next() { - row := Order{} - err = rows.Scan(&row.Id, &row.RegistrationId, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderList{}, err - } - Orders.Rows = append(Orders.Rows, row) - } + for rows.Next() { + row := Order{} + err = rows.Scan(&row.Id, &row.RegistrationId, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderList{}, err + } + Orders.Rows = append(Orders.Rows, row) + } - return Orders, nil + return Orders, nil } type Auth struct { - Id string - Identifier string - RegistrationId int - Status string - Expires string - Combinations string + Id string + Identifier string + RegistrationId int + Status string + Expires string + Combinations string } type AuthList struct { - BaseList - Rows []Auth + BaseList + Rows []Auth } type OrderShow struct { - BaseShow - Related []AuthList - Related2 []BaseList + BaseShow + Related []AuthList + Related2 []BaseList } func GetOrder(w http.ResponseWriter, r *http.Request, id int) (OrderShow, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderShow{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderShow{}, err + } - defer db.Close() + defer db.Close() - partial := "SELECT id, identifier, registrationID, status, expires, combinations FROM " - where := " WHERE id IN (SELECT authzID FROM orderToAuthz WHERE orderID=?)" - rows, err := db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where, strconv.Itoa(id), strconv.Itoa(id)) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderShow{}, err - } + partial := "SELECT id, identifier, registrationID, status, expires, combinations FROM " + where := " WHERE id IN (SELECT authzID FROM orderToAuthz WHERE orderID=?)" + rows, err := db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where, strconv.Itoa(id), strconv.Itoa(id)) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderShow{}, err + } - Authz := AuthList{ - BaseList: BaseList{ - Title: "Authorizations", - TableClass: "rel_authz_list", - Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires", "Combinations"}, - }, - Rows: []Auth{}, - } + Authz := AuthList{ + BaseList: BaseList{ + Title: "Authorizations", + TableClass: "rel_authz_list", + Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires", "Combinations"}, + }, + Rows: []Auth{}, + } - for rows.Next() { - row := Auth{} - err = rows.Scan(&row.Id, &row.Identifier, &row.RegistrationId, &row.Status, &row.Expires, &row.Combinations) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderShow{}, err - } - Authz.Rows = append(Authz.Rows, row) - } + for rows.Next() { + row := Auth{} + err = rows.Scan(&row.Id, &row.Identifier, &row.RegistrationId, &row.Status, &row.Expires, &row.Combinations) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderShow{}, err + } + Authz.Rows = append(Authz.Rows, row) + } - rows, err = db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders WHERE id=?", strconv.Itoa(id)) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderShow{}, err - } + rows, err = db.Query("SELECT id, registrationID, expires, certificateSerial, beganProcessing, created FROM orders WHERE id=?", strconv.Itoa(id)) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderShow{}, err + } - OrderDetails := OrderShow{ - BaseShow: BaseShow{ - Title: "Order", - TableClass: "order_show", - Rows: []NameValue{}, - Links: []NameValHtml{}, - }, - Related: []AuthList{Authz}, - } + OrderDetails := OrderShow{ + BaseShow: BaseShow{ + Title: "Order", + TableClass: "order_show", + Rows: []NameValue{}, + Links: []NameValHtml{}, + }, + Related: []AuthList{Authz}, + } - for rows.Next() { - row := Order{} - err = rows.Scan(&row.Id, &row.RegistrationId, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return OrderShow{}, err - } - OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) - OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Expires", row.Expires}) - v := "false" - if row.BeganProc { - v = "true" - } - OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Began Processing?", v}) - OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Created", row.Created}) + for rows.Next() { + row := Order{} + err = rows.Scan(&row.Id, &row.RegistrationId, &row.Expires, &row.CertSerial, &row.BeganProc, &row.Created) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return OrderShow{}, err + } + OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) + OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Expires", row.Expires}) + v := "false" + if row.BeganProc { + v = "true" + } + OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Began Processing?", v}) + OrderDetails.Rows = append(OrderDetails.Rows, NameValue{"Created", row.Created}) - OrderDetails.Links = append(OrderDetails.Links, NameValHtml{"Certificate", template.HTML("" + row.CertSerial + "")}) - OrderDetails.Links = append(OrderDetails.Links, NameValHtml{"Account", template.HTML("" + strconv.Itoa(row.RegistrationId) + "")}) - } + OrderDetails.Links = append(OrderDetails.Links, NameValHtml{"Certificate", template.HTML("" + row.CertSerial + "")}) + OrderDetails.Links = append(OrderDetails.Links, NameValHtml{"Account", template.HTML("" + strconv.Itoa(row.RegistrationId) + "")}) + } - return OrderDetails, nil + return OrderDetails, nil } func GetAuthz(w http.ResponseWriter, r *http.Request) (AuthList, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthList{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthList{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT id, identifier, registrationID, status, expires, combinations FROM authz UNION SELECT id, identifier, registrationID, status, expires, combinations FROM pendingAuthorizations") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthList{}, err - } + rows, err := db.Query("SELECT id, identifier, registrationID, status, expires, combinations FROM authz UNION SELECT id, identifier, registrationID, status, expires, combinations FROM pendingAuthorizations") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthList{}, err + } - Authz := AuthList{ - BaseList: BaseList{ - Title: "Authorizations", - TableClass: "authz_list", - Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires", "Combinations"}, - }, - Rows: []Auth{}, - } + Authz := AuthList{ + BaseList: BaseList{ + Title: "Authorizations", + TableClass: "authz_list", + Header: []template.HTML{"ID", "Identifier", "Account ID", "Status", "Expires", "Combinations"}, + }, + Rows: []Auth{}, + } - for rows.Next() { - row := Auth{} - err = rows.Scan(&row.Id, &row.Identifier, &row.RegistrationId, &row.Status, &row.Expires, &row.Combinations) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthList{}, err - } - Authz.Rows = append(Authz.Rows, row) - } + for rows.Next() { + row := Auth{} + err = rows.Scan(&row.Id, &row.Identifier, &row.RegistrationId, &row.Status, &row.Expires, &row.Combinations) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthList{}, err + } + Authz.Rows = append(Authz.Rows, row) + } - return Authz, nil + return Authz, nil } type Challenge struct { - Id int - AuthId string - Type string - Status string - Validated string - Token string - KeyAuth string + Id int + AuthId string + Type string + Status string + Validated string + Token string + KeyAuth string } type ChallengeList struct { - BaseList - Rows []Challenge + BaseList + Rows []Challenge } type NameValHtml struct { - Name string - Value template.HTML + Name string + Value template.HTML } type AuthShow struct { - BaseShow - Related []ChallengeList - Related2 []BaseList + BaseShow + Related []ChallengeList + Related2 []BaseList } func GetAuth(w http.ResponseWriter, r *http.Request, id string) (AuthShow, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthShow{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthShow{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT id, authorizationID, type, status, validated, token, keyAuthorization FROM challenges WHERE authorizationID=?", id) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthShow{}, err - } + rows, err := db.Query("SELECT id, authorizationID, type, status, validated, token, keyAuthorization FROM challenges WHERE authorizationID=?", id) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthShow{}, err + } - Challenges := ChallengeList{ - BaseList: BaseList{ - Title: "Challenges", - TableClass: "rel_challenges_list", - Header: []template.HTML{"ID", "Authorization ID", "Type", "Status", "Validated", "Token", "Key Authorization"}, - }, - Rows: []Challenge{}, - } + Challenges := ChallengeList{ + BaseList: BaseList{ + Title: "Challenges", + TableClass: "rel_challenges_list", + Header: []template.HTML{"ID", "Authorization ID", "Type", "Status", "Validated", "Token", "Key Authorization"}, + }, + Rows: []Challenge{}, + } - for rows.Next() { - row := Challenge{} - err = rows.Scan(&row.Id, &row.AuthId, &row.Type, &row.Status, &row.Validated, &row.Token, &row.KeyAuth) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthShow{}, err - } - Challenges.Rows = append(Challenges.Rows, row) - } + for rows.Next() { + row := Challenge{} + err = rows.Scan(&row.Id, &row.AuthId, &row.Type, &row.Status, &row.Validated, &row.Token, &row.KeyAuth) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthShow{}, err + } + Challenges.Rows = append(Challenges.Rows, row) + } - partial := "SELECT id, identifier, registrationID, status, expires, combinations FROM " - where := " WHERE id IN (SELECT authzID FROM orderToAuthz WHERE id=?)" - rows, err = db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where, id, id) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthShow{}, err - } + partial := "SELECT id, identifier, registrationID, status, expires, combinations FROM " + where := " WHERE id IN (SELECT authzID FROM orderToAuthz WHERE id=?)" + rows, err = db.Query(partial+"authz"+where+" UNION "+partial+"pendingAuthorizations"+where, id, id) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthShow{}, err + } - AuthDetails := AuthShow{ - BaseShow: BaseShow{ - Title: "Authorization", - TableClass: "auth_show", - Rows: []NameValue{}, - Links: []NameValHtml{}, - }, - Related: []ChallengeList{Challenges}, - } + AuthDetails := AuthShow{ + BaseShow: BaseShow{ + Title: "Authorization", + TableClass: "auth_show", + Rows: []NameValue{}, + Links: []NameValHtml{}, + }, + Related: []ChallengeList{Challenges}, + } - for rows.Next() { - row := Auth{} - err = rows.Scan(&row.Id, &row.Identifier, &row.RegistrationId, &row.Status, &row.Expires, &row.Combinations) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return AuthShow{}, err - } - AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"ID", row.Id}) - AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Identifier", row.Identifier}) - AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Status", row.Status}) - AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Expires", row.Expires}) - AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Combinations", row.Combinations}) + for rows.Next() { + row := Auth{} + err = rows.Scan(&row.Id, &row.Identifier, &row.RegistrationId, &row.Status, &row.Expires, &row.Combinations) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return AuthShow{}, err + } + AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"ID", row.Id}) + AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Identifier", row.Identifier}) + AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Status", row.Status}) + AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Expires", row.Expires}) + AuthDetails.Rows = append(AuthDetails.Rows, NameValue{"Combinations", row.Combinations}) - Link := NameValHtml{"Account", template.HTML("" + strconv.Itoa(row.RegistrationId) + "")} - AuthDetails.Links = append(AuthDetails.Links, Link) - } + Link := NameValHtml{"Account", template.HTML("" + strconv.Itoa(row.RegistrationId) + "")} + AuthDetails.Links = append(AuthDetails.Links, Link) + } - return AuthDetails, nil + return AuthDetails, nil } func GetChallenges(w http.ResponseWriter, r *http.Request) (ChallengeList, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return ChallengeList{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return ChallengeList{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT id, authorizationID, type, status, validated, token, keyAuthorization FROM challenges") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return ChallengeList{}, err - } + rows, err := db.Query("SELECT id, authorizationID, type, status, validated, token, keyAuthorization FROM challenges") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return ChallengeList{}, err + } - Challenges := ChallengeList{ - BaseList: BaseList{ - Title: "Challenges", - TableClass: "challenges_list", - Header: []template.HTML{"ID", "Authorization ID", "Type", "Status", "Validated", "Token", "Key Authorization"}, - }, - Rows: []Challenge{}, - } + Challenges := ChallengeList{ + BaseList: BaseList{ + Title: "Challenges", + TableClass: "challenges_list", + Header: []template.HTML{"ID", "Authorization ID", "Type", "Status", "Validated", "Token", "Key Authorization"}, + }, + Rows: []Challenge{}, + } - for rows.Next() { - row := Challenge{} - err = rows.Scan(&row.Id, &row.AuthId, &row.Type, &row.Status, &row.Validated, &row.Token, &row.KeyAuth) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return ChallengeList{}, err - } - Challenges.Rows = append(Challenges.Rows, row) - } + for rows.Next() { + row := Challenge{} + err = rows.Scan(&row.Id, &row.AuthId, &row.Type, &row.Status, &row.Validated, &row.Token, &row.KeyAuth) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return ChallengeList{}, err + } + Challenges.Rows = append(Challenges.Rows, row) + } - return Challenges, nil + return Challenges, nil } type ChallengeShow struct { - BaseShow - Related []ChallengeList - Related2 []BaseList + BaseShow + Related []ChallengeList + Related2 []BaseList } func GetChallenge(w http.ResponseWriter, r *http.Request, id int) (ChallengeShow, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return ChallengeShow{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return ChallengeShow{}, err + } - defer db.Close() + defer db.Close() - rows, err := db.Query("SELECT id, authorizationID, type, status, validated, token, keyAuthorization FROM challenges WHERE id=?", id) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return ChallengeShow{}, err - } + rows, err := db.Query("SELECT id, authorizationID, type, status, validated, token, keyAuthorization FROM challenges WHERE id=?", id) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return ChallengeShow{}, err + } - ChallengeDetails := ChallengeShow{ - BaseShow: BaseShow{ - Title: "Challenge", - TableClass: "challenge_show", - Rows: []NameValue{}, - Links: []NameValHtml{}, - }, - Related: []ChallengeList{}, - } + ChallengeDetails := ChallengeShow{ + BaseShow: BaseShow{ + Title: "Challenge", + TableClass: "challenge_show", + Rows: []NameValue{}, + Links: []NameValHtml{}, + }, + Related: []ChallengeList{}, + } - for rows.Next() { - row := Challenge{} - err = rows.Scan(&row.Id, &row.AuthId, &row.Type, &row.Status, &row.Validated, &row.Token, &row.KeyAuth) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return ChallengeShow{}, err - } - ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) - ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Type", row.Type}) - ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Status", row.Status}) - ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Validated", row.Validated}) - ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Token", row.Token}) - ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"KeyAuth", row.KeyAuth}) + for rows.Next() { + row := Challenge{} + err = rows.Scan(&row.Id, &row.AuthId, &row.Type, &row.Status, &row.Validated, &row.Token, &row.KeyAuth) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return ChallengeShow{}, err + } + ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) + ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Type", row.Type}) + ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Status", row.Status}) + ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Validated", row.Validated}) + ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"Token", row.Token}) + ChallengeDetails.Rows = append(ChallengeDetails.Rows, NameValue{"KeyAuth", row.KeyAuth}) - Link := NameValHtml{"Authorization", template.HTML("" + row.AuthId + "")} - ChallengeDetails.Links = append(ChallengeDetails.Links, Link) - } + Link := NameValHtml{"Authorization", template.HTML("" + row.AuthId + "")} + ChallengeDetails.Links = append(ChallengeDetails.Links, Link) + } - return ChallengeDetails, nil + return ChallengeDetails, nil } type Certificate struct { - Id int - RegistrationId int - Serial string - Status string - Issued string - Expires string + Id int + RegistrationId int + Serial string + Status string + Issued string + Expires string } type CertificateList struct { - BaseList - Rows []Certificate + BaseList + Rows []Certificate } func GetCertificates(w http.ResponseWriter, r *http.Request) (CertificateList, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateList{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateList{}, err + } - defer db.Close() + defer db.Close() - where := "" - if r.URL.Query().Get("active") != "" { - where = " WHERE cs.revokedDate='0000-00-00 00:00:00' AND cs.notAfter >= NOW()"; - } else if r.URL.Query().Get("expired") != "" { - where = " WHERE cs.revokedDate='0000-00-00 00:00:00' AND cs.notAfter < NOW()"; - } else if r.URL.Query().Get("revoked") != "" { - where = " WHERE cs.revokedDate<>'0000-00-00 00:00:00'"; - } + where := "" + if r.URL.Query().Get("active") != "" { + where = " WHERE cs.revokedDate='0000-00-00 00:00:00' AND cs.notAfter >= NOW()" + } else if r.URL.Query().Get("expired") != "" { + where = " WHERE cs.revokedDate='0000-00-00 00:00:00' AND cs.notAfter < NOW()" + } else if r.URL.Query().Get("revoked") != "" { + where = " WHERE cs.revokedDate<>'0000-00-00 00:00:00'" + } - rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id" + where) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateList{}, err - } + rows, err := db.Query("SELECT c.id, c.registrationID, c.serial, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, c.issued, c.expires FROM certificates c JOIN certificateStatus cs ON cs.id = c.id" + where) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateList{}, err + } - Certificates := CertificateList{ - BaseList: BaseList{ - Title: "Certificates", - TableClass: "certificates_list", - Header: []template.HTML{"ID", "Account ID", "Serial", "Status", "Issued", "Expires"}, - }, - Rows: []Certificate{}, - } + Certificates := CertificateList{ + BaseList: BaseList{ + Title: "Certificates", + TableClass: "certificates_list", + Header: []template.HTML{"ID", "Account ID", "Serial", "Status", "Issued", "Expires"}, + }, + Rows: []Certificate{}, + } - for rows.Next() { - row := Certificate{} - err = rows.Scan(&row.Id, &row.RegistrationId, &row.Serial, &row.Status, &row.Issued, &row.Expires) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateList{}, err - } - Certificates.Rows = append(Certificates.Rows, row) - } + for rows.Next() { + row := Certificate{} + err = rows.Scan(&row.Id, &row.RegistrationId, &row.Serial, &row.Status, &row.Issued, &row.Expires) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateList{}, err + } + Certificates.Rows = append(Certificates.Rows, row) + } - return Certificates, nil + return Certificates, nil } type CertificateShow struct { - BaseShow - Related []BaseList - Related2 []BaseList + BaseShow + Related []BaseList + Related2 []BaseList } type CertificateExtra struct { - Id int - RegistrationId int - Serial string - Digest string - Issued string - Expires string - SubscriberApproved bool - Status string - OCSPLastUpdate string - Revoked string - RevokedReason int - LastNagSent string - NotAfter string - IsExpired bool + Id int + RegistrationId int + Serial string + Digest string + Issued string + Expires string + SubscriberApproved bool + Status string + OCSPLastUpdate string + Revoked string + RevokedReason int + LastNagSent string + NotAfter string + IsExpired bool } func GetCertificate(w http.ResponseWriter, r *http.Request, id int, serial string) (CertificateShow, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateShow{}, err - } + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateShow{}, err + } - defer db.Close() + defer db.Close() - var rows *sql.Rows - selectWhere := "SELECT c.id, c.registrationID, c.serial, c.digest, c.issued, c.expires, cs.subscriberApproved, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, cs.ocspLastUpdated, cs.revokedDate, cs.revokedReason, cs.lastExpirationNagSent, cs.notAfter, cs.isExpired FROM certificates c JOIN certificateStatus cs ON cs.id = c.id WHERE " + var rows *sql.Rows + selectWhere := "SELECT c.id, c.registrationID, c.serial, c.digest, c.issued, c.expires, cs.subscriberApproved, CASE WHEN cs.notAfter < NOW() THEN 'expired' ELSE cs.status END AS status, cs.ocspLastUpdated, cs.revokedDate, cs.revokedReason, cs.lastExpirationNagSent, cs.notAfter, cs.isExpired FROM certificates c JOIN certificateStatus cs ON cs.id = c.id WHERE " - if serial != "" { - rows, err = db.Query(selectWhere+"c.serial=?", serial) - } else { - rows, err = db.Query(selectWhere+"c.id=?", id) - } - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateShow{}, err - } + if serial != "" { + rows, err = db.Query(selectWhere+"c.serial=?", serial) + } else { + rows, err = db.Query(selectWhere+"c.id=?", id) + } + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateShow{}, err + } - CertificateDetails := CertificateShow{ - BaseShow: BaseShow{ - Title: "Certificate", - TableClass: "certificate_show", - Rows: []NameValue{}, - Links: []NameValHtml{}, - }, - } + CertificateDetails := CertificateShow{ + BaseShow: BaseShow{ + Title: "Certificate", + TableClass: "certificate_show", + Rows: []NameValue{}, + Links: []NameValHtml{}, + }, + } - for rows.Next() { - row := CertificateExtra{} - err = rows.Scan(&row.Id, &row.RegistrationId, &row.Serial, &row.Digest, &row.Issued, &row.Expires, &row.SubscriberApproved, &row.Status, &row.OCSPLastUpdate, &row.Revoked, &row.RevokedReason, &row.LastNagSent, &row.NotAfter, &row.IsExpired) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateShow{}, err - } - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Serial", row.Serial}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Digest", row.Digest}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Issued", row.Issued}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Expires", row.Expires}) - v := "false" - if row.SubscriberApproved { - v = "true" - } - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Subscriber Approved", v}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Status", row.Status}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"OCSP Last Update", row.OCSPLastUpdate}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Revoked", row.Revoked}) - reasonText := "" - switch row.RevokedReason { - case 0: - if row.Revoked != "0000-00-00 00:00:00" { - reasonText = " - Unspecified" - } - case 1: - reasonText = " - Key Compromise" - case 2: - reasonText = " - CA Compromise" - case 3: - reasonText = " - Affiliation Changed" - case 4: - reasonText = " - Superseded" - case 5: - reasonText = " - Cessation Of Operation" - case 6: - reasonText = " - Certificate Hold" - case 8: - reasonText = " - Remove From CRL" - case 9: - reasonText = " - Privilege Withdrawn" - case 10: - reasonText = " - AA Compromise" - } - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Revoked Reason", strconv.Itoa(row.RevokedReason) + reasonText}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Last Expiration Nag Sent", row.LastNagSent}) - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Not After", row.NotAfter}) - v = "false" - if row.IsExpired { - v = "true" - } - CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Is Expired", v}) + for rows.Next() { + row := CertificateExtra{} + err = rows.Scan(&row.Id, &row.RegistrationId, &row.Serial, &row.Digest, &row.Issued, &row.Expires, &row.SubscriberApproved, &row.Status, &row.OCSPLastUpdate, &row.Revoked, &row.RevokedReason, &row.LastNagSent, &row.NotAfter, &row.IsExpired) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateShow{}, err + } + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"ID", strconv.Itoa(row.Id)}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Serial", row.Serial}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Digest", row.Digest}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Issued", row.Issued}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Expires", row.Expires}) + v := "false" + if row.SubscriberApproved { + v = "true" + } + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Subscriber Approved", v}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Status", row.Status}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"OCSP Last Update", row.OCSPLastUpdate}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Revoked", row.Revoked}) + reasonText := "" + switch row.RevokedReason { + case 0: + if row.Revoked != "0000-00-00 00:00:00" { + reasonText = " - Unspecified" + } + case 1: + reasonText = " - Key Compromise" + case 2: + reasonText = " - CA Compromise" + case 3: + reasonText = " - Affiliation Changed" + case 4: + reasonText = " - Superseded" + case 5: + reasonText = " - Cessation Of Operation" + case 6: + reasonText = " - Certificate Hold" + case 8: + reasonText = " - Remove From CRL" + case 9: + reasonText = " - Privilege Withdrawn" + case 10: + reasonText = " - AA Compromise" + } + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Revoked Reason", strconv.Itoa(row.RevokedReason) + reasonText}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Last Expiration Nag Sent", row.LastNagSent}) + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Not After", row.NotAfter}) + v = "false" + if row.IsExpired { + v = "true" + } + CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Is Expired", v}) - Link := NameValHtml{"Account", template.HTML("" + strconv.Itoa(row.RegistrationId) + "")} - CertificateDetails.Links = append(CertificateDetails.Links, Link) + Link := NameValHtml{"Account", template.HTML("" + strconv.Itoa(row.RegistrationId) + "")} + CertificateDetails.Links = append(CertificateDetails.Links, Link) - if row.Revoked == "0000-00-00 00:00:00" { - revokeHtml, err := tmpls.RenderSingle("views/revoke-partial.tmpl", struct{ Serial string }{Serial: row.Serial}) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return CertificateShow{}, err - } - CertificateDetails.Extra = append(CertificateDetails.Extra, template.HTML(revokeHtml)) - } - } + if row.Revoked == "0000-00-00 00:00:00" { + revokeHtml, err := tmpls.RenderSingle("views/revoke-partial.tmpl", struct{ Serial string }{Serial: row.Serial}) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return CertificateShow{}, err + } + CertificateDetails.Extra = append(CertificateDetails.Extra, template.HTML(revokeHtml)) + } + } - return CertificateDetails, nil + return CertificateDetails, nil } - diff --git a/gui/certificate.go b/gui/certificate.go index e92b4e4..94bd441 100644 --- a/gui/certificate.go +++ b/gui/certificate.go @@ -1,485 +1,485 @@ package main import ( - "errors" - "fmt" - "io" - "io/ioutil" - "mime/multipart" - "os" - "os/exec" - "path/filepath" - "runtime/debug" - "strings" + "errors" + "fmt" + "io" + "io/ioutil" + "mime/multipart" + "os" + "os/exec" + "path/filepath" + "runtime/debug" + "strings" ) type CertificateInfo struct { - IsRoot bool - KeyTypes map[string]string - KeyType string - CreateType string + IsRoot bool + KeyTypes map[string]string + KeyType string + CreateType string - Country string - Organization string - OrgUnit string - CommonName string + Country string + Organization string + OrgUnit string + CommonName string - ImportFile multipart.File - ImportHandler *multipart.FileHeader - ImportPwd string + ImportFile multipart.File + ImportHandler *multipart.FileHeader + ImportPwd string - Key string - Passphrase string - Certificate string + Key string + Passphrase string + Certificate string - RequestBase string - Errors map[string]string + RequestBase string + Errors map[string]string } func (ci *CertificateInfo) Initialize() { - ci.Errors = make(map[string]string) + ci.Errors = make(map[string]string) - ci.KeyTypes = make(map[string]string) - ci.KeyTypes["rsa4096"] = "RSA-4096" - ci.KeyTypes["rsa3072"] = "RSA-3072" - ci.KeyTypes["rsa2048"] = "RSA-2048" - ci.KeyTypes["ecdsa384"] = "ECDSA-384" - ci.KeyTypes["ecdsa256"] = "ECDSA-256" + ci.KeyTypes = make(map[string]string) + ci.KeyTypes["rsa4096"] = "RSA-4096" + ci.KeyTypes["rsa3072"] = "RSA-3072" + ci.KeyTypes["rsa2048"] = "RSA-2048" + ci.KeyTypes["ecdsa384"] = "ECDSA-384" + ci.KeyTypes["ecdsa256"] = "ECDSA-256" - ci.KeyType = "rsa4096" + ci.KeyType = "rsa4096" } func (ci *CertificateInfo) Validate() bool { - ci.Errors = make(map[string]string) + ci.Errors = make(map[string]string) - if ci.CreateType == "generate" { - if strings.TrimSpace(ci.KeyType) == "" || strings.TrimSpace(ci.KeyTypes[ci.KeyType]) == "" { - ci.Errors["KeyType"] = "Please select a key type/size" - } - if strings.TrimSpace(ci.Country) == "" || len(ci.Country) < 2 { - ci.Errors["Country"] = "Please enter a valid 2-character country code" - } - if strings.TrimSpace(ci.Organization) == "" { - ci.Errors["Organization"] = "Please enter an organization name" - } - if strings.TrimSpace(ci.CommonName) == "" { - ci.Errors["CommonName"] = "Please enter a common name" - } - } + if ci.CreateType == "generate" { + if strings.TrimSpace(ci.KeyType) == "" || strings.TrimSpace(ci.KeyTypes[ci.KeyType]) == "" { + ci.Errors["KeyType"] = "Please select a key type/size" + } + if strings.TrimSpace(ci.Country) == "" || len(ci.Country) < 2 { + ci.Errors["Country"] = "Please enter a valid 2-character country code" + } + if strings.TrimSpace(ci.Organization) == "" { + ci.Errors["Organization"] = "Please enter an organization name" + } + if strings.TrimSpace(ci.CommonName) == "" { + ci.Errors["CommonName"] = "Please enter a common name" + } + } - if (ci.CreateType == "import") && (ci.ImportHandler != nil) { - ext := ci.ImportHandler.Filename[len(ci.ImportHandler.Filename)-4:] - if (ci.ImportHandler.Size == 0) || (ext != ".zip" && ext != ".pfx") { - ci.Errors["Import"] = "Please provide a bundle (.pfx or .zip) with a key and certificate" - } - } + if (ci.CreateType == "import") && (ci.ImportHandler != nil) { + ext := ci.ImportHandler.Filename[len(ci.ImportHandler.Filename)-4:] + if (ci.ImportHandler.Size == 0) || (ext != ".zip" && ext != ".pfx") { + ci.Errors["Import"] = "Please provide a bundle (.pfx or .zip) with a key and certificate" + } + } - if ci.CreateType == "upload" { - if strings.TrimSpace(ci.Key) == "" { - ci.Errors["Key"] = "Please provide a PEM-encoded key" - } - if strings.TrimSpace(ci.Certificate) == "" { - ci.Errors["Certificate"] = "Please provide a PEM-encoded certificate" - } - } + if ci.CreateType == "upload" { + if strings.TrimSpace(ci.Key) == "" { + ci.Errors["Key"] = "Please provide a PEM-encoded key" + } + if strings.TrimSpace(ci.Certificate) == "" { + ci.Errors["Certificate"] = "Please provide a PEM-encoded certificate" + } + } - return len(ci.Errors) == 0 + return len(ci.Errors) == 0 } func reportError(err error) error { - lines := strings.Split(string(debug.Stack()), "\n") - if len(lines) >= 5 { - lines = append(lines[:0], lines[5:]...) - } + lines := strings.Split(string(debug.Stack()), "\n") + if len(lines) >= 5 { + lines = append(lines[:0], lines[5:]...) + } - stop := len(lines) - for i := 0; i < len(lines); i++ { - if strings.Index(lines[i], ".ServeHTTP(") >= 0 { - stop = i - break - } - } - lines = lines[:stop] - lines = append(lines, "...") + stop := len(lines) + for i := 0; i < len(lines); i++ { + if strings.Index(lines[i], ".ServeHTTP(") >= 0 { + stop = i + break + } + } + lines = lines[:stop] + lines = append(lines, "...") - fmt.Println(strings.Join(lines, "\n")) + fmt.Println(strings.Join(lines, "\n")) - return errors.New("Error (" + err.Error() + ")! See LabCA logs for details") + return errors.New("Error (" + err.Error() + ")! See LabCA logs for details") } func preCreateTasks(path string) error { - if _, err := exe_cmd("touch " + path + "index.txt"); err != nil { - return reportError(err) - } - if _, err := exe_cmd("touch " + path + "index.txt.attr"); err != nil { - return reportError(err) - } + if _, err := exe_cmd("touch " + path + "index.txt"); err != nil { + return reportError(err) + } + if _, err := exe_cmd("touch " + path + "index.txt.attr"); err != nil { + return reportError(err) + } - if _, err := os.Stat(path + "serial"); os.IsNotExist(err) { - if err := ioutil.WriteFile(path+"serial", []byte("1000\n"), 0644); err != nil { - return err - } - } - if _, err := os.Stat(path + "crlnumber"); os.IsNotExist(err) { - if err = ioutil.WriteFile(path+"crlnumber", []byte("1000\n"), 0644); err != nil { - return err - } - } + if _, err := os.Stat(path + "serial"); os.IsNotExist(err) { + if err := ioutil.WriteFile(path+"serial", []byte("1000\n"), 0644); err != nil { + return err + } + } + if _, err := os.Stat(path + "crlnumber"); os.IsNotExist(err) { + if err = ioutil.WriteFile(path+"crlnumber", []byte("1000\n"), 0644); err != nil { + return err + } + } - if _, err := exe_cmd("mkdir -p " + path + "certs"); err != nil { - return reportError(err) - } + if _, err := exe_cmd("mkdir -p " + path + "certs"); err != nil { + return reportError(err) + } - return nil + return nil } func (ci *CertificateInfo) Create(path string, certBase string) error { - if err := preCreateTasks(path); err != nil { - return err - } + if err := preCreateTasks(path); err != nil { + return err + } - tmpDir, err := ioutil.TempDir("", "labca") - if err != nil { - return err - } + tmpDir, err := ioutil.TempDir("", "labca") + if err != nil { + return err + } - defer os.RemoveAll(tmpDir) + defer os.RemoveAll(tmpDir) - var tmpKey string - var tmpCert string - if ci.IsRoot { - tmpKey = filepath.Join(tmpDir, "root-ca.key") - tmpCert = filepath.Join(tmpDir, "root-ca.pem") - } else { - tmpKey = filepath.Join(tmpDir, "ca-int.key") - tmpCert = filepath.Join(tmpDir, "ca-int.pem") - } + var tmpKey string + var tmpCert string + if ci.IsRoot { + tmpKey = filepath.Join(tmpDir, "root-ca.key") + tmpCert = filepath.Join(tmpDir, "root-ca.pem") + } else { + tmpKey = filepath.Join(tmpDir, "ca-int.key") + tmpCert = filepath.Join(tmpDir, "ca-int.pem") + } - if ci.CreateType == "generate" { - // 1. Generate key - createCmd := "genrsa -aes256 -passout pass:foobar" - keySize := " 4096" - if strings.HasPrefix(ci.KeyType, "ecdsa") { - keySize = "" - createCmd = "ecparam -genkey -name " - if ci.KeyType == "ecdsa256" { - createCmd = createCmd + "prime256v1" - } - if ci.KeyType == "ecdsa384" { - createCmd = createCmd + "secp384r1" - } - } else { - if strings.HasSuffix(ci.KeyType, "3072") { - keySize = " 3072" - } - if strings.HasSuffix(ci.KeyType, "2048") { - keySize = " 2048" - } - } + if ci.CreateType == "generate" { + // 1. Generate key + createCmd := "genrsa -aes256 -passout pass:foobar" + keySize := " 4096" + if strings.HasPrefix(ci.KeyType, "ecdsa") { + keySize = "" + createCmd = "ecparam -genkey -name " + if ci.KeyType == "ecdsa256" { + createCmd = createCmd + "prime256v1" + } + if ci.KeyType == "ecdsa384" { + createCmd = createCmd + "secp384r1" + } + } else { + if strings.HasSuffix(ci.KeyType, "3072") { + keySize = " 3072" + } + if strings.HasSuffix(ci.KeyType, "2048") { + keySize = " 2048" + } + } - if _, err := exe_cmd("openssl " + createCmd + " -out " + path + certBase + ".key" + keySize); err != nil { - return reportError(err) - } - if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -passin pass:foobar -out " + path + certBase + ".tmp"); err != nil { - return reportError(err) - } - if _, err = exe_cmd("mv " + path + certBase + ".tmp " + path + certBase + ".key"); err != nil { - return reportError(err) - } + if _, err := exe_cmd("openssl " + createCmd + " -out " + path + certBase + ".key" + keySize); err != nil { + return reportError(err) + } + if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -passin pass:foobar -out " + path + certBase + ".tmp"); err != nil { + return reportError(err) + } + if _, err = exe_cmd("mv " + path + certBase + ".tmp " + path + certBase + ".key"); err != nil { + return reportError(err) + } - _, _ = exe_cmd("sleep 1") + _, _ = exe_cmd("sleep 1") - // 2. Generate certificate - subject := "/C=" + ci.Country + "/O=" + ci.Organization - if ci.OrgUnit != "" { - subject = subject + "/OU=" + ci.OrgUnit - } - subject = subject + "/CN=" + ci.CommonName - subject = strings.Replace(subject, " ", "\\\\", -1) + // 2. Generate certificate + subject := "/C=" + ci.Country + "/O=" + ci.Organization + if ci.OrgUnit != "" { + subject = subject + "/OU=" + ci.OrgUnit + } + subject = subject + "/CN=" + ci.CommonName + subject = strings.Replace(subject, " ", "\\\\", -1) - if ci.IsRoot { - if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -days 3650 -new -x509 -extensions v3_ca -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".pem"); err != nil { - return reportError(err) - } - } else { - if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -new -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".csr"); err != nil { - return reportError(err) - } - if _, err := exe_cmd("openssl ca -config " + path + "../openssl.cnf -extensions v3_intermediate_ca -days 3600 -md sha384 -notext -batch -in " + path + certBase + ".csr -out " + path + certBase + ".pem"); err != nil { - return reportError(err) - } - } + if ci.IsRoot { + if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -days 3650 -new -x509 -extensions v3_ca -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".pem"); err != nil { + return reportError(err) + } + } else { + if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -new -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".csr"); err != nil { + return reportError(err) + } + if _, err := exe_cmd("openssl ca -config " + path + "../openssl.cnf -extensions v3_intermediate_ca -days 3600 -md sha384 -notext -batch -in " + path + certBase + ".csr -out " + path + certBase + ".pem"); err != nil { + return reportError(err) + } + } - } else if ci.CreateType == "import" { - tmpFile := filepath.Join(tmpDir, ci.ImportHandler.Filename) + } else if ci.CreateType == "import" { + tmpFile := filepath.Join(tmpDir, ci.ImportHandler.Filename) - f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0666) - if err != nil { - return err - } + f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + return err + } - defer f.Close() + defer f.Close() - io.Copy(f, ci.ImportFile) + io.Copy(f, ci.ImportFile) - contentType := ci.ImportHandler.Header.Get("Content-Type") - if contentType == "application/x-pkcs12" { - if ci.IsRoot { - if strings.Index(ci.ImportHandler.Filename, "labca_root") != 0 { - fmt.Printf("WARNING: importing root from .pfx file but name is %s\n", ci.ImportHandler.Filename) - } - } else { - if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { - fmt.Printf("WARNING: importing issuer from .pfx file but name is %s\n", ci.ImportHandler.Filename) - } - } + contentType := ci.ImportHandler.Header.Get("Content-Type") + if contentType == "application/x-pkcs12" { + if ci.IsRoot { + if strings.Index(ci.ImportHandler.Filename, "labca_root") != 0 { + fmt.Printf("WARNING: importing root from .pfx file but name is %s\n", ci.ImportHandler.Filename) + } + } else { + if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { + fmt.Printf("WARNING: importing issuer from .pfx file but name is %s\n", ci.ImportHandler.Filename) + } + } - pwd := "pass:dummy" - if ci.ImportPwd != "" { - pwd = "pass:" + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) - } + pwd := "pass:dummy" + if ci.ImportPwd != "" { + pwd = "pass:" + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) + } - if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil { - if strings.Index(string(out), "invalid password") >= 0 { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } - if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil { - if strings.Index(string(out), "invalid password") >= 0 { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } - } else if contentType == "application/zip" { - if ci.IsRoot { - if (strings.Index(ci.ImportHandler.Filename, "labca_root") != 0) && (strings.Index(ci.ImportHandler.Filename, "labca_certificates") != 0) { - fmt.Printf("WARNING: importing root from .zip file but name is %s\n", ci.ImportHandler.Filename) - } - } else { - if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { - fmt.Printf("WARNING: importing issuer from .zip file but name is %s\n", ci.ImportHandler.Filename) - } - } + if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil { + if strings.Index(string(out), "invalid password") >= 0 { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } + if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil { + if strings.Index(string(out), "invalid password") >= 0 { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } + } else if contentType == "application/zip" { + if ci.IsRoot { + if (strings.Index(ci.ImportHandler.Filename, "labca_root") != 0) && (strings.Index(ci.ImportHandler.Filename, "labca_certificates") != 0) { + fmt.Printf("WARNING: importing root from .zip file but name is %s\n", ci.ImportHandler.Filename) + } + } else { + if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { + fmt.Printf("WARNING: importing issuer from .zip file but name is %s\n", ci.ImportHandler.Filename) + } + } - cmd := "unzip -j" - if ci.ImportPwd != "" { - cmd = cmd + " -P " + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) - } else { - cmd = cmd + " -P dummy" - } - cmd = cmd + " " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -d " + tmpDir + cmd := "unzip -j" + if ci.ImportPwd != "" { + cmd = cmd + " -P " + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) + } else { + cmd = cmd + " -P dummy" + } + cmd = cmd + " " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -d " + tmpDir - if _, err := exe_cmd(cmd); err != nil { - if err.Error() == "exit status 82" { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } - } else { - return errors.New("Content Type '" + contentType + "' not supported!") - } + if _, err := exe_cmd(cmd); err != nil { + if err.Error() == "exit status 82" { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } + } else { + return errors.New("Content Type '" + contentType + "' not supported!") + } - } else if ci.CreateType == "upload" { - if err := ioutil.WriteFile(tmpKey, []byte(ci.Key), 0644); err != nil { - return err - } + } else if ci.CreateType == "upload" { + if err := ioutil.WriteFile(tmpKey, []byte(ci.Key), 0644); err != nil { + return err + } - pwd := "pass:dummy" - if ci.Passphrase != "" { - pwd = "pass:" + strings.Replace(ci.Passphrase, " ", "\\\\", -1) - } + pwd := "pass:dummy" + if ci.Passphrase != "" { + pwd = "pass:" + strings.Replace(ci.Passphrase, " ", "\\\\", -1) + } - if out, err := exe_cmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil { - if strings.Index(string(out), ":bad decrypt:") >= 0 { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } else { - if _, err = exe_cmd("mv " + tmpKey + "-out " + tmpKey); err != nil { - return reportError(err) - } - } + if out, err := exe_cmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil { + if strings.Index(string(out), ":bad decrypt:") >= 0 { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } else { + if _, err = exe_cmd("mv " + tmpKey + "-out " + tmpKey); err != nil { + return reportError(err) + } + } - if err := ioutil.WriteFile(tmpCert, []byte(ci.Certificate), 0644); err != nil { - return err - } + if err := ioutil.WriteFile(tmpCert, []byte(ci.Certificate), 0644); err != nil { + return err + } - } else { - return fmt.Errorf("Unknown CreateType!") - } + } else { + return fmt.Errorf("Unknown CreateType!") + } - // This is shared between pfx/zip upload and pem text upload - if ci.CreateType != "generate" { - var rootCert string - var rootKey string - var issuerCert string - var issuerKey string + // This is shared between pfx/zip upload and pem text upload + if ci.CreateType != "generate" { + var rootCert string + var rootKey string + var issuerCert string + var issuerKey string - if ci.IsRoot { - rootCert = filepath.Join(tmpDir, "root-ca.pem") - rootKey = filepath.Join(tmpDir, "root-ca.key") + if ci.IsRoot { + rootCert = filepath.Join(tmpDir, "root-ca.pem") + rootKey = filepath.Join(tmpDir, "root-ca.key") - if _, err := os.Stat(rootCert); os.IsNotExist(err) { - return errors.New("File does not contain root-ca.pem!") - } - if _, err := os.Stat(rootKey); os.IsNotExist(err) { - return errors.New("File does not contain root-ca.key!") - } - } + if _, err := os.Stat(rootCert); os.IsNotExist(err) { + return errors.New("File does not contain root-ca.pem!") + } + if _, err := os.Stat(rootKey); os.IsNotExist(err) { + return errors.New("File does not contain root-ca.key!") + } + } - issuerCert = filepath.Join(tmpDir, "ca-int.pem") - issuerKey = filepath.Join(tmpDir, "ca-int.key") + issuerCert = filepath.Join(tmpDir, "ca-int.pem") + issuerKey = filepath.Join(tmpDir, "ca-int.key") - if _, err := os.Stat(issuerCert); os.IsNotExist(err) { - if ci.IsRoot { - issuerCert = "" - } else { - return errors.New("File does not contain ca-int.pem!") - } - } - if _, err := os.Stat(issuerKey); os.IsNotExist(err) { - if ci.IsRoot { - issuerKey = "" - } else { - return errors.New("File does not contain ca-int.key!") - } - } + if _, err := os.Stat(issuerCert); os.IsNotExist(err) { + if ci.IsRoot { + issuerCert = "" + } else { + return errors.New("File does not contain ca-int.pem!") + } + } + if _, err := os.Stat(issuerKey); os.IsNotExist(err) { + if ci.IsRoot { + issuerKey = "" + } else { + return errors.New("File does not contain ca-int.key!") + } + } - var rootSubject string - if (rootCert != "") && (rootKey != "") { - r, err := exe_cmd("openssl x509 -noout -subject -in " + rootCert) - if err != nil { - return reportError(err) - } else { - rootSubject = string(r[0 : len(r)-1]) - fmt.Printf("Import root with subject '%s'\n", rootSubject) - } + var rootSubject string + if (rootCert != "") && (rootKey != "") { + r, err := exe_cmd("openssl x509 -noout -subject -in " + rootCert) + if err != nil { + return reportError(err) + } else { + rootSubject = string(r[0 : len(r)-1]) + fmt.Printf("Import root with subject '%s'\n", rootSubject) + } - r, err = exe_cmd("openssl pkey -noout -in " + rootKey) - if err != nil { - return reportError(err) - } else { - fmt.Println("Import root key") - } - } + r, err = exe_cmd("openssl pkey -noout -in " + rootKey) + if err != nil { + return reportError(err) + } else { + fmt.Println("Import root key") + } + } - if (issuerCert != "") && (issuerKey != "") { - if ci.IsRoot { - if err := preCreateTasks(path + "issuer/"); err != nil { - return err - } - } + if (issuerCert != "") && (issuerKey != "") { + if ci.IsRoot { + if err := preCreateTasks(path + "issuer/"); err != nil { + return err + } + } - r, err := exe_cmd("openssl x509 -noout -subject -in " + issuerCert) - if err != nil { - return reportError(err) - } else { - fmt.Printf("Import issuer with subject '%s'\n", string(r[0:len(r)-1])) - } + r, err := exe_cmd("openssl x509 -noout -subject -in " + issuerCert) + if err != nil { + return reportError(err) + } else { + fmt.Printf("Import issuer with subject '%s'\n", string(r[0:len(r)-1])) + } - r, err = exe_cmd("openssl x509 -noout -issuer -in " + issuerCert) - if err != nil { - return reportError(err) - } else { - issuerIssuer := string(r[0 : len(r)-1]) - fmt.Printf("Issuer certificate issued by CA '%s'\n", issuerIssuer) + r, err = exe_cmd("openssl x509 -noout -issuer -in " + issuerCert) + if err != nil { + return reportError(err) + } else { + issuerIssuer := string(r[0 : len(r)-1]) + fmt.Printf("Issuer certificate issued by CA '%s'\n", issuerIssuer) - if rootSubject == "" { - r, err := exe_cmd("openssl x509 -noout -subject -in data/root-ca.pem") - if err != nil { - return reportError(err) - } else { - rootSubject = string(r[0 : len(r)-1]) - } - } + if rootSubject == "" { + r, err := exe_cmd("openssl x509 -noout -subject -in data/root-ca.pem") + if err != nil { + return reportError(err) + } else { + rootSubject = string(r[0 : len(r)-1]) + } + } - issuerIssuer = strings.Replace(issuerIssuer, "issuer=", "", -1) - rootSubject = strings.Replace(rootSubject, "subject=", "", -1) - if issuerIssuer != rootSubject { - return errors.New("Issuer not issued by our Root CA!") - } - } + issuerIssuer = strings.Replace(issuerIssuer, "issuer=", "", -1) + rootSubject = strings.Replace(rootSubject, "subject=", "", -1) + if issuerIssuer != rootSubject { + return errors.New("Issuer not issued by our Root CA!") + } + } - r, err = exe_cmd("openssl pkey -noout -in " + issuerKey) - if err != nil { - return reportError(err) - } else { - fmt.Println("Import issuer key") - } - } + r, err = exe_cmd("openssl pkey -noout -in " + issuerKey) + if err != nil { + return reportError(err) + } else { + fmt.Println("Import issuer key") + } + } - // All is good now, move files to their permanent location... - if rootCert != "" { - if _, err = exe_cmd("mv " + rootCert + " " + path); err != nil { - return reportError(err) - } - } - if rootKey != "" { - if _, err = exe_cmd("mv " + rootKey + " " + path); err != nil { - return reportError(err) - } - } - if issuerCert != "" { - if _, err = exe_cmd("mv " + issuerCert + " data/issuer/"); err != nil { - return reportError(err) - } - } - if issuerKey != "" { - if _, err = exe_cmd("mv " + issuerKey + " data/issuer/"); err != nil { - return reportError(err) - } - } + // All is good now, move files to their permanent location... + if rootCert != "" { + if _, err = exe_cmd("mv " + rootCert + " " + path); err != nil { + return reportError(err) + } + } + if rootKey != "" { + if _, err = exe_cmd("mv " + rootKey + " " + path); err != nil { + return reportError(err) + } + } + if issuerCert != "" { + if _, err = exe_cmd("mv " + issuerCert + " data/issuer/"); err != nil { + return reportError(err) + } + } + if issuerKey != "" { + if _, err = exe_cmd("mv " + issuerKey + " data/issuer/"); err != nil { + return reportError(err) + } + } - if (issuerCert != "") && (issuerKey != "") && ci.IsRoot { - if err := postCreateTasks(path+"issuer/", "ca-int"); err != nil { - return err - } - } - } + if (issuerCert != "") && (issuerKey != "") && ci.IsRoot { + if err := postCreateTasks(path+"issuer/", "ca-int"); err != nil { + return err + } + } + } - if err := postCreateTasks(path, certBase); err != nil { - return err - } + if err := postCreateTasks(path, certBase); err != nil { + return err + } - if ci.IsRoot { - if _, err := exe_cmd("openssl ca -config " + path + "openssl.cnf -gencrl -keyfile " + path + certBase + ".key -cert " + path + certBase + ".pem -out " + path + certBase + ".crl"); err != nil { - return reportError(err) - } - } + if ci.IsRoot { + if _, err := exe_cmd("openssl ca -config " + path + "openssl.cnf -gencrl -keyfile " + path + certBase + ".key -cert " + path + certBase + ".pem -out " + path + certBase + ".crl"); err != nil { + return reportError(err) + } + } - return nil + return nil } func postCreateTasks(path string, certBase string) error { - if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -out " + path + certBase + ".key.der -outform der"); err != nil { - return reportError(err) - } + if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -out " + path + certBase + ".key.der -outform der"); err != nil { + return reportError(err) + } - if _, err := exe_cmd("openssl x509 -in " + path + certBase + ".pem -out " + path + certBase + ".der -outform DER"); err != nil { - return reportError(err) - } + if _, err := exe_cmd("openssl x509 -in " + path + certBase + ".pem -out " + path + certBase + ".der -outform DER"); err != nil { + return reportError(err) + } - return nil + return nil } func exe_cmd(cmd string) ([]byte, error) { - parts := strings.Fields(cmd) - for i := 0; i < len(parts); i++ { - parts[i] = strings.Replace(parts[i], "\\\\", " ", -1) - } - head := parts[0] - parts = parts[1:] + parts := strings.Fields(cmd) + for i := 0; i < len(parts); i++ { + parts[i] = strings.Replace(parts[i], "\\\\", " ", -1) + } + head := parts[0] + parts = parts[1:] - out, err := exec.Command(head, parts...).CombinedOutput() - if err != nil { - fmt.Print(fmt.Sprint(err) + ": " + string(out)) - } else { - //fmt.Println(string(out)) - } - return out, err + out, err := exec.Command(head, parts...).CombinedOutput() + if err != nil { + fmt.Print(fmt.Sprint(err) + ": " + string(out)) + } else { + //fmt.Println(string(out)) + } + return out, err } diff --git a/gui/dashboard.go b/gui/dashboard.go index 66cc147..6ae73f1 100644 --- a/gui/dashboard.go +++ b/gui/dashboard.go @@ -1,359 +1,358 @@ package main import ( - "database/sql" - "fmt" - "github.com/dustin/go-humanize" - "log" - "net/http" - "regexp" - "strconv" - "strings" - "time" + "database/sql" + "fmt" + "github.com/dustin/go-humanize" + "log" + "net/http" + "regexp" + "strconv" + "strings" + "time" ) type Activity struct { - Title string - Message string - Timestamp string - TimestampRel string - Class string + Title string + Message string + Timestamp string + TimestampRel string + Class string } func _parseLine(line string, loc *time.Location) Activity { - var activity Activity + var activity Activity - // Remove ansi colors - b := make([]byte, len(line)) - var bl int - for i := 0; i < len(line); i++ { - c := line[i] - if c >= 32 && c != 127 { - b[bl] = c - bl++ - } - } - line = string(b[:bl]) - line = strings.Replace(line, "[31m", "", -1) - line = strings.Replace(line, "[1m", "", -1) - line = strings.Replace(line, "[0m", "", -1) + // Remove ansi colors + b := make([]byte, len(line)) + var bl int + for i := 0; i < len(line); i++ { + c := line[i] + if c >= 32 && c != 127 { + b[bl] = c + bl++ + } + } + line = string(b[:bl]) + line = strings.Replace(line, "[31m", "", -1) + line = strings.Replace(line, "[1m", "", -1) + line = strings.Replace(line, "[0m", "", -1) - re := regexp.MustCompile("^.*\\|\\s*(\\S)(\\S+) (\\S+) (\\S+) (.*)$") - result := re.FindStringSubmatch(line) + re := regexp.MustCompile("^.*\\|\\s*(\\S)(\\S+) (\\S+) (\\S+) (.*)$") + result := re.FindStringSubmatch(line) - activity.Class = "" - if result[1] == "W" { - activity.Class = "warning" - } - if result[1] == "E" { - activity.Class = "error" - } + activity.Class = "" + if result[1] == "W" { + activity.Class = "warning" + } + if result[1] == "E" { + activity.Class = "error" + } - timestamp, err := time.ParseInLocation("060102150405", result[2], loc) - activity.Timestamp = "" - activity.TimestampRel = "??" - if err == nil { - activity.Timestamp = timestamp.Format("02-Jan-2006 15:04:05 MST") - activity.Timestamp = strings.Replace(activity.Timestamp, "+0000", "GMT", -1) - activity.TimestampRel = humanize.RelTime(timestamp, time.Now(), "", "") - } + timestamp, err := time.ParseInLocation("060102150405", result[2], loc) + activity.Timestamp = "" + activity.TimestampRel = "??" + if err == nil { + activity.Timestamp = timestamp.Format("02-Jan-2006 15:04:05 MST") + activity.Timestamp = strings.Replace(activity.Timestamp, "+0000", "GMT", -1) + activity.TimestampRel = humanize.RelTime(timestamp, time.Now(), "", "") + } - tail := result[3][len(result[3])-2:] - activity.Title = "" - switch tail { - case "ca": - activity.Title = "Certification Agent" - case "ra": - activity.Title = "Registration Agent" - case "sa": - activity.Title = "Storage Agent" - case "va": - activity.Title = "Validation Agent" - } + tail := result[3][len(result[3])-2:] + activity.Title = "" + switch tail { + case "ca": + activity.Title = "Certification Agent" + case "ra": + activity.Title = "Registration Agent" + case "sa": + activity.Title = "Storage Agent" + case "va": + activity.Title = "Validation Agent" + } - message := result[5] - idx := strings.Index(message, ".well-known/acme-challenge") - if idx > -1 { - message = message[0:idx] - } - if strings.Index(message, "Checked CAA records for") > -1 { - message = message[0:strings.Index(message, ",")] - } - if strings.Index(message, "Validation result") > -1 { - message = message[0:17] - } - idx = strings.Index(message, " csr=[") - if idx > -1 { - message = message[0:idx] - } - idx = strings.Index(message, " precertificate=[") - if idx > -1 { - message = message[0:idx] - } - if strings.Index(message, "Certificate request - ") > -1 { - idx = strings.Index(message, " JSON={") - if idx > -1 { - message = message[0:idx] - } - } - activity.Message = message + message := result[5] + idx := strings.Index(message, ".well-known/acme-challenge") + if idx > -1 { + message = message[0:idx] + } + if strings.Index(message, "Checked CAA records for") > -1 { + message = message[0:strings.Index(message, ",")] + } + if strings.Index(message, "Validation result") > -1 { + message = message[0:17] + } + idx = strings.Index(message, " csr=[") + if idx > -1 { + message = message[0:idx] + } + idx = strings.Index(message, " precertificate=[") + if idx > -1 { + message = message[0:idx] + } + if strings.Index(message, "Certificate request - ") > -1 { + idx = strings.Index(message, " JSON={") + if idx > -1 { + message = message[0:idx] + } + } + activity.Message = message - return activity + return activity } func _parseActivity(data string) []Activity { - var activity []Activity + var activity []Activity - lines := strings.Split(data, "\n") + lines := strings.Split(data, "\n") - loc, err := time.LoadLocation(lines[0]) - if err != nil { - log.Printf("Could not determine location: %s\n", err) - loc = time.Local - } + loc, err := time.LoadLocation(lines[0]) + if err != nil { + log.Printf("Could not determine location: %s\n", err) + loc = time.Local + } - for i := len(lines) - 2; i >= 1; i-- { - activity = append(activity, _parseLine(lines[i], loc)) - } + for i := len(lines) - 2; i >= 1; i-- { + activity = append(activity, _parseLine(lines[i], loc)) + } - return activity + return activity } type Component struct { - Name string - Timestamp string - TimestampRel string - Class string - LogUrl string - LogTitle string - Buttons []map[string]interface{} + Name string + Timestamp string + TimestampRel string + Class string + LogUrl string + LogTitle string + Buttons []map[string]interface{} } func _parseComponents(data string) []Component { - var components []Component + var components []Component - if data[len(data)-1:] == "\n" { - data = data[0 : len(data)-1] - } + if data[len(data)-1:] == "\n" { + data = data[0 : len(data)-1] + } - parts := strings.Split(data, "|") + parts := strings.Split(data, "|") - loc, err := time.LoadLocation(parts[0]) - if err != nil { - log.Printf("Could not determine location: %s\n", err) - loc = time.Local - } + loc, err := time.LoadLocation(parts[0]) + if err != nil { + log.Printf("Could not determine location: %s\n", err) + loc = time.Local + } - nginx, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[1], loc) - nginxReal := "" - nginxNice := "stopped" - nginxClass := "error" - if err == nil { - nginxReal = nginx.Format("02-Jan-2006 15:04:05 MST") - nginxNice = humanize.RelTime(nginx, time.Now(), "", "") - nginxClass = "" - } + nginx, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[1], loc) + nginxReal := "" + nginxNice := "stopped" + nginxClass := "error" + if err == nil { + nginxReal = nginx.Format("02-Jan-2006 15:04:05 MST") + nginxNice = humanize.RelTime(nginx, time.Now(), "", "") + nginxClass = "" + } - svc, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[2], loc) - svcReal := "" - svcNice := "stopped" - svcClass := "error" - if err == nil { - svcReal = svc.Format("02-Jan-2006 15:04:05 MST") - svcNice = humanize.RelTime(svc, time.Now(), "", "") - svcClass = "" - } + svc, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[2], loc) + svcReal := "" + svcNice := "stopped" + svcClass := "error" + if err == nil { + svcReal = svc.Format("02-Jan-2006 15:04:05 MST") + svcNice = humanize.RelTime(svc, time.Now(), "", "") + svcClass = "" + } - boulder, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[3], loc) - boulderReal := "" - boulderNice := "stopped" - boulderClass := "error" - if err == nil { - boulderReal = boulder.Format("02-Jan-2006 15:04:05 MST") - boulderNice = humanize.RelTime(boulder, time.Now(), "", "") - boulderClass = "" - } + boulder, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[3], loc) + boulderReal := "" + boulderNice := "stopped" + boulderClass := "error" + if err == nil { + boulderReal = boulder.Format("02-Jan-2006 15:04:05 MST") + boulderNice = humanize.RelTime(boulder, time.Now(), "", "") + boulderClass = "" + } - labca, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[4], loc) - labcaReal := "" - labcaNice := "stopped" - labcaClass := "error" - if err == nil { - labcaReal = labca.Format("02-Jan-2006 15:04:05 MST") - labcaNice = humanize.RelTime(labca, time.Now(), "", "") - labcaClass = "" - } + labca, err := time.ParseInLocation("Jan _2 15:04:05 2006", parts[4], loc) + labcaReal := "" + labcaNice := "stopped" + labcaClass := "error" + if err == nil { + labcaReal = labca.Format("02-Jan-2006 15:04:05 MST") + labcaNice = humanize.RelTime(labca, time.Now(), "", "") + labcaClass = "" + } - components = append(components, Component{Name: "NGINX Webserver", Timestamp: nginxReal, TimestampRel: nginxNice, Class: nginxClass}) - components = append(components, Component{Name: "Host Service", Timestamp: svcReal, TimestampRel: svcNice, Class: svcClass}) - components = append(components, Component{Name: "Boulder (ACME)", Timestamp: boulderReal, TimestampRel: boulderNice, Class: boulderClass}) - components = append(components, Component{Name: "LabCA Application", Timestamp: labcaReal, TimestampRel: labcaNice, Class: labcaClass}) + components = append(components, Component{Name: "NGINX Webserver", Timestamp: nginxReal, TimestampRel: nginxNice, Class: nginxClass}) + components = append(components, Component{Name: "Host Service", Timestamp: svcReal, TimestampRel: svcNice, Class: svcClass}) + components = append(components, Component{Name: "Boulder (ACME)", Timestamp: boulderReal, TimestampRel: boulderNice, Class: boulderClass}) + components = append(components, Component{Name: "LabCA Application", Timestamp: labcaReal, TimestampRel: labcaNice, Class: labcaClass}) - return components + return components } type Stat struct { - Name string - Hint string - Value string - Class string + Name string + Hint string + Value string + Class string } func _parseStats(data string) []Stat { - var stats []Stat + var stats []Stat - if data[len(data)-1:] == "\n" { - data = data[0 : len(data)-1] - } + if data[len(data)-1:] == "\n" { + data = data[0 : len(data)-1] + } - parts := strings.Split(data, "|") + parts := strings.Split(data, "|") - loc, err := time.LoadLocation(parts[0]) - if err != nil { - log.Printf("Could not determine location: %s\n", err) - loc = time.Local - } + loc, err := time.LoadLocation(parts[0]) + if err != nil { + log.Printf("Could not determine location: %s\n", err) + loc = time.Local + } - since, err := time.ParseInLocation("2006-01-02 15:04:05", parts[1], loc) - var sinceReal string - sinceNice := "??" - if err == nil { - sinceReal = since.Format("02-Jan-2006 15:04:05 MST") - sinceNice = humanize.RelTime(since, time.Now(), "", "") - } - stats = append(stats, Stat{Name: "System Uptime", Hint: sinceReal, Value: sinceNice}) + since, err := time.ParseInLocation("2006-01-02 15:04:05", parts[1], loc) + var sinceReal string + sinceNice := "??" + if err == nil { + sinceReal = since.Format("02-Jan-2006 15:04:05 MST") + sinceNice = humanize.RelTime(since, time.Now(), "", "") + } + stats = append(stats, Stat{Name: "System Uptime", Hint: sinceReal, Value: sinceNice}) - numProcs, err := strconv.Atoi(parts[2]) - if err != nil { - numProcs = 0 - } - stats = append(stats, Stat{Name: "Process Count", Value: strconv.Itoa(numProcs)}) + numProcs, err := strconv.Atoi(parts[2]) + if err != nil { + numProcs = 0 + } + stats = append(stats, Stat{Name: "Process Count", Value: strconv.Itoa(numProcs)}) - memUsed, err := strconv.ParseUint(parts[3], 10, 64) - if err != nil { - memUsed = 0 - } - memAvail, err := strconv.ParseUint(parts[4], 10, 64) - if err != nil { - memAvail = 0 - } + memUsed, err := strconv.ParseUint(parts[3], 10, 64) + if err != nil { + memUsed = 0 + } + memAvail, err := strconv.ParseUint(parts[4], 10, 64) + if err != nil { + memAvail = 0 + } - percMem := float64(0) - if (memUsed + memAvail) > 0 { - percMem = float64(100) * float64(memUsed) / float64(memUsed+memAvail) - } + percMem := float64(0) + if (memUsed + memAvail) > 0 { + percMem = float64(100) * float64(memUsed) / float64(memUsed+memAvail) + } - usedHuman := humanize.IBytes(memUsed) - availHuman := humanize.IBytes(memAvail) - percHuman := fmt.Sprintf("%s %%", humanize.FtoaWithDigits(percMem, 1)) + usedHuman := humanize.IBytes(memUsed) + availHuman := humanize.IBytes(memAvail) + percHuman := fmt.Sprintf("%s %%", humanize.FtoaWithDigits(percMem, 1)) - class := "" - if percMem > 75 { - class = "warning" - } - if percMem > 90 { - class = "error" - } - stats = append(stats, Stat{Name: "Memory Usage", Value: percHuman, Class: class}) - stats = append(stats, Stat{Name: "Memory Used", Value: usedHuman}) - class = "" - if memAvail < 250000000 { - class = "warning" - } - if memAvail < 100000000 { - class = "error" - } - stats = append(stats, Stat{Name: "Memory Available", Value: availHuman, Class: class}) + class := "" + if percMem > 75 { + class = "warning" + } + if percMem > 90 { + class = "error" + } + stats = append(stats, Stat{Name: "Memory Usage", Value: percHuman, Class: class}) + stats = append(stats, Stat{Name: "Memory Used", Value: usedHuman}) + class = "" + if memAvail < 250000000 { + class = "warning" + } + if memAvail < 100000000 { + class = "error" + } + stats = append(stats, Stat{Name: "Memory Available", Value: availHuman, Class: class}) - return stats + return stats } -func CollectDashboardData(w http.ResponseWriter, r *http.Request) (map[string]interface {}, error) { - db, err := sql.Open(dbType, dbConn) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } +func CollectDashboardData(w http.ResponseWriter, r *http.Request) (map[string]interface{}, error) { + db, err := sql.Open(dbType, dbConn) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - defer db.Close() + defer db.Close() - dashboardData := make(map[string]interface{}) - dashboardData["RequestBase"] = r.Header.Get("X-Request-Base") + dashboardData := make(map[string]interface{}) + dashboardData["RequestBase"] = r.Header.Get("X-Request-Base") - rows, err := db.Query("SELECT count(*) FROM registrations") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + rows, err := db.Query("SELECT count(*) FROM registrations") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - var dbres int - if rows.Next() { - err = rows.Scan(&dbres) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + var dbres int + if rows.Next() { + err = rows.Scan(&dbres) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - dashboardData["NumAccounts"] = dbres - } + dashboardData["NumAccounts"] = dbres + } - rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter >= NOW()") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter >= NOW()") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - if rows.Next() { - err = rows.Scan(&dbres) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + if rows.Next() { + err = rows.Scan(&dbres) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - dashboardData["NumCerts"] = dbres - } + dashboardData["NumCerts"] = dbres + } - rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter < NOW()") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate='0000-00-00 00:00:00' AND notAfter < NOW()") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - if rows.Next() { - err = rows.Scan(&dbres) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + if rows.Next() { + err = rows.Scan(&dbres) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - dashboardData["NumExpired"] = dbres - } + dashboardData["NumExpired"] = dbres + } - rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate<>'0000-00-00 00:00:00'") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + rows, err = db.Query("SELECT count(*) FROM certificateStatus WHERE revokedDate<>'0000-00-00 00:00:00'") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - if rows.Next() { - err = rows.Scan(&dbres) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return nil, err - } + if rows.Next() { + err = rows.Scan(&dbres) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return nil, err + } - dashboardData["NumRevoked"] = dbres - } + dashboardData["NumRevoked"] = dbres + } - activity := getLog(w, r, "activity") - dashboardData["Activity"] = _parseActivity(activity) + activity := getLog(w, r, "activity") + dashboardData["Activity"] = _parseActivity(activity) - components := getLog(w, r, "components") - dashboardData["Components"] = _parseComponents(components) + components := getLog(w, r, "components") + dashboardData["Components"] = _parseComponents(components) - stats := getLog(w, r, "stats") - dashboardData["Stats"] = _parseStats(stats) + stats := getLog(w, r, "stats") + dashboardData["Stats"] = _parseStats(stats) - return dashboardData, nil + return dashboardData, nil } - diff --git a/gui/main.go b/gui/main.go index 9cfbe48..3f53ea3 100644 --- a/gui/main.go +++ b/gui/main.go @@ -1,2274 +1,2273 @@ package main import ( - "bufio" - "bytes" - "crypto/aes" - "crypto/cipher" - "crypto/rand" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "github.com/biz/templates" - _ "github.com/go-sql-driver/mysql" - "github.com/gorilla/mux" - "github.com/gorilla/securecookie" - "github.com/gorilla/sessions" - "github.com/gorilla/websocket" - "github.com/nbutton23/zxcvbn-go" - "github.com/theherk/viper" - "golang.org/x/crypto/bcrypt" - "html/template" - "io" - "io/ioutil" - "log" - "math" - "net" - "net/http" - "os" - "os/exec" - "path/filepath" - "reflect" - "regexp" - "runtime" - "runtime/debug" - "strconv" - "strings" - "time" + "bufio" + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "encoding/base64" + "encoding/json" + "errors" + "fmt" + "github.com/biz/templates" + _ "github.com/go-sql-driver/mysql" + "github.com/gorilla/mux" + "github.com/gorilla/securecookie" + "github.com/gorilla/sessions" + "github.com/gorilla/websocket" + "github.com/nbutton23/zxcvbn-go" + "github.com/theherk/viper" + "golang.org/x/crypto/bcrypt" + "html/template" + "io" + "io/ioutil" + "log" + "math" + "net" + "net/http" + "os" + "os/exec" + "path/filepath" + "reflect" + "regexp" + "runtime" + "runtime/debug" + "strconv" + "strings" + "time" ) const ( - writeWait = 10 * time.Second - pongWait = 60 * time.Second - pingPeriod = (pongWait * 9) / 10 + writeWait = 10 * time.Second + pongWait = 60 * time.Second + pingPeriod = (pongWait * 9) / 10 ) var ( - appSession *sessions.Session - restartSecret string - sessionStore *sessions.CookieStore - tmpls *templates.Templates - version string - dbConn string - dbType string - isDev bool + appSession *sessions.Session + restartSecret string + sessionStore *sessions.CookieStore + tmpls *templates.Templates + version string + dbConn string + dbType string + isDev bool - upgrader = websocket.Upgrader{ - ReadBufferSize: 1024, - WriteBufferSize: 1024, - } + upgrader = websocket.Upgrader{ + ReadBufferSize: 1024, + WriteBufferSize: 1024, + } ) type User struct { - Name string - Email string - Password string - Confirm string - NewPassword string - RequestBase string - Errors map[string]string + Name string + Email string + Password string + Confirm string + NewPassword string + RequestBase string + Errors map[string]string } func (reg *User) Validate(isNew bool, isChange bool) bool { - reg.Errors = make(map[string]string) + reg.Errors = make(map[string]string) - if strings.TrimSpace(reg.Name) == "" { - reg.Errors["Name"] = "Please enter a user name" - } + if strings.TrimSpace(reg.Name) == "" { + reg.Errors["Name"] = "Please enter a user name" + } - if isNew || isChange { - re := regexp.MustCompile(".+@.+\\..+") - matched := re.Match([]byte(reg.Email)) - if matched == false { - reg.Errors["Email"] = "Please enter a valid email address" - } - } + if isNew || isChange { + re := regexp.MustCompile(".+@.+\\..+") + matched := re.Match([]byte(reg.Email)) + if matched == false { + reg.Errors["Email"] = "Please enter a valid email address" + } + } - blacklist := []string{"labca", "acme", reg.Name} - if x := strings.Index(reg.Email, "@"); x > 0 { - blacklist = append(blacklist, reg.Email[:x]) - d := strings.Split(reg.Email[x+1:], ".") - for i:=0; i 0 { + blacklist = append(blacklist, reg.Email[:x]) + d := strings.Split(reg.Email[x+1:], ".") + for i := 0; i < len(d)-1; i++ { + blacklist = append(blacklist, d[i]) + } + } - if strings.TrimSpace(reg.Password) == "" { - reg.Errors["Password"] = "Please enter a password" - } else if isNew { - strength := zxcvbn.PasswordStrength(reg.Password, blacklist).Score - if strength < 1 { - reg.Errors["Password"] = "Please pick a stronger, more secure password" - } - } + if strings.TrimSpace(reg.Password) == "" { + reg.Errors["Password"] = "Please enter a password" + } else if isNew { + strength := zxcvbn.PasswordStrength(reg.Password, blacklist).Score + if strength < 1 { + reg.Errors["Password"] = "Please pick a stronger, more secure password" + } + } - if isNew { - if strings.TrimSpace(reg.Confirm) == "" { - reg.Errors["Confirm"] = "Please enter the password again" - } else if strings.TrimSpace(reg.Confirm) != strings.TrimSpace(reg.Password) { - reg.Errors["Confirm"] = "Passwords do not match!" - } - } + if isNew { + if strings.TrimSpace(reg.Confirm) == "" { + reg.Errors["Confirm"] = "Please enter the password again" + } else if strings.TrimSpace(reg.Confirm) != strings.TrimSpace(reg.Password) { + reg.Errors["Confirm"] = "Passwords do not match!" + } + } - if isChange { - if strings.TrimSpace(reg.NewPassword) != "" { - strength := zxcvbn.PasswordStrength(reg.NewPassword, blacklist).Score - if strength < 1 { - reg.Errors["NewPassword"] = "Please pick a stronger, more secure password" - } + if isChange { + if strings.TrimSpace(reg.NewPassword) != "" { + strength := zxcvbn.PasswordStrength(reg.NewPassword, blacklist).Score + if strength < 1 { + reg.Errors["NewPassword"] = "Please pick a stronger, more secure password" + } - if strings.TrimSpace(reg.Confirm) == "" { - reg.Errors["Confirm"] = "Please enter the new password again" - } else if strings.TrimSpace(reg.Confirm) != strings.TrimSpace(reg.NewPassword) { - reg.Errors["Confirm"] = "New passwords do not match!" - } - } + if strings.TrimSpace(reg.Confirm) == "" { + reg.Errors["Confirm"] = "Please enter the new password again" + } else if strings.TrimSpace(reg.Confirm) != strings.TrimSpace(reg.NewPassword) { + reg.Errors["Confirm"] = "New passwords do not match!" + } + } - byteStored := []byte(viper.GetString("user.password")) - err := bcrypt.CompareHashAndPassword(byteStored, []byte(reg.Password)) - if err != nil { - reg.Errors["Password"] = "Current password is not correct!" - } - } + byteStored := []byte(viper.GetString("user.password")) + err := bcrypt.CompareHashAndPassword(byteStored, []byte(reg.Password)) + if err != nil { + reg.Errors["Password"] = "Current password is not correct!" + } + } - return len(reg.Errors) == 0 + return len(reg.Errors) == 0 } type SetupConfig struct { - Fqdn string - Organization string - Dns string - DomainMode string - LockdownDomains string - WhitelistDomains string - RequestBase string - Errors map[string]string + Fqdn string + Organization string + Dns string + DomainMode string + LockdownDomains string + WhitelistDomains string + RequestBase string + Errors map[string]string } func (cfg *SetupConfig) Validate(orgRequired bool) bool { - cfg.Errors = make(map[string]string) + cfg.Errors = make(map[string]string) - if strings.TrimSpace(cfg.Fqdn) == "" { - cfg.Errors["Fqdn"] = "Please enter the Fully Qualified Domain name for this host" - } + if strings.TrimSpace(cfg.Fqdn) == "" { + cfg.Errors["Fqdn"] = "Please enter the Fully Qualified Domain name for this host" + } - if strings.TrimSpace(cfg.Organization) == "" && orgRequired { - cfg.Errors["Organization"] = "Please enter the organization name to show on the public pages" - } + if strings.TrimSpace(cfg.Organization) == "" && orgRequired { + cfg.Errors["Organization"] = "Please enter the organization name to show on the public pages" + } - if strings.TrimSpace(cfg.Dns) == "" { - cfg.Errors["Dns"] = "Please enter the DNS server to be used for validation" - } + if strings.TrimSpace(cfg.Dns) == "" { + cfg.Errors["Dns"] = "Please enter the DNS server to be used for validation" + } - if cfg.DomainMode != "lockdown" && cfg.DomainMode != "whitelist" && cfg.DomainMode != "standard" { - cfg.Errors["DomainMode"] = "Please select the domain mode to use" - } + if cfg.DomainMode != "lockdown" && cfg.DomainMode != "whitelist" && cfg.DomainMode != "standard" { + cfg.Errors["DomainMode"] = "Please select the domain mode to use" + } - if cfg.DomainMode == "lockdown" && strings.TrimSpace(cfg.LockdownDomains) == "" { - cfg.Errors["LockdownDomains"] = "Please enter one or more domains that this PKI host is locked down to" - } + if cfg.DomainMode == "lockdown" && strings.TrimSpace(cfg.LockdownDomains) == "" { + cfg.Errors["LockdownDomains"] = "Please enter one or more domains that this PKI host is locked down to" + } - if cfg.DomainMode == "whitelist" && strings.TrimSpace(cfg.WhitelistDomains) == "" { - cfg.Errors["WhitelistDomains"] = "Please enter one or more domains that are whitelisted for this PKI host" - } + if cfg.DomainMode == "whitelist" && strings.TrimSpace(cfg.WhitelistDomains) == "" { + cfg.Errors["WhitelistDomains"] = "Please enter one or more domains that are whitelisted for this PKI host" + } - return len(cfg.Errors) == 0 + return len(cfg.Errors) == 0 } func getSession(w http.ResponseWriter, r *http.Request) *sessions.Session { - if appSession != nil { - return appSession - } + if appSession != nil { + return appSession + } - session, err := sessionStore.Get(r, "labca") - if err != nil { - // Create new session - session = sessions.NewSession(sessionStore, "labca") - session.Save(r, w) - } + session, err := sessionStore.Get(r, "labca") + if err != nil { + // Create new session + session = sessions.NewSession(sessionStore, "labca") + session.Save(r, w) + } - appSession = session - return appSession + appSession = session + return appSession } func errorHandler(w http.ResponseWriter, r *http.Request, err error, status int) { - log.Printf("errorHandler: %v", err) + log.Printf("errorHandler: %v", err) - w.WriteHeader(status) + w.WriteHeader(status) - pc := make([]uintptr, 15) - n := runtime.Callers(2, pc) - frames := runtime.CallersFrames(pc[:n]) - frame, _ := frames.Next() - //fmt.Printf("%s:%d, %s\n", frame.File, frame.Line, frame.Function) + pc := make([]uintptr, 15) + n := runtime.Callers(2, pc) + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + //fmt.Printf("%s:%d, %s\n", frame.File, frame.Line, frame.Function) - if frame.Function == "main.render" { - fmt.Fprintf(w, "Could not render requested page") - return - } + if frame.Function == "main.render" { + fmt.Fprintf(w, "Could not render requested page") + return + } - if status == http.StatusNotFound { - render(w, r, "error", map[string]interface{}{"Message": "That page does not exist"}) - } else { - lines := strings.Split(string(debug.Stack()), "\n") - if len(lines) >= 5 { - lines = append(lines[:0], lines[5:]...) - } - fmt.Print(strings.Join(lines, "\n")) + if status == http.StatusNotFound { + render(w, r, "error", map[string]interface{}{"Message": "That page does not exist"}) + } else { + lines := strings.Split(string(debug.Stack()), "\n") + if len(lines) >= 5 { + lines = append(lines[:0], lines[5:]...) + } + fmt.Print(strings.Join(lines, "\n")) - render(w, r, "error", map[string]interface{}{"Message": "Some unexpected error occurred!"}) - // TODO: send email eventually with info on the error - } + render(w, r, "error", map[string]interface{}{"Message": "Some unexpected error occurred!"}) + // TODO: send email eventually with info on the error + } } func rootHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - dashboardData, err := CollectDashboardData(w, r) - if err == nil { - render(w, r, "dashboard", dashboardData) - } + dashboardData, err := CollectDashboardData(w, r) + if err == nil { + render(w, r, "dashboard", dashboardData) + } } func aboutHandler(w http.ResponseWriter, r *http.Request) { - render(w, r, "about", map[string]interface{}{ - "Title": "About", - }) + render(w, r, "about", map[string]interface{}{ + "Title": "About", + }) } func loginHandler(w http.ResponseWriter, r *http.Request) { - if viper.Get("user.password") == nil { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if viper.Get("user.password") == nil { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - session := getSession(w, r) - var bounceUrl string - if session.Values["bounce"] == nil { - bounceUrl = "/" - } else { - bounceUrl = session.Values["bounce"].(string) - } + session := getSession(w, r) + var bounceUrl string + if session.Values["bounce"] == nil { + bounceUrl = "/" + } else { + bounceUrl = session.Values["bounce"].(string) + } - if session.Values["user"] != nil { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+bounceUrl, http.StatusFound) - return - } + if session.Values["user"] != nil { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+bounceUrl, http.StatusFound) + return + } - if r.Method == "GET" { - reg := &User{ - RequestBase: r.Header.Get("X-Request-Base"), - } - render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) - return - } else if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + if r.Method == "GET" { + reg := &User{ + RequestBase: r.Header.Get("X-Request-Base"), + } + render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) + return + } else if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - reg := &User{ - Name: r.Form.Get("username"), - Password: r.Form.Get("password"), - RequestBase: r.Header.Get("X-Request-Base"), - } + reg := &User{ + Name: r.Form.Get("username"), + Password: r.Form.Get("password"), + RequestBase: r.Header.Get("X-Request-Base"), + } - if reg.Validate(false, false) == false { - render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) - return - } + if reg.Validate(false, false) == false { + render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) + return + } - if viper.GetString("user.name") != reg.Name { - reg.Errors["Name"] = "Incorrect username or password" - render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) - return - } else { - byteStored := []byte(viper.GetString("user.password")) - err := bcrypt.CompareHashAndPassword(byteStored, []byte(reg.Password)) - if err != nil { - log.Println(err) - reg.Errors["Name"] = "Incorrect username or password" - render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) - return - } - } + if viper.GetString("user.name") != reg.Name { + reg.Errors["Name"] = "Incorrect username or password" + render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) + return + } else { + byteStored := []byte(viper.GetString("user.password")) + err := bcrypt.CompareHashAndPassword(byteStored, []byte(reg.Password)) + if err != nil { + log.Println(err) + reg.Errors["Name"] = "Incorrect username or password" + render(w, r, "login", map[string]interface{}{"User": reg, "IsLogin": true}) + return + } + } - session.Values["user"] = reg.Name - session.Save(r, w) + session.Values["user"] = reg.Name + session.Save(r, w) - http.Redirect(w, r, r.Header.Get("X-Request-Base")+bounceUrl, http.StatusFound) - } else { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/login", http.StatusSeeOther) - return - } + http.Redirect(w, r, r.Header.Get("X-Request-Base")+bounceUrl, http.StatusFound) + } else { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/login", http.StatusSeeOther) + return + } } func logoutHandler(w http.ResponseWriter, r *http.Request) { - appSession = nil - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) + appSession = nil + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) } func _sendCmdOutput(w http.ResponseWriter, r *http.Request, cmd string) { - parts := strings.Fields(cmd) - for i := 0; i < len(parts); i++ { - parts[i] = strings.Replace(parts[i], "\\\\", " ", -1) - } - head := parts[0] - parts = parts[1:] + parts := strings.Fields(cmd) + for i := 0; i < len(parts); i++ { + parts[i] = strings.Replace(parts[i], "\\\\", " ", -1) + } + head := parts[0] + parts = parts[1:] - out, err := exec.Command(head, parts...).Output() - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + out, err := exec.Command(head, parts...).Output() + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - buf := bytes.NewBuffer(out) - _, err = buf.WriteTo(w) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + buf := bytes.NewBuffer(out) + _, err = buf.WriteTo(w) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } } func _backupHandler(w http.ResponseWriter, r *http.Request) { - res := struct { - Success bool - Message string - }{Success: true} + res := struct { + Success bool + Message string + }{Success: true} - action := r.Form.Get("action") - if action == "backup-restore" { - backup := r.Form.Get("backup") - if !_hostCommand(w, r, action, backup) { - res.Success = false - res.Message = "Command failed - see LabCA log for any details" - } + action := r.Form.Get("action") + if action == "backup-restore" { + backup := r.Form.Get("backup") + if !_hostCommand(w, r, action, backup) { + res.Success = false + res.Message = "Command failed - see LabCA log for any details" + } - defer _hostCommand(w, r, "server-restart") - } else if action == "backup-delete" { - backup := r.Form.Get("backup") - if !_hostCommand(w, r, action, backup) { - res.Success = false - res.Message = "Command failed - see LabCA log for any details" - } - } else if action == "backup-now" { - res.Message = getLog(w, r, "server-backup") - if res.Message == "" { - res.Success = false - res.Message = "Command failed - see LabCA log for any details" - } else { - res.Message = filepath.Base(res.Message) - } - } + defer _hostCommand(w, r, "server-restart") + } else if action == "backup-delete" { + backup := r.Form.Get("backup") + if !_hostCommand(w, r, action, backup) { + res.Success = false + res.Message = "Command failed - see LabCA log for any details" + } + } else if action == "backup-now" { + res.Message = getLog(w, r, "server-backup") + if res.Message == "" { + res.Success = false + res.Message = "Command failed - see LabCA log for any details" + } else { + res.Message = filepath.Base(res.Message) + } + } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func _accountUpdateHandler(w http.ResponseWriter, r *http.Request) { - reg := &User{ - Name: r.Form.Get("username"), - Email: r.Form.Get("email"), - NewPassword: r.Form.Get("new-password"), - Confirm: r.Form.Get("confirm"), - Password: r.Form.Get("password"), - } + reg := &User{ + Name: r.Form.Get("username"), + Email: r.Form.Get("email"), + NewPassword: r.Form.Get("new-password"), + Confirm: r.Form.Get("confirm"), + Password: r.Form.Get("password"), + } - res := struct { - Success bool - Errors map[string]string - }{Success: true} + res := struct { + Success bool + Errors map[string]string + }{Success: true} - if reg.Validate(false, true) { - viper.Set("user.name", reg.Name) - viper.Set("user.email", reg.Email) + if reg.Validate(false, true) { + viper.Set("user.name", reg.Name) + viper.Set("user.email", reg.Email) - if reg.NewPassword != "" { - hash, err := bcrypt.GenerateFromPassword([]byte(reg.NewPassword), bcrypt.MinCost) - if err != nil { - res.Success = false - errorHandler(w, r, err, http.StatusInternalServerError) - return - } - viper.Set("user.password", string(hash)) + if reg.NewPassword != "" { + hash, err := bcrypt.GenerateFromPassword([]byte(reg.NewPassword), bcrypt.MinCost) + if err != nil { + res.Success = false + errorHandler(w, r, err, http.StatusInternalServerError) + return + } + viper.Set("user.password", string(hash)) - // Forget current session, so user has to login with the new password - appSession = nil - } + // Forget current session, so user has to login with the new password + appSession = nil + } - viper.WriteConfig() + viper.WriteConfig() - } else { - res.Success = false - res.Errors = reg.Errors - } + } else { + res.Success = false + res.Errors = reg.Errors + } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func _configUpdateHandler(w http.ResponseWriter, r *http.Request) { - cfg := &SetupConfig{ - Fqdn: r.Form.Get("fqdn"), - Organization: r.Form.Get("organization"), - Dns: r.Form.Get("dns"), - DomainMode: r.Form.Get("domain_mode"), - LockdownDomains: r.Form.Get("lockdown_domains"), - WhitelistDomains: r.Form.Get("whitelist_domains"), - } + cfg := &SetupConfig{ + Fqdn: r.Form.Get("fqdn"), + Organization: r.Form.Get("organization"), + Dns: r.Form.Get("dns"), + DomainMode: r.Form.Get("domain_mode"), + LockdownDomains: r.Form.Get("lockdown_domains"), + WhitelistDomains: r.Form.Get("whitelist_domains"), + } - res := struct { - Success bool - Errors map[string]string - }{Success: true} + res := struct { + Success bool + Errors map[string]string + }{Success: true} - if cfg.Validate(true) { - delta := false + if cfg.Validate(true) { + delta := false - if cfg.Fqdn != viper.GetString("labca.fqdn") { - delta = true - viper.Set("labca.fqdn", cfg.Fqdn) - } + if cfg.Fqdn != viper.GetString("labca.fqdn") { + delta = true + viper.Set("labca.fqdn", cfg.Fqdn) + } - if cfg.Organization != viper.GetString("labca.organization") { - delta = true - viper.Set("labca.organization", cfg.Organization) - } + if cfg.Organization != viper.GetString("labca.organization") { + delta = true + viper.Set("labca.organization", cfg.Organization) + } - matched, err := regexp.MatchString(":\\d+$", cfg.Dns) - if err == nil && !matched { - cfg.Dns += ":53" - } + matched, err := regexp.MatchString(":\\d+$", cfg.Dns) + if err == nil && !matched { + cfg.Dns += ":53" + } - if cfg.Dns != viper.GetString("labca.dns") { - delta = true - viper.Set("labca.dns", cfg.Dns) - } + if cfg.Dns != viper.GetString("labca.dns") { + delta = true + viper.Set("labca.dns", cfg.Dns) + } - domain_mode := cfg.DomainMode - if domain_mode != viper.GetString("labca.domain_mode") { - delta = true - viper.Set("labca.domain_mode", cfg.DomainMode) - } + domain_mode := cfg.DomainMode + if domain_mode != viper.GetString("labca.domain_mode") { + delta = true + viper.Set("labca.domain_mode", cfg.DomainMode) + } - if domain_mode == "lockdown" { - if cfg.LockdownDomains != viper.GetString("labca.lockdown") { - delta = true - viper.Set("labca.lockdown", cfg.LockdownDomains) - } - } - if domain_mode == "whitelist" { - if cfg.WhitelistDomains != viper.GetString("labca.whitelist") { - delta = true - viper.Set("labca.whitelist", cfg.WhitelistDomains) - } - } + if domain_mode == "lockdown" { + if cfg.LockdownDomains != viper.GetString("labca.lockdown") { + delta = true + viper.Set("labca.lockdown", cfg.LockdownDomains) + } + } + if domain_mode == "whitelist" { + if cfg.WhitelistDomains != viper.GetString("labca.whitelist") { + delta = true + viper.Set("labca.whitelist", cfg.WhitelistDomains) + } + } - if delta { - viper.WriteConfig() + if delta { + viper.WriteConfig() - err := _applyConfig() - if err != nil { - res.Success = false - res.Errors = cfg.Errors - res.Errors["ConfigUpdate"] = "Config apply error: '" + err.Error() + "'" - } - } else { - res.Success = false - res.Errors = cfg.Errors - res.Errors["ConfigUpdate"] = "Nothing changed!" - } + err := _applyConfig() + if err != nil { + res.Success = false + res.Errors = cfg.Errors + res.Errors["ConfigUpdate"] = "Config apply error: '" + err.Error() + "'" + } + } else { + res.Success = false + res.Errors = cfg.Errors + res.Errors["ConfigUpdate"] = "Nothing changed!" + } - } else { - res.Success = false - res.Errors = cfg.Errors - } + } else { + res.Success = false + res.Errors = cfg.Errors + } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } type EmailConfig struct { - DoEmail bool - Server string - Port string - EmailUser string - EmailPwd []byte - From string - Errors map[string]string + DoEmail bool + Server string + Port string + EmailUser string + EmailPwd []byte + From string + Errors map[string]string } func (cfg *EmailConfig) Validate() bool { - cfg.Errors = make(map[string]string) + cfg.Errors = make(map[string]string) - result, err := _encrypt(cfg.EmailPwd) - if err == nil { - cfg.EmailPwd = []byte(result) - } else { - cfg.Errors["EmailPwd"] = "Could not encrypt this password: " + err.Error() - } + result, err := _encrypt(cfg.EmailPwd) + if err == nil { + cfg.EmailPwd = []byte(result) + } else { + cfg.Errors["EmailPwd"] = "Could not encrypt this password: " + err.Error() + } - if cfg.DoEmail == false { - return len(cfg.Errors) == 0 - } + if cfg.DoEmail == false { + return len(cfg.Errors) == 0 + } - if strings.TrimSpace(cfg.Server) == "" { - cfg.Errors["Server"] = "Please enter the email server address" - } + if strings.TrimSpace(cfg.Server) == "" { + cfg.Errors["Server"] = "Please enter the email server address" + } - if strings.TrimSpace(cfg.Port) == "" { - cfg.Errors["Port"] = "Please enter the email server port number" - } + if strings.TrimSpace(cfg.Port) == "" { + cfg.Errors["Port"] = "Please enter the email server port number" + } - p, err := strconv.Atoi(cfg.Port) - if err != nil { - cfg.Errors["Port"] = "Port number must be numeric" - } else if p <= 0 { - cfg.Errors["Port"] = "Port number must be positive" - } else if p > 65535 { - cfg.Errors["Port"] = "Port number too large" - } + p, err := strconv.Atoi(cfg.Port) + if err != nil { + cfg.Errors["Port"] = "Port number must be numeric" + } else if p <= 0 { + cfg.Errors["Port"] = "Port number must be positive" + } else if p > 65535 { + cfg.Errors["Port"] = "Port number too large" + } - if strings.TrimSpace(cfg.EmailUser) == "" { - cfg.Errors["EmailUser"] = "Please enter the username for authorization to the email server" - } + if strings.TrimSpace(cfg.EmailUser) == "" { + cfg.Errors["EmailUser"] = "Please enter the username for authorization to the email server" + } - res, err := _decrypt(string(cfg.EmailPwd)) - if err != nil { - cfg.Errors["EmailPwd"] = "Could not decrypt this password: " + err.Error() - } - if strings.TrimSpace(string(res)) == "" { - cfg.Errors["EmailPwd"] = "Please enter the password for authorization to the email server" - } + res, err := _decrypt(string(cfg.EmailPwd)) + if err != nil { + cfg.Errors["EmailPwd"] = "Could not decrypt this password: " + err.Error() + } + if strings.TrimSpace(string(res)) == "" { + cfg.Errors["EmailPwd"] = "Please enter the password for authorization to the email server" + } - if strings.TrimSpace(cfg.From) == "" { - cfg.Errors["From"] = "Please enter the from email address" - } + if strings.TrimSpace(cfg.From) == "" { + cfg.Errors["From"] = "Please enter the from email address" + } - return len(cfg.Errors) == 0 + return len(cfg.Errors) == 0 } - func _emailUpdateHandler(w http.ResponseWriter, r *http.Request) { - cfg := &EmailConfig{ - DoEmail: (r.Form.Get("do_email") == "true"), - Server: r.Form.Get("server"), - Port: r.Form.Get("port"), - EmailUser: r.Form.Get("email_user"), - EmailPwd: []byte(r.Form.Get("email_pwd")), - From: r.Form.Get("from"), - } + cfg := &EmailConfig{ + DoEmail: (r.Form.Get("do_email") == "true"), + Server: r.Form.Get("server"), + Port: r.Form.Get("port"), + EmailUser: r.Form.Get("email_user"), + EmailPwd: []byte(r.Form.Get("email_pwd")), + From: r.Form.Get("from"), + } - res := struct { - Success bool - Errors map[string]string - }{Success: true} + res := struct { + Success bool + Errors map[string]string + }{Success: true} - if cfg.Validate() { - delta := false + if cfg.Validate() { + delta := false - if cfg.DoEmail != viper.GetBool("labca.email.enable") { - delta = true - viper.Set("labca.email.enable", cfg.DoEmail) - } + if cfg.DoEmail != viper.GetBool("labca.email.enable") { + delta = true + viper.Set("labca.email.enable", cfg.DoEmail) + } - if cfg.Server != viper.GetString("labca.email.server") { - delta = true - viper.Set("labca.email.server", cfg.Server) - } + if cfg.Server != viper.GetString("labca.email.server") { + delta = true + viper.Set("labca.email.server", cfg.Server) + } - if cfg.Port != viper.GetString("labca.email.port") { - delta = true - viper.Set("labca.email.port", cfg.Port) - } + if cfg.Port != viper.GetString("labca.email.port") { + delta = true + viper.Set("labca.email.port", cfg.Port) + } - if cfg.EmailUser != viper.GetString("labca.email.user") { - delta = true - viper.Set("labca.email.user", cfg.EmailUser) - } + if cfg.EmailUser != viper.GetString("labca.email.user") { + delta = true + viper.Set("labca.email.user", cfg.EmailUser) + } - res1, err1 := _decrypt(string(cfg.EmailPwd)) - if err1 != nil && cfg.DoEmail { - log.Println("WARNING: could not decrypt given password: " + err1.Error()) - } - res2, err2 := _decrypt(viper.GetString("labca.email.pass")) - if err2 != nil && cfg.DoEmail && viper.GetString("labca.email.pass") != "" { - log.Println("WARNING: could not decrypt stored password: " + err2.Error()) - } - if string(res1) != string(res2) { - delta = true - viper.Set("labca.email.pass", string(cfg.EmailPwd)) - } + res1, err1 := _decrypt(string(cfg.EmailPwd)) + if err1 != nil && cfg.DoEmail { + log.Println("WARNING: could not decrypt given password: " + err1.Error()) + } + res2, err2 := _decrypt(viper.GetString("labca.email.pass")) + if err2 != nil && cfg.DoEmail && viper.GetString("labca.email.pass") != "" { + log.Println("WARNING: could not decrypt stored password: " + err2.Error()) + } + if string(res1) != string(res2) { + delta = true + viper.Set("labca.email.pass", string(cfg.EmailPwd)) + } - if cfg.From != viper.GetString("labca.email.from") { - delta = true - viper.Set("labca.email.from", cfg.From) - } + if cfg.From != viper.GetString("labca.email.from") { + delta = true + viper.Set("labca.email.from", cfg.From) + } - if delta { - viper.WriteConfig() + if delta { + viper.WriteConfig() - err := _applyConfig() - if err != nil { - res.Success = false - res.Errors = cfg.Errors - res.Errors["EmailUpdate"] = "Config apply error: '" + err.Error() + "'" - } - } else { - res.Success = false - res.Errors = cfg.Errors - res.Errors["EmailUpdate"] = "Nothing changed!" - } + err := _applyConfig() + if err != nil { + res.Success = false + res.Errors = cfg.Errors + res.Errors["EmailUpdate"] = "Config apply error: '" + err.Error() + "'" + } + } else { + res.Success = false + res.Errors = cfg.Errors + res.Errors["EmailUpdate"] = "Nothing changed!" + } - } else { - res.Success = false - res.Errors = cfg.Errors - } + } else { + res.Success = false + res.Errors = cfg.Errors + } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func _emailSendHandler(w http.ResponseWriter, r *http.Request) { - res := struct { - Success bool - Errors map[string]string - }{Success: true, Errors: make(map[string]string)} + res := struct { + Success bool + Errors map[string]string + }{Success: true, Errors: make(map[string]string)} - recipient := viper.GetString("user.email") - if !_hostCommand(w, r, "test-email", recipient) { - res.Success = false - res.Errors["EmailSend"] = "Failed to send email - see logs" - } + recipient := viper.GetString("user.email") + if !_hostCommand(w, r, "test-email", recipient) { + res.Success = false + res.Errors["EmailSend"] = "Failed to send email - see logs" + } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) } func _exportHandler(w http.ResponseWriter, r *http.Request) { - basename := "certificates" - if r.Form.Get("root") != "true" { - basename = "issuer" - } - if r.Form.Get("issuer") != "true" { - basename = "root" - } + basename := "certificates" + if r.Form.Get("root") != "true" { + basename = "issuer" + } + if r.Form.Get("issuer") != "true" { + basename = "root" + } - if r.Form.Get("type") == "pfx" { - w.Header().Set("Content-Type", "application/x-pkcs12") - w.Header().Set("Content-Disposition", "attachment; filename=labca_"+basename+".pfx") + if r.Form.Get("type") == "pfx" { + w.Header().Set("Content-Type", "application/x-pkcs12") + w.Header().Set("Content-Disposition", "attachment; filename=labca_"+basename+".pfx") - var certBase string - if basename == "root" { - certBase = "data/root-ca" - } else { - certBase = "data/issuer/ca-int" - } + var certBase string + if basename == "root" { + certBase = "data/root-ca" + } else { + certBase = "data/issuer/ca-int" + } - cmd := "openssl pkcs12 -export -inkey " + certBase + ".key -in " + certBase + ".pem -passout pass:" + r.Form.Get("export-pwd") + cmd := "openssl pkcs12 -export -inkey " + certBase + ".key -in " + certBase + ".pem -passout pass:" + r.Form.Get("export-pwd") - _sendCmdOutput(w, r, cmd) - } + _sendCmdOutput(w, r, cmd) + } - if r.Form.Get("type") == "zip" { - w.Header().Set("Content-Type", "application/zip") - w.Header().Set("Content-Disposition", "attachment; filename=labca_"+basename+".zip") + if r.Form.Get("type") == "zip" { + w.Header().Set("Content-Type", "application/zip") + w.Header().Set("Content-Disposition", "attachment; filename=labca_"+basename+".zip") - cmd := "zip -j -P " + r.Form.Get("export-pwd") + " - " - var certBase string - if r.Form.Get("root") == "true" { - certBase = "data/root-ca" - cmd = cmd + certBase + ".key " + certBase + ".pem " - } - if r.Form.Get("issuer") == "true" { - certBase = "data/issuer/ca-int" - cmd = cmd + certBase + ".key " + certBase + ".pem " - } + cmd := "zip -j -P " + r.Form.Get("export-pwd") + " - " + var certBase string + if r.Form.Get("root") == "true" { + certBase = "data/root-ca" + cmd = cmd + certBase + ".key " + certBase + ".pem " + } + if r.Form.Get("issuer") == "true" { + certBase = "data/issuer/ca-int" + cmd = cmd + certBase + ".key " + certBase + ".pem " + } - _sendCmdOutput(w, r, cmd) - } + _sendCmdOutput(w, r, cmd) + } } func _doCmdOutput(w http.ResponseWriter, r *http.Request, cmd string) string { - parts := strings.Fields(cmd) - for i := 0; i < len(parts); i++ { - parts[i] = strings.Replace(parts[i], "\\\\", " ", -1) - } - head := parts[0] - parts = parts[1:] + parts := strings.Fields(cmd) + for i := 0; i < len(parts); i++ { + parts[i] = strings.Replace(parts[i], "\\\\", " ", -1) + } + head := parts[0] + parts = parts[1:] - out, err := exec.Command(head, parts...).Output() - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return "" - } + out, err := exec.Command(head, parts...).Output() + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return "" + } - return string(out) + return string(out) } func _encrypt(plaintext []byte) (string, error) { - key := []byte(viper.GetString("keys.enc")) - block, err := aes.NewCipher(key[:32]) - if err != nil { - return "", err - } + key := []byte(viper.GetString("keys.enc")) + block, err := aes.NewCipher(key[:32]) + if err != nil { + return "", err + } - gcm, err := cipher.NewGCM(block) - if err != nil { - return "", err - } + gcm, err := cipher.NewGCM(block) + if err != nil { + return "", err + } - nonce := make([]byte, gcm.NonceSize()) - _, err = io.ReadFull(rand.Reader, nonce) - if err != nil { - return "", err - } + nonce := make([]byte, gcm.NonceSize()) + _, err = io.ReadFull(rand.Reader, nonce) + if err != nil { + return "", err + } - return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, plaintext, nil)), nil + return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, plaintext, nil)), nil } func _decrypt(ciphertext string) ([]byte, error) { - key := []byte(viper.GetString("keys.enc")) - block, err := aes.NewCipher(key[:32]) - if err != nil { - return nil, err - } + key := []byte(viper.GetString("keys.enc")) + block, err := aes.NewCipher(key[:32]) + if err != nil { + return nil, err + } - gcm, err := cipher.NewGCM(block) - if err != nil { - return nil, err - } + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } - ct, err := base64.StdEncoding.DecodeString(ciphertext) - if err != nil { - return nil, err - } + ct, err := base64.StdEncoding.DecodeString(ciphertext) + if err != nil { + return nil, err + } - if len(ct) < gcm.NonceSize() { - return nil, errors.New("malformed ciphertext") - } + if len(ct) < gcm.NonceSize() { + return nil, errors.New("malformed ciphertext") + } - return gcm.Open(nil, ct[:gcm.NonceSize()], ct[gcm.NonceSize():], nil,) + return gcm.Open(nil, ct[:gcm.NonceSize()], ct[gcm.NonceSize():], nil) } func manageHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - action := r.Form.Get("action") - switch action { - case "backup-restore": - case "backup-delete": - case "backup-now": - case "cert-export": - case "nginx-reload": - case "nginx-restart": - case "svc-restart": - case "boulder-start": - case "boulder-stop": - case "boulder-restart": - case "labca-restart": - case "server-restart": - case "server-shutdown": - case "update-account": - case "update-config": - case "update-email": - case "send-email": - default: - errorHandler(w, r, errors.New(fmt.Sprintf("Unknown manage action '%s'", action)), http.StatusBadRequest) - return - } + action := r.Form.Get("action") + switch action { + case "backup-restore": + case "backup-delete": + case "backup-now": + case "cert-export": + case "nginx-reload": + case "nginx-restart": + case "svc-restart": + case "boulder-start": + case "boulder-stop": + case "boulder-restart": + case "labca-restart": + case "server-restart": + case "server-shutdown": + case "update-account": + case "update-config": + case "update-email": + case "send-email": + default: + errorHandler(w, r, errors.New(fmt.Sprintf("Unknown manage action '%s'", action)), http.StatusBadRequest) + return + } - if action == "backup-restore" || action == "backup-delete" || action == "backup-now" { - _backupHandler(w, r) - return - } + if action == "backup-restore" || action == "backup-delete" || action == "backup-now" { + _backupHandler(w, r) + return + } - if action == "cert-export" { - _exportHandler(w, r) - return - } + if action == "cert-export" { + _exportHandler(w, r) + return + } - if action == "update-account" { - _accountUpdateHandler(w, r) - return - } + if action == "update-account" { + _accountUpdateHandler(w, r) + return + } - if action == "update-config" { - _configUpdateHandler(w, r) - return - } + if action == "update-config" { + _configUpdateHandler(w, r) + return + } - if action == "update-email" { - _emailUpdateHandler(w, r) - return - } + if action == "update-email" { + _emailUpdateHandler(w, r) + return + } - if action == "send-email" { - _emailSendHandler(w, r) - return - } + if action == "send-email" { + _emailSendHandler(w, r) + return + } - res := struct { - Success bool - Message string - Timestamp string - TimestampRel string - Class string - }{Success: true} - if !_hostCommand(w, r, action) { - res.Success = false - res.Message = "Command failed - see LabCA log for any details" - } + res := struct { + Success bool + Message string + Timestamp string + TimestampRel string + Class string + }{Success: true} + if !_hostCommand(w, r, action) { + res.Success = false + res.Message = "Command failed - see LabCA log for any details" + } - if action != "server-restart" && action != "server-shutdown" { - components := _parseComponents(getLog(w, r, "components")) - for i := 0; i < len(components); i++ { - if (components[i].Name == "NGINX Webserver" && (action == "nginx-reload" || action == "nginx-restart")) || - (components[i].Name == "Host Service" && action == "svc-restart") || - (components[i].Name == "Boulder (ACME)" && (action == "boulder-start" || action == "boulder-stop" || action == "boulder-restart")) || - (components[i].Name == "LabCA Application" && action == "labca-restart") { - res.Timestamp = components[i].Timestamp - res.TimestampRel = components[i].TimestampRel - res.Class = components[i].Class - break - } - } - } + if action != "server-restart" && action != "server-shutdown" { + components := _parseComponents(getLog(w, r, "components")) + for i := 0; i < len(components); i++ { + if (components[i].Name == "NGINX Webserver" && (action == "nginx-reload" || action == "nginx-restart")) || + (components[i].Name == "Host Service" && action == "svc-restart") || + (components[i].Name == "Boulder (ACME)" && (action == "boulder-start" || action == "boulder-stop" || action == "boulder-restart")) || + (components[i].Name == "LabCA Application" && action == "labca-restart") { + res.Timestamp = components[i].Timestamp + res.TimestampRel = components[i].TimestampRel + res.Class = components[i].Class + break + } + } + } - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) - } else { - manageData := make(map[string]interface{}) - manageData["RequestBase"] = r.Header.Get("X-Request-Base") + } else { + manageData := make(map[string]interface{}) + manageData["RequestBase"] = r.Header.Get("X-Request-Base") - components := _parseComponents(getLog(w, r, "components")) - for i := 0; i < len(components); i++ { - if components[i].Name == "NGINX Webserver" { - components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/weberr" - components[i].LogTitle = "Web Error Log" + components := _parseComponents(getLog(w, r, "components")) + for i := 0; i < len(components); i++ { + if components[i].Name == "NGINX Webserver" { + components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/weberr" + components[i].LogTitle = "Web Error Log" - btn := make(map[string]interface{}) - btn["Class"] = "btn-info" - btn["Id"] = "nginx-reload" - btn["Title"] = "Reload web server configuration with minimal impact to the users" - btn["Label"] = "Reload" - components[i].Buttons = append(components[i].Buttons, btn) + btn := make(map[string]interface{}) + btn["Class"] = "btn-info" + btn["Id"] = "nginx-reload" + btn["Title"] = "Reload web server configuration with minimal impact to the users" + btn["Label"] = "Reload" + components[i].Buttons = append(components[i].Buttons, btn) - btn = make(map[string]interface{}) - btn["Class"] = "btn-warning" - btn["Id"] = "nginx-restart" - btn["Title"] = "Restart the web server with some downtime for the users" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - } + btn = make(map[string]interface{}) + btn["Class"] = "btn-warning" + btn["Id"] = "nginx-restart" + btn["Title"] = "Restart the web server with some downtime for the users" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + } - if components[i].Name == "Host Service" { - components[i].LogUrl = "" - components[i].LogTitle = "" + if components[i].Name == "Host Service" { + components[i].LogUrl = "" + components[i].LogTitle = "" - btn := make(map[string]interface{}) - btn["Class"] = "btn-warning" - btn["Id"] = "svc-restart" - btn["Title"] = "Restart the host service" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - } + btn := make(map[string]interface{}) + btn["Class"] = "btn-warning" + btn["Id"] = "svc-restart" + btn["Title"] = "Restart the host service" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + } - if components[i].Name == "Boulder (ACME)" { - components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/boulder" - components[i].LogTitle = "ACME Log" + if components[i].Name == "Boulder (ACME)" { + components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/boulder" + components[i].LogTitle = "ACME Log" - btn := make(map[string]interface{}) - cls := "btn-success" - if components[i].TimestampRel != "stopped" { - cls = cls + " hidden" - } - btn["Class"] = cls - btn["Id"] = "boulder-start" - btn["Title"] = "Start the core ACME application" - btn["Label"] = "Start" - components[i].Buttons = append(components[i].Buttons, btn) + btn := make(map[string]interface{}) + cls := "btn-success" + if components[i].TimestampRel != "stopped" { + cls = cls + " hidden" + } + btn["Class"] = cls + btn["Id"] = "boulder-start" + btn["Title"] = "Start the core ACME application" + btn["Label"] = "Start" + components[i].Buttons = append(components[i].Buttons, btn) - btn = make(map[string]interface{}) - cls = "btn-warning" - if components[i].TimestampRel == "stopped" { - cls = cls + " hidden" - } - btn["Class"] = cls - btn["Id"] = "boulder-restart" - btn["Title"] = "Stop and restart the core ACME application" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) + btn = make(map[string]interface{}) + cls = "btn-warning" + if components[i].TimestampRel == "stopped" { + cls = cls + " hidden" + } + btn["Class"] = cls + btn["Id"] = "boulder-restart" + btn["Title"] = "Stop and restart the core ACME application" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) - btn = make(map[string]interface{}) - cls = "btn-danger" - if components[i].TimestampRel == "stopped" { - cls = cls + " hidden" - } - btn["Class"] = cls - btn["Id"] = "boulder-stop" - btn["Title"] = "Stop the core ACME application; users can no longer use ACME clients to interact with this instance" - btn["Label"] = "Stop" - components[i].Buttons = append(components[i].Buttons, btn) - } + btn = make(map[string]interface{}) + cls = "btn-danger" + if components[i].TimestampRel == "stopped" { + cls = cls + " hidden" + } + btn["Class"] = cls + btn["Id"] = "boulder-stop" + btn["Title"] = "Stop the core ACME application; users can no longer use ACME clients to interact with this instance" + btn["Label"] = "Stop" + components[i].Buttons = append(components[i].Buttons, btn) + } - if components[i].Name == "LabCA Application" { - components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/labca" - components[i].LogTitle = "LabCA Log" + if components[i].Name == "LabCA Application" { + components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/labca" + components[i].LogTitle = "LabCA Log" - btn := make(map[string]interface{}) - btn["Class"] = "btn-warning" - btn["Id"] = "labca-restart" - btn["Title"] = "Stop and restart this LabCA admin application" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - } - } - manageData["Components"] = components + btn := make(map[string]interface{}) + btn["Class"] = "btn-warning" + btn["Id"] = "labca-restart" + btn["Title"] = "Stop and restart this LabCA admin application" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + } + } + manageData["Components"] = components - stats := _parseStats(getLog(w, r, "stats")) - for _, stat := range stats { - if stat.Name == "System Uptime" { - manageData["ServerTimestamp"] = stat.Hint - manageData["ServerTimestampRel"] = stat.Value - break - } - } + stats := _parseStats(getLog(w, r, "stats")) + for _, stat := range stats { + if stat.Name == "System Uptime" { + manageData["ServerTimestamp"] = stat.Hint + manageData["ServerTimestampRel"] = stat.Value + break + } + } - backupFiles := strings.Split(getLog(w, r, "backups"), "\n") - backupFiles = backupFiles[:len(backupFiles)-1] - manageData["BackupFiles"] = backupFiles + backupFiles := strings.Split(getLog(w, r, "backups"), "\n") + backupFiles = backupFiles[:len(backupFiles)-1] + manageData["BackupFiles"] = backupFiles - manageData["RootDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/root-ca.pem") - manageData["IssuerDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/issuer/ca-int.pem") + manageData["RootDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/root-ca.pem") + manageData["IssuerDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/issuer/ca-int.pem") - manageData["Fqdn"] = viper.GetString("labca.fqdn") - manageData["Organization"] = viper.GetString("labca.organization") - manageData["Dns"] = viper.GetString("labca.dns") - domain_mode := viper.GetString("labca.domain_mode") - manageData["DomainMode"] = domain_mode - if domain_mode == "lockdown" { - manageData["LockdownDomains"] = viper.GetString("labca.lockdown") - } - if domain_mode == "whitelist" { - manageData["WhitelistDomains"] = viper.GetString("labca.whitelist") - } + manageData["Fqdn"] = viper.GetString("labca.fqdn") + manageData["Organization"] = viper.GetString("labca.organization") + manageData["Dns"] = viper.GetString("labca.dns") + domain_mode := viper.GetString("labca.domain_mode") + manageData["DomainMode"] = domain_mode + if domain_mode == "lockdown" { + manageData["LockdownDomains"] = viper.GetString("labca.lockdown") + } + if domain_mode == "whitelist" { + manageData["WhitelistDomains"] = viper.GetString("labca.whitelist") + } - manageData["DoEmail"] = viper.GetBool("labca.email.enable") - manageData["Server"] = viper.GetString("labca.email.server") - manageData["Port"] = viper.GetInt("labca.email.port") - manageData["EmailUser"] = viper.GetString("labca.email.user") - manageData["EmailPwd"] = "" - if viper.Get("labca.email.pass") != nil { - pwd := viper.GetString("labca.email.pass") - result, err := _decrypt(pwd) - if err == nil { - manageData["EmailPwd"] = string(result) - } else { - log.Printf("WARNING: could not decrypt email password: %s!\n", err.Error()) - } - } - manageData["From"] = viper.GetString("labca.email.from") + manageData["DoEmail"] = viper.GetBool("labca.email.enable") + manageData["Server"] = viper.GetString("labca.email.server") + manageData["Port"] = viper.GetInt("labca.email.port") + manageData["EmailUser"] = viper.GetString("labca.email.user") + manageData["EmailPwd"] = "" + if viper.Get("labca.email.pass") != nil { + pwd := viper.GetString("labca.email.pass") + result, err := _decrypt(pwd) + if err == nil { + manageData["EmailPwd"] = string(result) + } else { + log.Printf("WARNING: could not decrypt email password: %s!\n", err.Error()) + } + } + manageData["From"] = viper.GetString("labca.email.from") - manageData["Name"] = viper.GetString("user.name") - manageData["Email"] = viper.GetString("user.email") + manageData["Name"] = viper.GetString("user.name") + manageData["Email"] = viper.GetString("user.email") - render(w, r, "manage", manageData) - } + render(w, r, "manage", manageData) + } } func logsHandler(w http.ResponseWriter, r *http.Request) { - vars := mux.Vars(r) - logType := vars["type"] + vars := mux.Vars(r) + logType := vars["type"] - proto := "ws" - if r.Header.Get("X-Forwarded-Proto") == "https" { - proto = "wss" - } + proto := "ws" + if r.Header.Get("X-Forwarded-Proto") == "https" { + proto = "wss" + } - wsurl := proto + "://" + r.Host + r.Header.Get("X-Request-Base") + "/ws?logType=" + logType + wsurl := proto + "://" + r.Host + r.Header.Get("X-Request-Base") + "/ws?logType=" + logType - var name string - var message string - var data string + var name string + var message string + var data string - switch logType { - case "cert": - name = "Web Certificate Log" - message = "Log file for the certificate renewal for this server." - wsurl = "" - data = getLog(w, r, logType) - case "boulder": - name = "ACME Backend Log" - message = "Live view on the backend ACME application (Boulder) logs." - case "audit": - name = "ACME Audit Log" - message = "Live view on only the audit messages in the backend ACME application (Boulder) logs." - case "labca": - name = "LabCA Log" - message = "Live view on the logs for this LabCA web application." - case "web": - name = "Web Access Log" - message = "Live view on the NGINX web server access log." - case "weberr": - name = "Web Error Log" - message = "Log file for the NGINX web server error log." - wsurl = "" - data = getLog(w, r, logType) - default: - errorHandler(w, r, errors.New(fmt.Sprintf("Unknown log type '%s'", logType)), http.StatusBadRequest) - return - } + switch logType { + case "cert": + name = "Web Certificate Log" + message = "Log file for the certificate renewal for this server." + wsurl = "" + data = getLog(w, r, logType) + case "boulder": + name = "ACME Backend Log" + message = "Live view on the backend ACME application (Boulder) logs." + case "audit": + name = "ACME Audit Log" + message = "Live view on only the audit messages in the backend ACME application (Boulder) logs." + case "labca": + name = "LabCA Log" + message = "Live view on the logs for this LabCA web application." + case "web": + name = "Web Access Log" + message = "Live view on the NGINX web server access log." + case "weberr": + name = "Web Error Log" + message = "Log file for the NGINX web server error log." + wsurl = "" + data = getLog(w, r, logType) + default: + errorHandler(w, r, errors.New(fmt.Sprintf("Unknown log type '%s'", logType)), http.StatusBadRequest) + return + } - render(w, r, "logs", map[string]interface{}{ - "Name": name, - "Message": message, - "Data": data, - "WsUrl": wsurl, - }) + render(w, r, "logs", map[string]interface{}{ + "Name": name, + "Message": message, + "Data": data, + "WsUrl": wsurl, + }) } func getLog(w http.ResponseWriter, r *http.Request, logType string) string { - ip, err := _discoverGateway() - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return "" - } + ip, err := _discoverGateway() + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return "" + } - conn, err := net.Dial("tcp", ip.String()+":3030") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return "" - } + conn, err := net.Dial("tcp", ip.String()+":3030") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return "" + } - defer conn.Close() + defer conn.Close() - fmt.Fprintf(conn, "log-"+logType+"\n") - reader := bufio.NewReader(conn) - contents, err := ioutil.ReadAll(reader) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return "" - } + fmt.Fprintf(conn, "log-"+logType+"\n") + reader := bufio.NewReader(conn) + contents, err := ioutil.ReadAll(reader) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return "" + } - return string(contents) + return string(contents) } func wsErrorHandler(err error) { - log.Printf("wsErrorHandler: %v", err) + log.Printf("wsErrorHandler: %v", err) - pc := make([]uintptr, 15) - n := runtime.Callers(2, pc) - frames := runtime.CallersFrames(pc[:n]) - frame, _ := frames.Next() - fmt.Printf("%s:%d, %s\n", frame.File, frame.Line, frame.Function) + pc := make([]uintptr, 15) + n := runtime.Callers(2, pc) + frames := runtime.CallersFrames(pc[:n]) + frame, _ := frames.Next() + fmt.Printf("%s:%d, %s\n", frame.File, frame.Line, frame.Function) - debug.PrintStack() + debug.PrintStack() } func showLog(ws *websocket.Conn, logType string) { - ip, err := _discoverGateway() - if err != nil { - wsErrorHandler(err) - return - } + ip, err := _discoverGateway() + if err != nil { + wsErrorHandler(err) + return + } - conn, err := net.Dial("tcp", ip.String()+":3030") - if err != nil { - wsErrorHandler(err) - return - } + conn, err := net.Dial("tcp", ip.String()+":3030") + if err != nil { + wsErrorHandler(err) + return + } - defer conn.Close() + defer conn.Close() - fmt.Fprintf(conn, "log-"+logType+"\n") - scanner := bufio.NewScanner(conn) - for scanner.Scan() { - msg := scanner.Text() - if logType != "audit" || strings.Index(msg, "[AUDIT]") > -1 { - ws.SetWriteDeadline(time.Now().Add(writeWait)) - if err := ws.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil { - // Probably "websocket: close sent" - return - } - } - } - if err := scanner.Err(); err != nil { - wsErrorHandler(err) - return - } + fmt.Fprintf(conn, "log-"+logType+"\n") + scanner := bufio.NewScanner(conn) + for scanner.Scan() { + msg := scanner.Text() + if logType != "audit" || strings.Index(msg, "[AUDIT]") > -1 { + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil { + // Probably "websocket: close sent" + return + } + } + } + if err := scanner.Err(); err != nil { + wsErrorHandler(err) + return + } - return + return } func reader(ws *websocket.Conn) { - defer ws.Close() - ws.SetReadLimit(512) - ws.SetReadDeadline(time.Now().Add(pongWait)) - ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) - for { - _, _, err := ws.ReadMessage() - if err != nil { - break - } - } + defer ws.Close() + ws.SetReadLimit(512) + ws.SetReadDeadline(time.Now().Add(pongWait)) + ws.SetPongHandler(func(string) error { ws.SetReadDeadline(time.Now().Add(pongWait)); return nil }) + for { + _, _, err := ws.ReadMessage() + if err != nil { + break + } + } } func writer(ws *websocket.Conn, logType string) { - pingTicker := time.NewTicker(pingPeriod) - defer func() { - pingTicker.Stop() - ws.Close() - }() + pingTicker := time.NewTicker(pingPeriod) + defer func() { + pingTicker.Stop() + ws.Close() + }() - go showLog(ws, logType) + go showLog(ws, logType) - for { - select { - case <-pingTicker.C: - ws.SetWriteDeadline(time.Now().Add(writeWait)) - if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { - // Probably "websocket: close sent" - return - } - } - } + for { + select { + case <-pingTicker.C: + ws.SetWriteDeadline(time.Now().Add(writeWait)) + if err := ws.WriteMessage(websocket.PingMessage, []byte{}); err != nil { + // Probably "websocket: close sent" + return + } + } + } } func wsHandler(w http.ResponseWriter, r *http.Request) { - ws, err := upgrader.Upgrade(w, r, nil) - if err != nil { - if _, ok := err.(websocket.HandshakeError); !ok { - log.Println(err) - } - return - } + ws, err := upgrader.Upgrade(w, r, nil) + if err != nil { + if _, ok := err.(websocket.HandshakeError); !ok { + log.Println(err) + } + return + } - logType := r.FormValue("logType") + logType := r.FormValue("logType") - switch logType { - case "boulder": - case "audit": - case "labca": - case "web": - default: - errorHandler(w, r, errors.New(fmt.Sprintf("Unknown log type '%s'", logType)), http.StatusBadRequest) - return - } + switch logType { + case "boulder": + case "audit": + case "labca": + case "web": + default: + errorHandler(w, r, errors.New(fmt.Sprintf("Unknown log type '%s'", logType)), http.StatusBadRequest) + return + } - go writer(ws, logType) - reader(ws) + go writer(ws, logType) + reader(ws) } func _certCreate(w http.ResponseWriter, r *http.Request, certBase string, isRoot bool) bool { - path := "data/" - if !isRoot { - path = path + "issuer/" - } + path := "data/" + if !isRoot { + path = path + "issuer/" + } - if _, err := os.Stat(path + certBase + ".pem"); os.IsNotExist(err) { - session := getSession(w, r) + if _, err := os.Stat(path + certBase + ".pem"); os.IsNotExist(err) { + session := getSession(w, r) - if r.Method == "GET" { - ci := &CertificateInfo{ - IsRoot: isRoot, - CreateType: "generate", - CommonName: "Root CA", - RequestBase: r.Header.Get("X-Request-Base"), - } - if !isRoot { - ci.CommonName = "CA" - } - ci.Initialize() + if r.Method == "GET" { + ci := &CertificateInfo{ + IsRoot: isRoot, + CreateType: "generate", + CommonName: "Root CA", + RequestBase: r.Header.Get("X-Request-Base"), + } + if !isRoot { + ci.CommonName = "CA" + } + ci.Initialize() - if session.Values["ct"] != nil { - ci.CreateType = session.Values["ct"].(string) - } - if session.Values["kt"] != nil { - ci.KeyType = session.Values["kt"].(string) - } - if session.Values["c"] != nil { - ci.Country = session.Values["c"].(string) - } - if session.Values["o"] != nil { - ci.Organization = session.Values["o"].(string) - } - if session.Values["ou"] != nil { - ci.OrgUnit = session.Values["ou"].(string) - } - if session.Values["cn"] != nil { - ci.CommonName = session.Values["cn"].(string) - ci.CommonName = strings.Replace(ci.CommonName, "Root", "", -1) - ci.CommonName = strings.Replace(ci.CommonName, " ", " ", -1) - } + if session.Values["ct"] != nil { + ci.CreateType = session.Values["ct"].(string) + } + if session.Values["kt"] != nil { + ci.KeyType = session.Values["kt"].(string) + } + if session.Values["c"] != nil { + ci.Country = session.Values["c"].(string) + } + if session.Values["o"] != nil { + ci.Organization = session.Values["o"].(string) + } + if session.Values["ou"] != nil { + ci.OrgUnit = session.Values["ou"].(string) + } + if session.Values["cn"] != nil { + ci.CommonName = session.Values["cn"].(string) + ci.CommonName = strings.Replace(ci.CommonName, "Root", "", -1) + ci.CommonName = strings.Replace(ci.CommonName, " ", " ", -1) + } - render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) - return false - } else if r.Method == "POST" { - if err := r.ParseMultipartForm(2 * 1024 * 1024); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return false - } + render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) + return false + } else if r.Method == "POST" { + if err := r.ParseMultipartForm(2 * 1024 * 1024); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } - ci := &CertificateInfo{} - ci.Initialize() - ci.IsRoot = r.Form.Get("cert") == "root" - ci.CreateType = r.Form.Get("createtype") + ci := &CertificateInfo{} + ci.Initialize() + ci.IsRoot = r.Form.Get("cert") == "root" + ci.CreateType = r.Form.Get("createtype") - if r.Form.Get("keytype") != "" { - ci.KeyType = r.Form.Get("keytype") - } - ci.Country = r.Form.Get("c") - ci.Organization = r.Form.Get("o") - ci.OrgUnit = r.Form.Get("ou") - ci.CommonName = r.Form.Get("cn") + if r.Form.Get("keytype") != "" { + ci.KeyType = r.Form.Get("keytype") + } + ci.Country = r.Form.Get("c") + ci.Organization = r.Form.Get("o") + ci.OrgUnit = r.Form.Get("ou") + ci.CommonName = r.Form.Get("cn") - if ci.CreateType == "import" { - file, handler, err := r.FormFile("import") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return false - } + if ci.CreateType == "import" { + file, handler, err := r.FormFile("import") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } - defer file.Close() + defer file.Close() - ci.ImportFile = file - ci.ImportHandler = handler - ci.ImportPwd = r.Form.Get("import-pwd") - } + ci.ImportFile = file + ci.ImportHandler = handler + ci.ImportPwd = r.Form.Get("import-pwd") + } - ci.Key = r.Form.Get("key") - ci.Passphrase = r.Form.Get("passphrase") - ci.Certificate = r.Form.Get("certificate") - ci.RequestBase = r.Header.Get("X-Request-Base") + ci.Key = r.Form.Get("key") + ci.Passphrase = r.Form.Get("passphrase") + ci.Certificate = r.Form.Get("certificate") + ci.RequestBase = r.Header.Get("X-Request-Base") - if ci.Validate() == false { - render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) - return false - } + if ci.Validate() == false { + render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) + return false + } - if err := ci.Create(path, certBase); err != nil { - ci.Errors[strings.Title(ci.CreateType)] = err.Error() - log.Printf("_certCreate: create failed: %v", err) - render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) - return false - } + if err := ci.Create(path, certBase); err != nil { + ci.Errors[strings.Title(ci.CreateType)] = err.Error() + log.Printf("_certCreate: create failed: %v", err) + render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) + return false + } - if viper.Get("labca.organization") == nil { - viper.Set("labca.organization", ci.Organization) - viper.WriteConfig() - } + if viper.Get("labca.organization") == nil { + viper.Set("labca.organization", ci.Organization) + viper.WriteConfig() + } - session.Values["ct"] = ci.CreateType - session.Values["kt"] = ci.KeyType - session.Values["c"] = ci.Country - session.Values["o"] = ci.Organization - session.Values["ou"] = ci.OrgUnit - session.Values["cn"] = ci.CommonName - session.Save(r, w) + session.Values["ct"] = ci.CreateType + session.Values["kt"] = ci.KeyType + session.Values["c"] = ci.Country + session.Values["o"] = ci.Organization + session.Values["ou"] = ci.OrgUnit + session.Values["cn"] = ci.CommonName + session.Save(r, w) - // Fake the method to GET as we need to continue in the setupHandler() function - r.Method = "GET" - } else { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) - return false - } - } + // Fake the method to GET as we need to continue in the setupHandler() function + r.Method = "GET" + } else { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + return false + } + } - return true + return true } func _parseLinuxIPRouteShow(output []byte) (net.IP, error) { - // Linux '/usr/bin/ip route show' format looks like this: - // default via 192.168.178.1 dev wlp3s0 metric 303 - // 192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303 - lines := strings.Split(string(output), "\n") - for _, line := range lines { - fields := strings.Fields(line) - if len(fields) >= 3 && fields[0] == "default" { - ip := net.ParseIP(fields[2]) - if ip != nil { - return ip, nil - } - } - } + // Linux '/usr/bin/ip route show' format looks like this: + // default via 192.168.178.1 dev wlp3s0 metric 303 + // 192.168.178.0/24 dev wlp3s0 proto kernel scope link src 192.168.178.76 metric 303 + lines := strings.Split(string(output), "\n") + for _, line := range lines { + fields := strings.Fields(line) + if len(fields) >= 3 && fields[0] == "default" { + ip := net.ParseIP(fields[2]) + if ip != nil { + return ip, nil + } + } + } - return nil, errors.New("no gateway found") + return nil, errors.New("no gateway found") } func _discoverGateway() (net.IP, error) { - if isDev { - ip := net.ParseIP("127.0.0.1") - if ip != nil { - return ip, nil - } - } + if isDev { + ip := net.ParseIP("127.0.0.1") + if ip != nil { + return ip, nil + } + } - routeCmd := exec.Command("ip", "route", "show") - output, err := routeCmd.CombinedOutput() - if err != nil { - return nil, err - } + routeCmd := exec.Command("ip", "route", "show") + output, err := routeCmd.CombinedOutput() + if err != nil { + return nil, err + } - return _parseLinuxIPRouteShow(output) + return _parseLinuxIPRouteShow(output) } func _hostCommand(w http.ResponseWriter, r *http.Request, command string, params ...string) bool { - ip, err := _discoverGateway() - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return false - } + ip, err := _discoverGateway() + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } - conn, err := net.Dial("tcp", ip.String()+":3030") - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return false - } + conn, err := net.Dial("tcp", ip.String()+":3030") + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } - defer conn.Close() + defer conn.Close() - fmt.Fprintf(conn, command+"\n") - for _, param := range params { - fmt.Fprintf(conn, param+"\n") - } + fmt.Fprintf(conn, command+"\n") + for _, param := range params { + fmt.Fprintf(conn, param+"\n") + } - reader := bufio.NewReader(conn) - message, err := ioutil.ReadAll(reader) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return false - } + reader := bufio.NewReader(conn) + message, err := ioutil.ReadAll(reader) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } - if strings.Compare(string(message), "ok\n") == 0 { - return true - } + if strings.Compare(string(message), "ok\n") == 0 { + return true + } - tail := message[len(message)-4:] - if strings.Compare(string(tail), "\nok\n") == 0 { - msg := message[0 : len(message)-4] - log.Printf("Message from server: '%s'", msg) - return true - } + tail := message[len(message)-4:] + if strings.Compare(string(tail), "\nok\n") == 0 { + msg := message[0 : len(message)-4] + log.Printf("Message from server: '%s'", msg) + return true + } - log.Printf("ERROR: Message from server: '%s'", message) - errorHandler(w, r, errors.New(string(message)), http.StatusInternalServerError) - return false + log.Printf("ERROR: Message from server: '%s'", message) + errorHandler(w, r, errors.New(string(message)), http.StatusInternalServerError) + return false } func randToken() string { - b := make([]byte, 8) - rand.Read(b) - return fmt.Sprintf("%x", b) + b := make([]byte, 8) + rand.Read(b) + return fmt.Sprintf("%x", b) } func _applyConfig() error { - os.Setenv("PKI_ROOT_CERT_BASE", "data/root-ca") - os.Setenv("PKI_INT_CERT_BASE", "data/issuer/ca-int") - os.Setenv("PKI_DEFAULT_O", viper.GetString("labca.organization")) - os.Setenv("PKI_DNS", viper.GetString("labca.dns")) - domain := viper.GetString("labca.fqdn") - os.Setenv("PKI_FQDN", domain) - pos := strings.Index(domain, ".") - if pos > -1 { - pos = pos + 1 - domain = domain[pos:] - } - os.Setenv("PKI_DOMAIN", domain) - os.Setenv("PKI_DOMAIN_MODE", viper.GetString("labca.domain_mode")) - os.Setenv("PKI_LOCKDOWN_DOMAINS", viper.GetString("labca.lockdown")) - os.Setenv("PKI_WHITELIST_DOMAINS", viper.GetString("labca.whitelist")) - if viper.GetBool("labca.email.enable") { - os.Setenv("PKI_EMAIL_SERVER", viper.GetString("labca.email.server")) - os.Setenv("PKI_EMAIL_PORT", viper.GetString("labca.email.port")) - os.Setenv("PKI_EMAIL_USER", viper.GetString("labca.email.user")) - res, err := _decrypt(viper.GetString("labca.email.pass")) - if err != nil { - log.Println("WARNING: could not decrypt stored password: " + err.Error()) - } - os.Setenv("PKI_EMAIL_PASS", string(res)) - os.Setenv("PKI_EMAIL_FROM", viper.GetString("labca.email.from")) - } else { - os.Setenv("PKI_EMAIL_SERVER", "localhost") - os.Setenv("PKI_EMAIL_PORT", "9380") - os.Setenv("PKI_EMAIL_USER", "cert-master@example.com") - os.Setenv("PKI_EMAIL_PASS", "password") - os.Setenv("PKI_EMAIL_FROM", "Expiry bot ") - } + os.Setenv("PKI_ROOT_CERT_BASE", "data/root-ca") + os.Setenv("PKI_INT_CERT_BASE", "data/issuer/ca-int") + os.Setenv("PKI_DEFAULT_O", viper.GetString("labca.organization")) + os.Setenv("PKI_DNS", viper.GetString("labca.dns")) + domain := viper.GetString("labca.fqdn") + os.Setenv("PKI_FQDN", domain) + pos := strings.Index(domain, ".") + if pos > -1 { + pos = pos + 1 + domain = domain[pos:] + } + os.Setenv("PKI_DOMAIN", domain) + os.Setenv("PKI_DOMAIN_MODE", viper.GetString("labca.domain_mode")) + os.Setenv("PKI_LOCKDOWN_DOMAINS", viper.GetString("labca.lockdown")) + os.Setenv("PKI_WHITELIST_DOMAINS", viper.GetString("labca.whitelist")) + if viper.GetBool("labca.email.enable") { + os.Setenv("PKI_EMAIL_SERVER", viper.GetString("labca.email.server")) + os.Setenv("PKI_EMAIL_PORT", viper.GetString("labca.email.port")) + os.Setenv("PKI_EMAIL_USER", viper.GetString("labca.email.user")) + res, err := _decrypt(viper.GetString("labca.email.pass")) + if err != nil { + log.Println("WARNING: could not decrypt stored password: " + err.Error()) + } + os.Setenv("PKI_EMAIL_PASS", string(res)) + os.Setenv("PKI_EMAIL_FROM", viper.GetString("labca.email.from")) + } else { + os.Setenv("PKI_EMAIL_SERVER", "localhost") + os.Setenv("PKI_EMAIL_PORT", "9380") + os.Setenv("PKI_EMAIL_USER", "cert-master@example.com") + os.Setenv("PKI_EMAIL_PASS", "password") + os.Setenv("PKI_EMAIL_FROM", "Expiry bot ") + } - _, err := exe_cmd("./apply") - if err != nil { - fmt.Println("") - } - return err + _, err := exe_cmd("./apply") + if err != nil { + fmt.Println("") + } + return err } func _progress(stage string) int { - max := 20.0 / 100.0 - curr := 1.0 + max := 20.0 / 100.0 + curr := 1.0 - if stage == "register" { - return int(math.Round(curr / max)) - } else { - curr += 2.0 - } + if stage == "register" { + return int(math.Round(curr / max)) + } else { + curr += 2.0 + } - if stage == "setup" { - return int(math.Round(curr / max)) - } else { - curr += 3.0 - } + if stage == "setup" { + return int(math.Round(curr / max)) + } else { + curr += 3.0 + } - if stage == "root-ca" { - return int(math.Round(curr / max)) - } else { - curr += 4.0 - } + if stage == "root-ca" { + return int(math.Round(curr / max)) + } else { + curr += 4.0 + } - if stage == "ca-int" { - return int(math.Round(curr / max)) - } else { - curr += 3.0 - } + if stage == "ca-int" { + return int(math.Round(curr / max)) + } else { + curr += 3.0 + } - if stage == "polling" { - return int(math.Round(curr / max)) - } else { - curr += 4.0 - } + if stage == "polling" { + return int(math.Round(curr / max)) + } else { + curr += 4.0 + } - if stage == "wrapup" { - return int(math.Round(curr / max)) - } else { - curr += 3.0 - } + if stage == "wrapup" { + return int(math.Round(curr / max)) + } else { + curr += 3.0 + } - if stage == "final" { - return int(math.Round(curr / max)) - } else { - return 0 - } + if stage == "final" { + return int(math.Round(curr / max)) + } else { + return 0 + } } func _helptext(stage string) template.HTML { - if stage == "register" { - return template.HTML(fmt.Sprint("

You need to create an admin account for managing this instance of\n", - "LabCA. There can only be one admin account, but you can configure all its attributes once the\n", - "initial setup has completed.

")) - } else if stage == "setup" { - return template.HTML(fmt.Sprint("

The fully qualified domain name (FQDN) is what end users will use\n", - "to connect to this server. It was provided in the initial setup and is shown here for reference.

\n", - "

Please fill in a DNS server (and optionally port, default is ':53') that will be used to lookup\n", - "the domains for which a certificate is requested.

\n", - "

LabCA is primarily intended for use inside an organization where all domains end in the same\n", - "domain, e.g. '.localdomain'. In lockdown mode only those domains are allowed. In whitelist mode\n", - "those domains are allowed next to all official, internet accessible domains and in standard\n", - "mode only the official domains are allowed.

")) - } else if stage == "root-ca" { - return template.HTML(fmt.Sprint("

This is the top level certificate that will sign the issuer\n", - "certificate(s). You can either generate a fresh Root CA (Certificate Authority) or import an\n", - "existing one, e.g. a backup from another LabCA instance.

\n", - "

If you want to generate a certificate, pick a key type and strength (the higher the number the\n", - "more secure, ECDSA is more modern than RSA), provide at least a country and organization name,\n", - "and the common name. It is recommended that the common name contains the word 'Root' as well\n", - "as your organization name so you can recognize it, and that's why that is automatically filled\n", - "once you leave the organization field.

")) - } else if stage == "ca-int" { - return template.HTML(fmt.Sprint("

This is what end users will see as the issuing certificate. Again,\n", - "you can either generate a fresh certificate or import an existing one, as long as it is signed by\n", - "the Root CA from the previous step.

\n", - "

If you want to generate a certificate, by default the same key type and strength is selected as\n", - "was choosen in the previous step when generating the root, but you may choose a different one. By\n", - "default the common name is the same as the CN for the Root CA, minus the word 'Root'.

")) - } else { - return template.HTML("") - } + if stage == "register" { + return template.HTML(fmt.Sprint("

You need to create an admin account for managing this instance of\n", + "LabCA. There can only be one admin account, but you can configure all its attributes once the\n", + "initial setup has completed.

")) + } else if stage == "setup" { + return template.HTML(fmt.Sprint("

The fully qualified domain name (FQDN) is what end users will use\n", + "to connect to this server. It was provided in the initial setup and is shown here for reference.

\n", + "

Please fill in a DNS server (and optionally port, default is ':53') that will be used to lookup\n", + "the domains for which a certificate is requested.

\n", + "

LabCA is primarily intended for use inside an organization where all domains end in the same\n", + "domain, e.g. '.localdomain'. In lockdown mode only those domains are allowed. In whitelist mode\n", + "those domains are allowed next to all official, internet accessible domains and in standard\n", + "mode only the official domains are allowed.

")) + } else if stage == "root-ca" { + return template.HTML(fmt.Sprint("

This is the top level certificate that will sign the issuer\n", + "certificate(s). You can either generate a fresh Root CA (Certificate Authority) or import an\n", + "existing one, e.g. a backup from another LabCA instance.

\n", + "

If you want to generate a certificate, pick a key type and strength (the higher the number the\n", + "more secure, ECDSA is more modern than RSA), provide at least a country and organization name,\n", + "and the common name. It is recommended that the common name contains the word 'Root' as well\n", + "as your organization name so you can recognize it, and that's why that is automatically filled\n", + "once you leave the organization field.

")) + } else if stage == "ca-int" { + return template.HTML(fmt.Sprint("

This is what end users will see as the issuing certificate. Again,\n", + "you can either generate a fresh certificate or import an existing one, as long as it is signed by\n", + "the Root CA from the previous step.

\n", + "

If you want to generate a certificate, by default the same key type and strength is selected as\n", + "was choosen in the previous step when generating the root, but you may choose a different one. By\n", + "default the common name is the same as the CN for the Root CA, minus the word 'Root'.

")) + } else { + return template.HTML("") + } } func setupHandler(w http.ResponseWriter, r *http.Request) { - if viper.GetBool("config.complete") == true { - render(w, r, "index:manage", map[string]interface{}{"Message": template.HTML("Setup already completed! Go home")}) - return - } + if viper.GetBool("config.complete") == true { + render(w, r, "index:manage", map[string]interface{}{"Message": template.HTML("Setup already completed! Go home")}) + return + } - // 1. Setup admin user - if viper.Get("user.password") == nil { - if r.Method == "GET" { - reg := &User{ - RequestBase: r.Header.Get("X-Request-Base"), - } - render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) - return - } else if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + // 1. Setup admin user + if viper.Get("user.password") == nil { + if r.Method == "GET" { + reg := &User{ + RequestBase: r.Header.Get("X-Request-Base"), + } + render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) + return + } else if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - reg := &User{ - Name: r.Form.Get("username"), - Email: r.Form.Get("email"), - Password: r.Form.Get("password"), - Confirm: r.Form.Get("confirm"), - RequestBase: r.Header.Get("X-Request-Base"), - } + reg := &User{ + Name: r.Form.Get("username"), + Email: r.Form.Get("email"), + Password: r.Form.Get("password"), + Confirm: r.Form.Get("confirm"), + RequestBase: r.Header.Get("X-Request-Base"), + } - if reg.Validate(true, false) == false { - render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) - return - } + if reg.Validate(true, false) == false { + render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) + return + } - hash, err := bcrypt.GenerateFromPassword([]byte(reg.Password), bcrypt.MinCost) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } - viper.Set("user.name", reg.Name) - viper.Set("user.email", reg.Email) - viper.Set("user.password", string(hash)) - viper.WriteConfig() + hash, err := bcrypt.GenerateFromPassword([]byte(reg.Password), bcrypt.MinCost) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } + viper.Set("user.name", reg.Name) + viper.Set("user.email", reg.Email) + viper.Set("user.password", string(hash)) + viper.WriteConfig() - session := getSession(w, r) - session.Values["user"] = reg.Name - session.Save(r, w) + session := getSession(w, r) + session.Values["user"] = reg.Name + session.Save(r, w) - // Fake the method to GET as we need to continue in the setupHandler() function - r.Method = "GET" - } else { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) - return - } - } + // Fake the method to GET as we need to continue in the setupHandler() function + r.Method = "GET" + } else { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + return + } + } - // 2. Setup essential configuration - if viper.Get("labca.dns") == nil { - if r.Method == "GET" { - domain := viper.GetString("labca.fqdn") - pos := strings.Index(domain, ".") - if pos > -1 { - pos = pos + 1 - domain = domain[pos:] - } + // 2. Setup essential configuration + if viper.Get("labca.dns") == nil { + if r.Method == "GET" { + domain := viper.GetString("labca.fqdn") + pos := strings.Index(domain, ".") + if pos > -1 { + pos = pos + 1 + domain = domain[pos:] + } - cfg := &SetupConfig{ - Fqdn: viper.GetString("labca.fqdn"), - DomainMode: "lockdown", - LockdownDomains: domain, - WhitelistDomains: domain, - RequestBase: r.Header.Get("X-Request-Base"), - } + cfg := &SetupConfig{ + Fqdn: viper.GetString("labca.fqdn"), + DomainMode: "lockdown", + LockdownDomains: domain, + WhitelistDomains: domain, + RequestBase: r.Header.Get("X-Request-Base"), + } - render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) - return - } else if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) + return + } else if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - cfg := &SetupConfig{ - Fqdn: r.Form.Get("fqdn"), - Dns: r.Form.Get("dns"), - DomainMode: r.Form.Get("domain_mode"), - LockdownDomains: r.Form.Get("lockdown_domains"), - WhitelistDomains: r.Form.Get("whitelist_domains"), - RequestBase: r.Header.Get("X-Request-Base"), - } + cfg := &SetupConfig{ + Fqdn: r.Form.Get("fqdn"), + Dns: r.Form.Get("dns"), + DomainMode: r.Form.Get("domain_mode"), + LockdownDomains: r.Form.Get("lockdown_domains"), + WhitelistDomains: r.Form.Get("whitelist_domains"), + RequestBase: r.Header.Get("X-Request-Base"), + } - if cfg.Validate(false) == false { - render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) - return - } + if cfg.Validate(false) == false { + render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) + return + } - matched, err := regexp.MatchString(":\\d+$", cfg.Dns) - if err == nil && !matched { - cfg.Dns += ":53" - } + matched, err := regexp.MatchString(":\\d+$", cfg.Dns) + if err == nil && !matched { + cfg.Dns += ":53" + } - viper.Set("labca.fqdn", cfg.Fqdn) - viper.Set("labca.dns", cfg.Dns) - viper.Set("labca.domain_mode", cfg.DomainMode) - if cfg.DomainMode == "lockdown" { - viper.Set("labca.lockdown", cfg.LockdownDomains) - } - if cfg.DomainMode == "whitelist" { - viper.Set("labca.whitelist", cfg.WhitelistDomains) - } - viper.WriteConfig() + viper.Set("labca.fqdn", cfg.Fqdn) + viper.Set("labca.dns", cfg.Dns) + viper.Set("labca.domain_mode", cfg.DomainMode) + if cfg.DomainMode == "lockdown" { + viper.Set("labca.lockdown", cfg.LockdownDomains) + } + if cfg.DomainMode == "whitelist" { + viper.Set("labca.whitelist", cfg.WhitelistDomains) + } + viper.WriteConfig() - // Fake the method to GET as we need to continue in the setupHandler() function - r.Method = "GET" - } else { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) - return - } - } + // Fake the method to GET as we need to continue in the setupHandler() function + r.Method = "GET" + } else { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + return + } + } - // 3. Setup root CA certificate - if !_certCreate(w, r, "root-ca", true) { - return - } + // 3. Setup root CA certificate + if !_certCreate(w, r, "root-ca", true) { + return + } - // 4. Setup issuer certificate - if !_certCreate(w, r, "ca-int", false) { - return - } + // 4. Setup issuer certificate + if !_certCreate(w, r, "ca-int", false) { + return + } - // 5. Apply configuration / populate with certificate info - err := _applyConfig() - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + // 5. Apply configuration / populate with certificate info + err := _applyConfig() + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - if !viper.GetBool("config.restarted") { - // 6. Trust the new certs - if !_hostCommand(w, r, "trust-store") { - return - } + if !viper.GetBool("config.restarted") { + // 6. Trust the new certs + if !_hostCommand(w, r, "trust-store") { + return + } - // Don't let the retry mechanism generate new restartSecret! - if r.Header.Get("X-Requested-With") == "XMLHttpRequest" { - render(w, r, "index", map[string]interface{}{"Message": "Retry OK"}) - } else { - // 8. Restart application - restartSecret = randToken() - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/wait?restart="+restartSecret, http.StatusFound) - } - return + // Don't let the retry mechanism generate new restartSecret! + if r.Header.Get("X-Requested-With") == "XMLHttpRequest" { + render(w, r, "index", map[string]interface{}{"Message": "Retry OK"}) + } else { + // 8. Restart application + restartSecret = randToken() + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/wait?restart="+restartSecret, http.StatusFound) + } + return - } else { - render(w, r, "wrapup:manage", map[string]interface{}{"Progress": _progress("wrapup"), "HelpText": _helptext("wrapup")}) - } + } else { + render(w, r, "wrapup:manage", map[string]interface{}{"Progress": _progress("wrapup"), "HelpText": _helptext("wrapup")}) + } } func waitHandler(w http.ResponseWriter, r *http.Request) { - if viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) - return - } + if viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) + return + } - render(w, r, "polling:manage", map[string]interface{}{"Progress": _progress("polling"), "HelpText": _helptext("polling")}) + render(w, r, "polling:manage", map[string]interface{}{"Progress": _progress("polling"), "HelpText": _helptext("polling")}) } func restartHandler(w http.ResponseWriter, r *http.Request) { - if viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) - return - } + if viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) + return + } - if strings.Compare(r.URL.Query().Get("token"), restartSecret) != 0 { - log.Println("WARNING: Restart token ('" + r.URL.Query().Get("token") + "') does not match our secret ('" + restartSecret + "')!") - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if strings.Compare(r.URL.Query().Get("token"), restartSecret) != 0 { + log.Println("WARNING: Restart token ('" + r.URL.Query().Get("token") + "') does not match our secret ('" + restartSecret + "')!") + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - viper.Set("config.restarted", true) - viper.WriteConfig() + viper.Set("config.restarted", true) + viper.WriteConfig() - if !_hostCommand(w, r, "docker-restart") { - viper.Set("config.restarted", false) - viper.WriteConfig() - return - } + if !_hostCommand(w, r, "docker-restart") { + viper.Set("config.restarted", false) + viper.WriteConfig() + return + } } func finalHandler(w http.ResponseWriter, r *http.Request) { - if viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) - return - } + if viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/", http.StatusFound) + return + } - // Don't let the retry mechanism trigger a certificate request and restart! - if r.Header.Get("X-Requested-With") == "XMLHttpRequest" { - render(w, r, "index", map[string]interface{}{"Message": "Retry OK"}) - } else { - // 9. Setup our own web certificate - if !_hostCommand(w, r, "acme-request") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/logs/cert", http.StatusSeeOther) - return - } + // Don't let the retry mechanism trigger a certificate request and restart! + if r.Header.Get("X-Requested-With") == "XMLHttpRequest" { + render(w, r, "index", map[string]interface{}{"Message": "Retry OK"}) + } else { + // 9. Setup our own web certificate + if !_hostCommand(w, r, "acme-request") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/logs/cert", http.StatusSeeOther) + return + } - // 10. remove the temporary bit from nginx config - if !_hostCommand(w, r, "nginx-remove-redirect") { - return - } + // 10. remove the temporary bit from nginx config + if !_hostCommand(w, r, "nginx-remove-redirect") { + return + } - // 11. reload nginx - if !_hostCommand(w, r, "nginx-reload") { - return - } + // 11. reload nginx + if !_hostCommand(w, r, "nginx-reload") { + return + } - viper.Set("config.complete", true) - viper.WriteConfig() + viper.Set("config.complete", true) + viper.WriteConfig() - render(w, r, "final:manage", map[string]interface{}{"RequestBase": r.Header.Get("X-Request-Base"), "Progress": _progress("final"), "HelpText": _helptext("final")}) - } + render(w, r, "final:manage", map[string]interface{}{"RequestBase": r.Header.Get("X-Request-Base"), "Progress": _progress("final"), "HelpText": _helptext("final")}) + } } // RangeStructer takes the first argument, which must be a struct, and // returns the value of each field in a slice. It will return nil // if there are no arguments or first argument is not a struct func RangeStructer(args ...interface{}) []interface{} { - if len(args) == 0 { - return nil - } + if len(args) == 0 { + return nil + } - v := reflect.ValueOf(args[0]) - if v.Kind() != reflect.Struct { - return nil - } + v := reflect.ValueOf(args[0]) + if v.Kind() != reflect.Struct { + return nil + } - out := make([]interface{}, v.NumField()) - for i := 0; i < v.NumField(); i++ { - switch v.Field(i).Kind() { - case reflect.String: - if v.Field(i).Type().String() == "template.HTML" { - out[i] = template.HTML(v.Field(i).String()) - } else { - out[i] = v.Field(i).String() - } - case reflect.Bool: - out[i] = v.Field(i).Bool() - default: - out[i] = v.Field(i) - } - } + out := make([]interface{}, v.NumField()) + for i := 0; i < v.NumField(); i++ { + switch v.Field(i).Kind() { + case reflect.String: + if v.Field(i).Type().String() == "template.HTML" { + out[i] = template.HTML(v.Field(i).String()) + } else { + out[i] = v.Field(i).String() + } + case reflect.Bool: + out[i] = v.Field(i).Bool() + default: + out[i] = v.Field(i) + } + } - return out + return out } func accountsHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - Accounts, err := GetAccounts(w, r) - if err == nil { - render(w, r, "list:accounts", map[string]interface{}{"List": Accounts}) - } + Accounts, err := GetAccounts(w, r) + if err == nil { + render(w, r, "list:accounts", map[string]interface{}{"List": Accounts}) + } } func accountHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - vars := mux.Vars(r) - id, err := strconv.Atoi(vars["id"]) - if err != nil { - errorHandler(w, r, err, http.StatusBadRequest) - return - } + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + errorHandler(w, r, err, http.StatusBadRequest) + return + } - AccountDetails, err := GetAccount(w, r, id) - if err == nil { - render(w, r, "show:accounts", map[string]interface{}{"Details": AccountDetails}) - } + AccountDetails, err := GetAccount(w, r, id) + if err == nil { + render(w, r, "show:accounts", map[string]interface{}{"Details": AccountDetails}) + } } func ordersHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - Orders, err := GetOrders(w, r) - if err == nil { - render(w, r, "list:orders", map[string]interface{}{"List": Orders}) - } + Orders, err := GetOrders(w, r) + if err == nil { + render(w, r, "list:orders", map[string]interface{}{"List": Orders}) + } } func orderHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - vars := mux.Vars(r) - id, err := strconv.Atoi(vars["id"]) - if err != nil { - errorHandler(w, r, err, http.StatusBadRequest) - return - } + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + errorHandler(w, r, err, http.StatusBadRequest) + return + } - OrderDetails, err := GetOrder(w, r, id) - if err == nil { - render(w, r, "show:orders", map[string]interface{}{"Details": OrderDetails}) - } + OrderDetails, err := GetOrder(w, r, id) + if err == nil { + render(w, r, "show:orders", map[string]interface{}{"Details": OrderDetails}) + } } func authzHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - Authz, err := GetAuthz(w, r) - if err == nil { - render(w, r, "list:authz", map[string]interface{}{"List": Authz}) - } + Authz, err := GetAuthz(w, r) + if err == nil { + render(w, r, "list:authz", map[string]interface{}{"List": Authz}) + } } func authHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - vars := mux.Vars(r) - id := vars["id"] + vars := mux.Vars(r) + id := vars["id"] - AuthDetails, err := GetAuth(w, r, id) - if err == nil { - render(w, r, "show:authz", map[string]interface{}{"Details": AuthDetails}) - } + AuthDetails, err := GetAuth(w, r, id) + if err == nil { + render(w, r, "show:authz", map[string]interface{}{"Details": AuthDetails}) + } } func challengesHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - Challenges, err := GetChallenges(w, r) - if err == nil { - render(w, r, "list:challenges", map[string]interface{}{"List": Challenges}) - } + Challenges, err := GetChallenges(w, r) + if err == nil { + render(w, r, "list:challenges", map[string]interface{}{"List": Challenges}) + } } func challengeHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - vars := mux.Vars(r) - id, err := strconv.Atoi(vars["id"]) - if err != nil { - errorHandler(w, r, err, http.StatusBadRequest) - return - } + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + errorHandler(w, r, err, http.StatusBadRequest) + return + } - ChallengeDetails, err := GetChallenge(w, r, id) - if err == nil { - render(w, r, "show:challenges", map[string]interface{}{"Details": ChallengeDetails}) - } + ChallengeDetails, err := GetChallenge(w, r, id) + if err == nil { + render(w, r, "show:challenges", map[string]interface{}{"Details": ChallengeDetails}) + } } func certificatesHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - Certificates, err := GetCertificates(w, r) - if err == nil { - render(w, r, "list:certificates", map[string]interface{}{"List": Certificates}) - } + Certificates, err := GetCertificates(w, r) + if err == nil { + render(w, r, "list:certificates", map[string]interface{}{"List": Certificates}) + } } func certificateHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) - return - } + if !viper.GetBool("config.complete") { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) + return + } - var serial string - vars := mux.Vars(r) - id, err := strconv.Atoi(vars["id"]) - if err != nil { - serial = vars["id"] - } + var serial string + vars := mux.Vars(r) + id, err := strconv.Atoi(vars["id"]) + if err != nil { + serial = vars["id"] + } - CertificateDetails, err := GetCertificate(w, r, id, serial) - if err == nil { - render(w, r, "show:certificates", map[string]interface{}{"Details": CertificateDetails}) - } + CertificateDetails, err := GetCertificate(w, r, id, serial) + if err == nil { + render(w, r, "show:certificates", map[string]interface{}{"Details": CertificateDetails}) + } } func certRevokeHandler(w http.ResponseWriter, r *http.Request) { - if !viper.GetBool("config.complete") { - errorHandler(w, r, errors.New("Method not allowed at this point"), http.StatusMethodNotAllowed) - return - } + if !viper.GetBool("config.complete") { + errorHandler(w, r, errors.New("Method not allowed at this point"), http.StatusMethodNotAllowed) + return + } - if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - serial := r.Form.Get("serial") - reason, err := strconv.Atoi(r.Form.Get("reason")) - if err != nil { - errorHandler(w, r, err, http.StatusBadRequest) - return - } + serial := r.Form.Get("serial") + reason, err := strconv.Atoi(r.Form.Get("reason")) + if err != nil { + errorHandler(w, r, err, http.StatusBadRequest) + return + } - if !_hostCommand(w, r, "revoke-cert", serial, strconv.Itoa(reason)) { - return - } - } + if !_hostCommand(w, r, "revoke-cert", serial, strconv.Itoa(reason)) { + return + } + } } type navItem struct { - Name string - Icon string - Attrs map[template.HTMLAttr]string - IsActive bool - SubMenu []navItem + Name string + Icon string + Attrs map[template.HTMLAttr]string + IsActive bool + SubMenu []navItem } func activeNav(active string, uri string, requestBase string) []navItem { - isAcmeActive := (uri == "/accounts" || strings.HasPrefix(uri, "/accounts/") || - uri == "/orders" || strings.HasPrefix(uri, "/orders/") || - uri == "/authz" || strings.HasPrefix(uri, "/authz/") || - uri == "/challenges" || strings.HasPrefix(uri, "/challenges/") || - uri == "/certificates" || strings.HasPrefix(uri, "/certificates/") || - false) + isAcmeActive := (uri == "/accounts" || strings.HasPrefix(uri, "/accounts/") || + uri == "/orders" || strings.HasPrefix(uri, "/orders/") || + uri == "/authz" || strings.HasPrefix(uri, "/authz/") || + uri == "/challenges" || strings.HasPrefix(uri, "/challenges/") || + uri == "/certificates" || strings.HasPrefix(uri, "/certificates/") || + false) - // create menu items - home := navItem{ - Name: "Dashboard", - Icon: "fa-dashboard", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/", - "title": "Main page with the status of the system", - }, - } - accounts := navItem{ - Name: "Accounts", - Icon: "fa-list-alt", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/accounts", - "title": "ACME Accounts", - }, - } - orders := navItem{ - Name: "Orders", - Icon: "fa-tags", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/orders", - "title": "ACME Orders", - }, - } - authz := navItem{ - Name: "Authorizations", - Icon: "fa-chain", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/authz", - "title": "ACME Authorizations", - }, - } - challenges := navItem{ - Name: "Challenges", - Icon: "fa-exchange", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/challenges", - "title": "ACME Challenges", - }, - } - certificates := navItem{ - Name: "Certificates", - Icon: "fa-lock", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/certificates", - "title": "ACME Certificates", - }, - } - acme := navItem{ - Name: "ACME", - Icon: "fa-sitemap", - Attrs: map[template.HTMLAttr]string{ - "href": "#", - "title": "Automated Certificate Management Environment", - }, - IsActive: isAcmeActive, - SubMenu: []navItem{accounts, certificates, orders, authz, challenges}, - } - cert := navItem{ - Name: "Web Certificate", - Icon: "fa-lock", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/logs/cert", - "title": "Log file for the certificate renewal for this server", - }, - } - boulder := navItem{ - Name: "ACME", - Icon: "fa-search-plus", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/logs/boulder", - "title": "Live view on the backend ACME application logs", - }, - } - audit := navItem{ - Name: "ACME Audit Log", - Icon: "fa-paw", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/logs/audit", - "title": "Live view on only the audit messages in the backend ACME application logs", - }, - } - labca := navItem{ - Name: "LabCA", - Icon: "fa-edit", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/logs/labca", - "title": "Live view on the logs for this LabCA web application", - }, - } - web := navItem{ - Name: "Web Access", - Icon: "fa-globe", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/logs/web", - "title": "Live view on the NGINX web server access log", - }, - } - weberr := navItem{ - Name: "Web Error", - Icon: "fa-times", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/logs/weberr", - "title": "Log file for the NGINX web server error log", - }, - } - logs := navItem{ - Name: "Logs", - Icon: "fa-files-o", - Attrs: map[template.HTMLAttr]string{ - "href": "#", - "title": "Log Files", - }, - SubMenu: []navItem{cert, boulder, audit, labca, web, weberr}, - } - manage := navItem{ - Name: "Manage", - Icon: "fa-wrench", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/manage", - "title": "Manage the system", - }, - } - about := navItem{ - Name: "About", - Icon: "fa-comments", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/about", - "title": "About LabCA", - }, - } - public := navItem{ - Name: "Public Area", - Icon: "fa-home", - Attrs: map[template.HTMLAttr]string{ - "href": "/", - "title": "The non-Admin pages of this LabCA instance", - }, - } + // create menu items + home := navItem{ + Name: "Dashboard", + Icon: "fa-dashboard", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/", + "title": "Main page with the status of the system", + }, + } + accounts := navItem{ + Name: "Accounts", + Icon: "fa-list-alt", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/accounts", + "title": "ACME Accounts", + }, + } + orders := navItem{ + Name: "Orders", + Icon: "fa-tags", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/orders", + "title": "ACME Orders", + }, + } + authz := navItem{ + Name: "Authorizations", + Icon: "fa-chain", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/authz", + "title": "ACME Authorizations", + }, + } + challenges := navItem{ + Name: "Challenges", + Icon: "fa-exchange", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/challenges", + "title": "ACME Challenges", + }, + } + certificates := navItem{ + Name: "Certificates", + Icon: "fa-lock", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/certificates", + "title": "ACME Certificates", + }, + } + acme := navItem{ + Name: "ACME", + Icon: "fa-sitemap", + Attrs: map[template.HTMLAttr]string{ + "href": "#", + "title": "Automated Certificate Management Environment", + }, + IsActive: isAcmeActive, + SubMenu: []navItem{accounts, certificates, orders, authz, challenges}, + } + cert := navItem{ + Name: "Web Certificate", + Icon: "fa-lock", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/logs/cert", + "title": "Log file for the certificate renewal for this server", + }, + } + boulder := navItem{ + Name: "ACME", + Icon: "fa-search-plus", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/logs/boulder", + "title": "Live view on the backend ACME application logs", + }, + } + audit := navItem{ + Name: "ACME Audit Log", + Icon: "fa-paw", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/logs/audit", + "title": "Live view on only the audit messages in the backend ACME application logs", + }, + } + labca := navItem{ + Name: "LabCA", + Icon: "fa-edit", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/logs/labca", + "title": "Live view on the logs for this LabCA web application", + }, + } + web := navItem{ + Name: "Web Access", + Icon: "fa-globe", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/logs/web", + "title": "Live view on the NGINX web server access log", + }, + } + weberr := navItem{ + Name: "Web Error", + Icon: "fa-times", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/logs/weberr", + "title": "Log file for the NGINX web server error log", + }, + } + logs := navItem{ + Name: "Logs", + Icon: "fa-files-o", + Attrs: map[template.HTMLAttr]string{ + "href": "#", + "title": "Log Files", + }, + SubMenu: []navItem{cert, boulder, audit, labca, web, weberr}, + } + manage := navItem{ + Name: "Manage", + Icon: "fa-wrench", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/manage", + "title": "Manage the system", + }, + } + about := navItem{ + Name: "About", + Icon: "fa-comments", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/about", + "title": "About LabCA", + }, + } + public := navItem{ + Name: "Public Area", + Icon: "fa-home", + Attrs: map[template.HTMLAttr]string{ + "href": "/", + "title": "The non-Admin pages of this LabCA instance", + }, + } - // set active menu class - switch active { - case "about": - about.Attrs["class"] = "active" - case "accounts": - accounts.Attrs["class"] = "active" - case "orders": - orders.Attrs["class"] = "active" - case "authz": - authz.Attrs["class"] = "active" - case "challenges": - challenges.Attrs["class"] = "active" - case "certificates": - certificates.Attrs["class"] = "active" - case "index": - home.Attrs["class"] = "active" - case "manage": - manage.Attrs["class"] = "active" - case "logs": - logs.Attrs["class"] = "active" - } + // set active menu class + switch active { + case "about": + about.Attrs["class"] = "active" + case "accounts": + accounts.Attrs["class"] = "active" + case "orders": + orders.Attrs["class"] = "active" + case "authz": + authz.Attrs["class"] = "active" + case "challenges": + challenges.Attrs["class"] = "active" + case "certificates": + certificates.Attrs["class"] = "active" + case "index": + home.Attrs["class"] = "active" + case "manage": + manage.Attrs["class"] = "active" + case "logs": + logs.Attrs["class"] = "active" + } - return []navItem{home, acme, logs, manage, about, public} + return []navItem{home, acme, logs, manage, about, public} } func render(w http.ResponseWriter, r *http.Request, view string, data map[string]interface{}) { - viewSlice := strings.Split(view, ":") - menu := viewSlice[0] - if len(viewSlice) > 1 { - menu = viewSlice[1] - } - data["Menu"] = activeNav(menu, r.RequestURI, r.Header.Get("X-Request-Base")) + viewSlice := strings.Split(view, ":") + menu := viewSlice[0] + if len(viewSlice) > 1 { + menu = viewSlice[1] + } + data["Menu"] = activeNav(menu, r.RequestURI, r.Header.Get("X-Request-Base")) - if version != "" { - data["Version"] = version - } + if version != "" { + data["Version"] = version + } - b, err := tmpls.Render("base.tmpl", "views/"+viewSlice[0]+".tmpl", data) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } + b, err := tmpls.Render("base.tmpl", "views/"+viewSlice[0]+".tmpl", data) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } - w.Write(b) + w.Write(b) } func notFoundHandler(w http.ResponseWriter, r *http.Request) { - errorHandler(w, r, fmt.Errorf("NotFoundHandler for: %s %s", r.Method, r.URL), http.StatusNotFound) + errorHandler(w, r, fmt.Errorf("NotFoundHandler for: %s %s", r.Method, r.URL), http.StatusNotFound) } func authorized(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - log.Println(r.Method + " " + r.RequestURI) + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + log.Println(r.Method + " " + r.RequestURI) - if r.RequestURI == "/login" || strings.Contains(r.RequestURI, "/static/") { - next.ServeHTTP(w, r) - } else { - session := getSession(w, r) - if session.Values["user"] != nil || (r.RequestURI == "/setup" && viper.Get("user.password") == nil) { - next.ServeHTTP(w, r) - } else { - session.Values["bounce"] = r.RequestURI - session.Save(r, w) - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/login", http.StatusFound) - } - } - }) + if r.RequestURI == "/login" || strings.Contains(r.RequestURI, "/static/") { + next.ServeHTTP(w, r) + } else { + session := getSession(w, r) + if session.Values["user"] != nil || (r.RequestURI == "/setup" && viper.Get("user.password") == nil) { + next.ServeHTTP(w, r) + } else { + session.Values["bounce"] = r.RequestURI + session.Save(r, w) + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/login", http.StatusFound) + } + } + }) } func init() { - if os.Getenv("DEVELOPMENT") != "" { - isDev = true - } + if os.Getenv("DEVELOPMENT") != "" { + isDev = true + } - var err error - tmpls, err = templates.New().ParseDir("./templates", "templates/") - if err != nil { - panic(fmt.Errorf("Fatal error templates: %s \n", err)) - } - tmpls.AddFunc("rangeStruct", RangeStructer) + var err error + tmpls, err = templates.New().ParseDir("./templates", "templates/") + if err != nil { + panic(fmt.Errorf("Fatal error templates: %s \n", err)) + } + tmpls.AddFunc("rangeStruct", RangeStructer) - viper.SetConfigName("config") - viper.AddConfigPath("data") - viper.SetDefault("config.complete", false) - if err := viper.ReadInConfig(); err != nil { - panic(fmt.Errorf("Fatal error config file: %s \n", err)) - } + viper.SetConfigName("config") + viper.AddConfigPath("data") + viper.SetDefault("config.complete", false) + if err := viper.ReadInConfig(); err != nil { + panic(fmt.Errorf("Fatal error config file: %s \n", err)) + } - if viper.Get("keys.auth") == nil { - key := securecookie.GenerateRandomKey(32) - if key == nil { - panic(fmt.Errorf("Fatal error random key\n")) - } - viper.Set("keys.auth", key) - viper.WriteConfig() - } - if viper.Get("keys.enc") == nil { - key := securecookie.GenerateRandomKey(32) - if key == nil { - panic(fmt.Errorf("Fatal error random key\n")) - } - viper.Set("keys.enc", key) - viper.WriteConfig() - } + if viper.Get("keys.auth") == nil { + key := securecookie.GenerateRandomKey(32) + if key == nil { + panic(fmt.Errorf("Fatal error random key\n")) + } + viper.Set("keys.auth", key) + viper.WriteConfig() + } + if viper.Get("keys.enc") == nil { + key := securecookie.GenerateRandomKey(32) + if key == nil { + panic(fmt.Errorf("Fatal error random key\n")) + } + viper.Set("keys.enc", key) + viper.WriteConfig() + } - if viper.Get("server.addr") == nil { - viper.Set("server.addr", "0.0.0.0") - viper.WriteConfig() - } + if viper.Get("server.addr") == nil { + viper.Set("server.addr", "0.0.0.0") + viper.WriteConfig() + } - if viper.Get("server.port") == nil { - viper.Set("server.port", 3000) - viper.WriteConfig() - } + if viper.Get("server.port") == nil { + viper.Set("server.port", 3000) + viper.WriteConfig() + } - if viper.Get("server.session.maxage") == nil { - viper.Set("server.session.maxage", 3600) // 1 hour - viper.WriteConfig() - } + if viper.Get("server.session.maxage") == nil { + viper.Set("server.session.maxage", 3600) // 1 hour + viper.WriteConfig() + } - if viper.Get("db.conn") == nil { - viper.Set("db.type", "mysql") - viper.Set("db.conn", "root@tcp(boulder-mysql:3306)/boulder_sa_integration") - viper.WriteConfig() - } - dbConn = viper.GetString("db.conn") - dbType = viper.GetString("db.type") + if viper.Get("db.conn") == nil { + viper.Set("db.type", "mysql") + viper.Set("db.conn", "root@tcp(boulder-mysql:3306)/boulder_sa_integration") + viper.WriteConfig() + } + dbConn = viper.GetString("db.conn") + dbType = viper.GetString("db.type") - version = viper.GetString("version") + version = viper.GetString("version") } func main() { - tmpls.Parse() + tmpls.Parse() - sessionStore = sessions.NewCookieStore([]byte(viper.GetString("keys.auth")), []byte(viper.GetString("keys.enc"))) - sessionStore.Options = &sessions.Options{ - Path: "/", - MaxAge: viper.GetInt("server.session.maxage") * 1, - HttpOnly: true, - } + sessionStore = sessions.NewCookieStore([]byte(viper.GetString("keys.auth")), []byte(viper.GetString("keys.enc"))) + sessionStore.Options = &sessions.Options{ + Path: "/", + MaxAge: viper.GetInt("server.session.maxage") * 1, + HttpOnly: true, + } - r := mux.NewRouter() - r.HandleFunc("/", rootHandler).Methods("GET") - r.HandleFunc("/about", aboutHandler).Methods("GET") - r.HandleFunc("/manage", manageHandler).Methods("GET", "POST") - r.HandleFunc("/final", finalHandler).Methods("GET") - r.HandleFunc("/login", loginHandler).Methods("GET", "POST") - r.HandleFunc("/logout", logoutHandler).Methods("GET") - r.HandleFunc("/logs/{type}", logsHandler).Methods("GET") - r.HandleFunc("/restart", restartHandler).Methods("GET") - r.HandleFunc("/setup", setupHandler).Methods("GET", "POST") - r.HandleFunc("/wait", waitHandler).Methods("GET") - r.HandleFunc("/ws", wsHandler).Methods("GET") + r := mux.NewRouter() + r.HandleFunc("/", rootHandler).Methods("GET") + r.HandleFunc("/about", aboutHandler).Methods("GET") + r.HandleFunc("/manage", manageHandler).Methods("GET", "POST") + r.HandleFunc("/final", finalHandler).Methods("GET") + r.HandleFunc("/login", loginHandler).Methods("GET", "POST") + r.HandleFunc("/logout", logoutHandler).Methods("GET") + r.HandleFunc("/logs/{type}", logsHandler).Methods("GET") + r.HandleFunc("/restart", restartHandler).Methods("GET") + r.HandleFunc("/setup", setupHandler).Methods("GET", "POST") + r.HandleFunc("/wait", waitHandler).Methods("GET") + r.HandleFunc("/ws", wsHandler).Methods("GET") - r.HandleFunc("/accounts", accountsHandler).Methods("GET") - r.HandleFunc("/accounts/{id}", accountHandler).Methods("GET") - r.HandleFunc("/orders", ordersHandler).Methods("GET") - r.HandleFunc("/orders/{id}", orderHandler).Methods("GET") - r.HandleFunc("/authz", authzHandler).Methods("GET") - r.HandleFunc("/authz/{id}", authHandler).Methods("GET") - r.HandleFunc("/challenges", challengesHandler).Methods("GET") - r.HandleFunc("/challenges/{id}", challengeHandler).Methods("GET") - r.HandleFunc("/certificates", certificatesHandler).Methods("GET") - r.HandleFunc("/certificates/{id}", certificateHandler).Methods("GET") - r.HandleFunc("/certificates/{id}", certRevokeHandler).Methods("POST") + r.HandleFunc("/accounts", accountsHandler).Methods("GET") + r.HandleFunc("/accounts/{id}", accountHandler).Methods("GET") + r.HandleFunc("/orders", ordersHandler).Methods("GET") + r.HandleFunc("/orders/{id}", orderHandler).Methods("GET") + r.HandleFunc("/authz", authzHandler).Methods("GET") + r.HandleFunc("/authz/{id}", authHandler).Methods("GET") + r.HandleFunc("/challenges", challengesHandler).Methods("GET") + r.HandleFunc("/challenges/{id}", challengeHandler).Methods("GET") + r.HandleFunc("/certificates", certificatesHandler).Methods("GET") + r.HandleFunc("/certificates/{id}", certificateHandler).Methods("GET") + r.HandleFunc("/certificates/{id}", certRevokeHandler).Methods("POST") - r.NotFoundHandler = http.HandlerFunc(notFoundHandler) - if isDev { - r.PathPrefix("/accounts/static/").Handler(http.StripPrefix("/accounts/static/", http.FileServer(http.Dir("../www")))) - r.PathPrefix("/authz/static/").Handler(http.StripPrefix("/authz/static/", http.FileServer(http.Dir("../www")))) - r.PathPrefix("/challenges/static/").Handler(http.StripPrefix("/challenges/static/", http.FileServer(http.Dir("../www")))) - r.PathPrefix("/certificates/static/").Handler(http.StripPrefix("/certificates/static/", http.FileServer(http.Dir("../www")))) - r.PathPrefix("/orders/static/").Handler(http.StripPrefix("/orders/static/", http.FileServer(http.Dir("../www")))) - r.PathPrefix("/logs/static/").Handler(http.StripPrefix("/logs/static/", http.FileServer(http.Dir("../www")))) - r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("../www")))) - } - r.Use(authorized) + r.NotFoundHandler = http.HandlerFunc(notFoundHandler) + if isDev { + r.PathPrefix("/accounts/static/").Handler(http.StripPrefix("/accounts/static/", http.FileServer(http.Dir("../www")))) + r.PathPrefix("/authz/static/").Handler(http.StripPrefix("/authz/static/", http.FileServer(http.Dir("../www")))) + r.PathPrefix("/challenges/static/").Handler(http.StripPrefix("/challenges/static/", http.FileServer(http.Dir("../www")))) + r.PathPrefix("/certificates/static/").Handler(http.StripPrefix("/certificates/static/", http.FileServer(http.Dir("../www")))) + r.PathPrefix("/orders/static/").Handler(http.StripPrefix("/orders/static/", http.FileServer(http.Dir("../www")))) + r.PathPrefix("/logs/static/").Handler(http.StripPrefix("/logs/static/", http.FileServer(http.Dir("../www")))) + r.PathPrefix("/static/").Handler(http.StripPrefix("/static/", http.FileServer(http.Dir("../www")))) + } + r.Use(authorized) - log.Printf("Listening on %s:%d...\n", viper.GetString("server.addr"), viper.GetInt("server.port")) - srv := &http.Server{ - Handler: r, - Addr: viper.GetString("server.addr") + ":" + viper.GetString("server.port"), - WriteTimeout: 15 * time.Second, - ReadTimeout: 15 * time.Second, - } - log.Fatal(srv.ListenAndServe()) + log.Printf("Listening on %s:%d...\n", viper.GetString("server.addr"), viper.GetInt("server.port")) + srv := &http.Server{ + Handler: r, + Addr: viper.GetString("server.addr") + ":" + viper.GetString("server.port"), + WriteTimeout: 15 * time.Second, + ReadTimeout: 15 * time.Second, + } + log.Fatal(srv.ListenAndServe()) } From 04aed0ed86044d422246ab20a7e0869f9183e515 Mon Sep 17 00:00:00 2001 From: Arjan H Date: Fri, 23 Nov 2018 20:46:57 +0100 Subject: [PATCH 2/3] Refactor code to reduce cyclomatic complexity #2 --- README.md | 2 +- gui/acme.go | 56 +-- gui/certificate.go | 636 ++++++++++++++++++--------------- gui/dashboard.go | 13 +- gui/main.go | 865 ++++++++++++++++++++++++--------------------- 5 files changed, 862 insertions(+), 710 deletions(-) diff --git a/README.md b/README.md index 3aef0b3..e265d7a 100644 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ Once the setup is completed, please make a backup of your Root and Issuer certif ### Update -Until issue #1 is implemented, updates can only be done from the Linux shell. On the server run this command as root to update the installation: +Until [issue #1](https://github.com/hakwerk/labca/issues/1) is implemented, updates can only be done from the Linux shell. On the server run this command as root to update the installation: ```sh ~labca/labca/install diff --git a/gui/acme.go b/gui/acme.go index 3bacc2d..3ede93f 100644 --- a/gui/acme.go +++ b/gui/acme.go @@ -630,6 +630,36 @@ type CertificateExtra struct { IsExpired bool } +func _getReasonText(RevokedReason int, Revoked string) string { + reasonText := "" + switch RevokedReason { + case 0: + if Revoked != "0000-00-00 00:00:00" { + reasonText = " - Unspecified" + } + case 1: + reasonText = " - Key Compromise" + case 2: + reasonText = " - CA Compromise" + case 3: + reasonText = " - Affiliation Changed" + case 4: + reasonText = " - Superseded" + case 5: + reasonText = " - Cessation Of Operation" + case 6: + reasonText = " - Certificate Hold" + case 8: + reasonText = " - Remove From CRL" + case 9: + reasonText = " - Privilege Withdrawn" + case 10: + reasonText = " - AA Compromise" + } + + return reasonText +} + func GetCertificate(w http.ResponseWriter, r *http.Request, id int, serial string) (CertificateShow, error) { db, err := sql.Open(dbType, dbConn) if err != nil { @@ -681,31 +711,7 @@ func GetCertificate(w http.ResponseWriter, r *http.Request, id int, serial strin CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Status", row.Status}) CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"OCSP Last Update", row.OCSPLastUpdate}) CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Revoked", row.Revoked}) - reasonText := "" - switch row.RevokedReason { - case 0: - if row.Revoked != "0000-00-00 00:00:00" { - reasonText = " - Unspecified" - } - case 1: - reasonText = " - Key Compromise" - case 2: - reasonText = " - CA Compromise" - case 3: - reasonText = " - Affiliation Changed" - case 4: - reasonText = " - Superseded" - case 5: - reasonText = " - Cessation Of Operation" - case 6: - reasonText = " - Certificate Hold" - case 8: - reasonText = " - Remove From CRL" - case 9: - reasonText = " - Privilege Withdrawn" - case 10: - reasonText = " - AA Compromise" - } + reasonText := _getReasonText(row.RevokedReason, row.Revoked) CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Revoked Reason", strconv.Itoa(row.RevokedReason) + reasonText}) CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Last Expiration Nag Sent", row.LastNagSent}) CertificateDetails.Rows = append(CertificateDetails.Rows, NameValue{"Not After", row.NotAfter}) diff --git a/gui/certificate.go b/gui/certificate.go index 94bd441..66b9b8a 100644 --- a/gui/certificate.go +++ b/gui/certificate.go @@ -49,22 +49,26 @@ func (ci *CertificateInfo) Initialize() { ci.KeyType = "rsa4096" } +func (ci *CertificateInfo) ValidateGenerate() { + if strings.TrimSpace(ci.KeyType) == "" || strings.TrimSpace(ci.KeyTypes[ci.KeyType]) == "" { + ci.Errors["KeyType"] = "Please select a key type/size" + } + if strings.TrimSpace(ci.Country) == "" || len(ci.Country) < 2 { + ci.Errors["Country"] = "Please enter a valid 2-character country code" + } + if strings.TrimSpace(ci.Organization) == "" { + ci.Errors["Organization"] = "Please enter an organization name" + } + if strings.TrimSpace(ci.CommonName) == "" { + ci.Errors["CommonName"] = "Please enter a common name" + } +} + func (ci *CertificateInfo) Validate() bool { ci.Errors = make(map[string]string) if ci.CreateType == "generate" { - if strings.TrimSpace(ci.KeyType) == "" || strings.TrimSpace(ci.KeyTypes[ci.KeyType]) == "" { - ci.Errors["KeyType"] = "Please select a key type/size" - } - if strings.TrimSpace(ci.Country) == "" || len(ci.Country) < 2 { - ci.Errors["Country"] = "Please enter a valid 2-character country code" - } - if strings.TrimSpace(ci.Organization) == "" { - ci.Errors["Organization"] = "Please enter an organization name" - } - if strings.TrimSpace(ci.CommonName) == "" { - ci.Errors["CommonName"] = "Please enter a common name" - } + ci.ValidateGenerate() } if (ci.CreateType == "import") && (ci.ImportHandler != nil) { @@ -133,6 +137,337 @@ func preCreateTasks(path string) error { return nil } +func (ci *CertificateInfo) Generate(path string, certBase string) error { + // 1. Generate key + createCmd := "genrsa -aes256 -passout pass:foobar" + keySize := " 4096" + if strings.HasPrefix(ci.KeyType, "ecdsa") { + keySize = "" + createCmd = "ecparam -genkey -name " + if ci.KeyType == "ecdsa256" { + createCmd = createCmd + "prime256v1" + } + if ci.KeyType == "ecdsa384" { + createCmd = createCmd + "secp384r1" + } + } else { + if strings.HasSuffix(ci.KeyType, "3072") { + keySize = " 3072" + } + if strings.HasSuffix(ci.KeyType, "2048") { + keySize = " 2048" + } + } + + if _, err := exe_cmd("openssl " + createCmd + " -out " + path + certBase + ".key" + keySize); err != nil { + return reportError(err) + } + if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -passin pass:foobar -out " + path + certBase + ".tmp"); err != nil { + return reportError(err) + } + if _, err := exe_cmd("mv " + path + certBase + ".tmp " + path + certBase + ".key"); err != nil { + return reportError(err) + } + + _, _ = exe_cmd("sleep 1") + + // 2. Generate certificate + subject := "/C=" + ci.Country + "/O=" + ci.Organization + if ci.OrgUnit != "" { + subject = subject + "/OU=" + ci.OrgUnit + } + subject = subject + "/CN=" + ci.CommonName + subject = strings.Replace(subject, " ", "\\\\", -1) + + if ci.IsRoot { + if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -days 3650 -new -x509 -extensions v3_ca -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".pem"); err != nil { + return reportError(err) + } + } else { + if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -new -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".csr"); err != nil { + return reportError(err) + } + if _, err := exe_cmd("openssl ca -config " + path + "../openssl.cnf -extensions v3_intermediate_ca -days 3600 -md sha384 -notext -batch -in " + path + certBase + ".csr -out " + path + certBase + ".pem"); err != nil { + return reportError(err) + } + } + + return nil +} + +func (ci *CertificateInfo) ImportPkcs12(tmpFile string, tmpKey string, tmpCert string) error { + if ci.IsRoot { + if strings.Index(ci.ImportHandler.Filename, "labca_root") != 0 { + fmt.Printf("WARNING: importing root from .pfx file but name is %s\n", ci.ImportHandler.Filename) + } + } else { + if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { + fmt.Printf("WARNING: importing issuer from .pfx file but name is %s\n", ci.ImportHandler.Filename) + } + } + + pwd := "pass:dummy" + if ci.ImportPwd != "" { + pwd = "pass:" + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) + } + + if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil { + if strings.Index(string(out), "invalid password") >= 0 { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } + if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil { + if strings.Index(string(out), "invalid password") >= 0 { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } + + return nil +} + +func (ci *CertificateInfo) ImportZip(tmpFile string, tmpDir string) error { + if ci.IsRoot { + if (strings.Index(ci.ImportHandler.Filename, "labca_root") != 0) && (strings.Index(ci.ImportHandler.Filename, "labca_certificates") != 0) { + fmt.Printf("WARNING: importing root from .zip file but name is %s\n", ci.ImportHandler.Filename) + } + } else { + if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { + fmt.Printf("WARNING: importing issuer from .zip file but name is %s\n", ci.ImportHandler.Filename) + } + } + + cmd := "unzip -j" + if ci.ImportPwd != "" { + cmd = cmd + " -P " + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) + } else { + cmd = cmd + " -P dummy" + } + cmd = cmd + " " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -d " + tmpDir + + if _, err := exe_cmd(cmd); err != nil { + if err.Error() == "exit status 82" { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } + + return nil +} + +func (ci *CertificateInfo) Import(path string, certBase string, tmpDir string, tmpKey string, tmpCert string) error { + tmpFile := filepath.Join(tmpDir, ci.ImportHandler.Filename) + + f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0666) + if err != nil { + return err + } + + defer f.Close() + + io.Copy(f, ci.ImportFile) + + contentType := ci.ImportHandler.Header.Get("Content-Type") + if contentType == "application/x-pkcs12" { + err := ci.ImportPkcs12(tmpFile, tmpKey, tmpCert) + if err != nil { + return err + } + + } else if contentType == "application/zip" { + err := ci.ImportZip(tmpFile, tmpDir) + if err != nil { + return err + } + + } else { + return errors.New("Content Type '" + contentType + "' not supported!") + } + + return nil +} + +func (ci *CertificateInfo) Upload(path string, certBase string, tmpKey string, tmpCert string) error { + if err := ioutil.WriteFile(tmpKey, []byte(ci.Key), 0644); err != nil { + return err + } + + pwd := "pass:dummy" + if ci.Passphrase != "" { + pwd = "pass:" + strings.Replace(ci.Passphrase, " ", "\\\\", -1) + } + + if out, err := exe_cmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil { + if strings.Index(string(out), ":bad decrypt:") >= 0 { + return errors.New("Incorrect password!") + } else { + return reportError(err) + } + } else { + if _, err = exe_cmd("mv " + tmpKey + "-out " + tmpKey); err != nil { + return reportError(err) + } + } + + if err := ioutil.WriteFile(tmpCert, []byte(ci.Certificate), 0644); err != nil { + return err + } + + return nil +} + +func (ci *CertificateInfo) ImportCerts(path string, rootCert string, rootKey string, issuerCert string, issuerKey string) error { + var rootSubject string + if (rootCert != "") && (rootKey != "") { + r, err := exe_cmd("openssl x509 -noout -subject -in " + rootCert) + if err != nil { + return reportError(err) + } else { + rootSubject = string(r[0 : len(r)-1]) + fmt.Printf("Import root with subject '%s'\n", rootSubject) + } + + r, err = exe_cmd("openssl pkey -noout -in " + rootKey) + if err != nil { + return reportError(err) + } else { + fmt.Println("Import root key") + } + } + + if (issuerCert != "") && (issuerKey != "") { + if ci.IsRoot { + if err := preCreateTasks(path + "issuer/"); err != nil { + return err + } + } + + r, err := exe_cmd("openssl x509 -noout -subject -in " + issuerCert) + if err != nil { + return reportError(err) + } else { + fmt.Printf("Import issuer with subject '%s'\n", string(r[0:len(r)-1])) + } + + r, err = exe_cmd("openssl x509 -noout -issuer -in " + issuerCert) + if err != nil { + return reportError(err) + } else { + issuerIssuer := string(r[0 : len(r)-1]) + fmt.Printf("Issuer certificate issued by CA '%s'\n", issuerIssuer) + + if rootSubject == "" { + r, err := exe_cmd("openssl x509 -noout -subject -in data/root-ca.pem") + if err != nil { + return reportError(err) + } else { + rootSubject = string(r[0 : len(r)-1]) + } + } + + issuerIssuer = strings.Replace(issuerIssuer, "issuer=", "", -1) + rootSubject = strings.Replace(rootSubject, "subject=", "", -1) + if issuerIssuer != rootSubject { + return errors.New("Issuer not issued by our Root CA!") + } + } + + r, err = exe_cmd("openssl pkey -noout -in " + issuerKey) + if err != nil { + return reportError(err) + } else { + fmt.Println("Import issuer key") + } + } + + return nil +} + +func (ci *CertificateInfo) MoveFiles(path string, rootCert string, rootKey string, issuerCert string, issuerKey string) error { + if rootCert != "" { + if _, err := exe_cmd("mv " + rootCert + " " + path); err != nil { + return reportError(err) + } + } + if rootKey != "" { + if _, err := exe_cmd("mv " + rootKey + " " + path); err != nil { + return reportError(err) + } + } + if issuerCert != "" { + if _, err := exe_cmd("mv " + issuerCert + " data/issuer/"); err != nil { + return reportError(err) + } + } + if issuerKey != "" { + if _, err := exe_cmd("mv " + issuerKey + " data/issuer/"); err != nil { + return reportError(err) + } + } + + if (issuerCert != "") && (issuerKey != "") && ci.IsRoot { + if err := postCreateTasks(path+"issuer/", "ca-int"); err != nil { + return err + } + } + + return nil +} + +func (ci *CertificateInfo) Extract(path string, certBase string, tmpDir string) error { + var rootCert string + var rootKey string + var issuerCert string + var issuerKey string + + if ci.IsRoot { + rootCert = filepath.Join(tmpDir, "root-ca.pem") + rootKey = filepath.Join(tmpDir, "root-ca.key") + + if _, err := os.Stat(rootCert); os.IsNotExist(err) { + return errors.New("File does not contain root-ca.pem!") + } + if _, err := os.Stat(rootKey); os.IsNotExist(err) { + return errors.New("File does not contain root-ca.key!") + } + } + + issuerCert = filepath.Join(tmpDir, "ca-int.pem") + issuerKey = filepath.Join(tmpDir, "ca-int.key") + + if _, err := os.Stat(issuerCert); os.IsNotExist(err) { + if ci.IsRoot { + issuerCert = "" + } else { + return errors.New("File does not contain ca-int.pem!") + } + } + if _, err := os.Stat(issuerKey); os.IsNotExist(err) { + if ci.IsRoot { + issuerKey = "" + } else { + return errors.New("File does not contain ca-int.key!") + } + } + + err := ci.ImportCerts(path, rootCert, rootKey, issuerCert, issuerKey) + if err != nil { + return err + } + + // All is good now, move files to their permanent location... + err = ci.MoveFiles(path, rootCert, rootKey, issuerCert, issuerKey) + if err != nil { + return err + } + + return nil +} + func (ci *CertificateInfo) Create(path string, certBase string) error { if err := preCreateTasks(path); err != nil { return err @@ -156,156 +491,20 @@ func (ci *CertificateInfo) Create(path string, certBase string) error { } if ci.CreateType == "generate" { - // 1. Generate key - createCmd := "genrsa -aes256 -passout pass:foobar" - keySize := " 4096" - if strings.HasPrefix(ci.KeyType, "ecdsa") { - keySize = "" - createCmd = "ecparam -genkey -name " - if ci.KeyType == "ecdsa256" { - createCmd = createCmd + "prime256v1" - } - if ci.KeyType == "ecdsa384" { - createCmd = createCmd + "secp384r1" - } - } else { - if strings.HasSuffix(ci.KeyType, "3072") { - keySize = " 3072" - } - if strings.HasSuffix(ci.KeyType, "2048") { - keySize = " 2048" - } - } - - if _, err := exe_cmd("openssl " + createCmd + " -out " + path + certBase + ".key" + keySize); err != nil { - return reportError(err) - } - if _, err := exe_cmd("openssl pkey -in " + path + certBase + ".key -passin pass:foobar -out " + path + certBase + ".tmp"); err != nil { - return reportError(err) - } - if _, err = exe_cmd("mv " + path + certBase + ".tmp " + path + certBase + ".key"); err != nil { - return reportError(err) - } - - _, _ = exe_cmd("sleep 1") - - // 2. Generate certificate - subject := "/C=" + ci.Country + "/O=" + ci.Organization - if ci.OrgUnit != "" { - subject = subject + "/OU=" + ci.OrgUnit - } - subject = subject + "/CN=" + ci.CommonName - subject = strings.Replace(subject, " ", "\\\\", -1) - - if ci.IsRoot { - if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -days 3650 -new -x509 -extensions v3_ca -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".pem"); err != nil { - return reportError(err) - } - } else { - if _, err := exe_cmd("openssl req -config " + path + "openssl.cnf -new -subj " + subject + " -key " + path + certBase + ".key -out " + path + certBase + ".csr"); err != nil { - return reportError(err) - } - if _, err := exe_cmd("openssl ca -config " + path + "../openssl.cnf -extensions v3_intermediate_ca -days 3600 -md sha384 -notext -batch -in " + path + certBase + ".csr -out " + path + certBase + ".pem"); err != nil { - return reportError(err) - } - } - - } else if ci.CreateType == "import" { - tmpFile := filepath.Join(tmpDir, ci.ImportHandler.Filename) - - f, err := os.OpenFile(tmpFile, os.O_WRONLY|os.O_CREATE, 0666) + err := ci.Generate(path, certBase) if err != nil { return err } - defer f.Close() - - io.Copy(f, ci.ImportFile) - - contentType := ci.ImportHandler.Header.Get("Content-Type") - if contentType == "application/x-pkcs12" { - if ci.IsRoot { - if strings.Index(ci.ImportHandler.Filename, "labca_root") != 0 { - fmt.Printf("WARNING: importing root from .pfx file but name is %s\n", ci.ImportHandler.Filename) - } - } else { - if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { - fmt.Printf("WARNING: importing issuer from .pfx file but name is %s\n", ci.ImportHandler.Filename) - } - } - - pwd := "pass:dummy" - if ci.ImportPwd != "" { - pwd = "pass:" + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) - } - - if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nocerts -nodes -out " + tmpKey); err != nil { - if strings.Index(string(out), "invalid password") >= 0 { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } - if out, err := exe_cmd("openssl pkcs12 -in " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -password " + pwd + " -nokeys -out " + tmpCert); err != nil { - if strings.Index(string(out), "invalid password") >= 0 { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } - } else if contentType == "application/zip" { - if ci.IsRoot { - if (strings.Index(ci.ImportHandler.Filename, "labca_root") != 0) && (strings.Index(ci.ImportHandler.Filename, "labca_certificates") != 0) { - fmt.Printf("WARNING: importing root from .zip file but name is %s\n", ci.ImportHandler.Filename) - } - } else { - if strings.Index(ci.ImportHandler.Filename, "labca_issuer") != 0 { - fmt.Printf("WARNING: importing issuer from .zip file but name is %s\n", ci.ImportHandler.Filename) - } - } - - cmd := "unzip -j" - if ci.ImportPwd != "" { - cmd = cmd + " -P " + strings.Replace(ci.ImportPwd, " ", "\\\\", -1) - } else { - cmd = cmd + " -P dummy" - } - cmd = cmd + " " + strings.Replace(tmpFile, " ", "\\\\", -1) + " -d " + tmpDir - - if _, err := exe_cmd(cmd); err != nil { - if err.Error() == "exit status 82" { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } - } else { - return errors.New("Content Type '" + contentType + "' not supported!") - } - - } else if ci.CreateType == "upload" { - if err := ioutil.WriteFile(tmpKey, []byte(ci.Key), 0644); err != nil { + } else if ci.CreateType == "import" { + err := ci.Import(path, certBase, tmpDir, tmpKey, tmpCert) + if err != nil { return err } - pwd := "pass:dummy" - if ci.Passphrase != "" { - pwd = "pass:" + strings.Replace(ci.Passphrase, " ", "\\\\", -1) - } - - if out, err := exe_cmd("openssl pkey -passin " + pwd + " -in " + tmpKey + " -out " + tmpKey + "-out"); err != nil { - if strings.Index(string(out), ":bad decrypt:") >= 0 { - return errors.New("Incorrect password!") - } else { - return reportError(err) - } - } else { - if _, err = exe_cmd("mv " + tmpKey + "-out " + tmpKey); err != nil { - return reportError(err) - } - } - - if err := ioutil.WriteFile(tmpCert, []byte(ci.Certificate), 0644); err != nil { + } else if ci.CreateType == "upload" { + err := ci.Upload(path, certBase, tmpKey, tmpCert) + if err != nil { return err } @@ -315,130 +514,9 @@ func (ci *CertificateInfo) Create(path string, certBase string) error { // This is shared between pfx/zip upload and pem text upload if ci.CreateType != "generate" { - var rootCert string - var rootKey string - var issuerCert string - var issuerKey string - - if ci.IsRoot { - rootCert = filepath.Join(tmpDir, "root-ca.pem") - rootKey = filepath.Join(tmpDir, "root-ca.key") - - if _, err := os.Stat(rootCert); os.IsNotExist(err) { - return errors.New("File does not contain root-ca.pem!") - } - if _, err := os.Stat(rootKey); os.IsNotExist(err) { - return errors.New("File does not contain root-ca.key!") - } - } - - issuerCert = filepath.Join(tmpDir, "ca-int.pem") - issuerKey = filepath.Join(tmpDir, "ca-int.key") - - if _, err := os.Stat(issuerCert); os.IsNotExist(err) { - if ci.IsRoot { - issuerCert = "" - } else { - return errors.New("File does not contain ca-int.pem!") - } - } - if _, err := os.Stat(issuerKey); os.IsNotExist(err) { - if ci.IsRoot { - issuerKey = "" - } else { - return errors.New("File does not contain ca-int.key!") - } - } - - var rootSubject string - if (rootCert != "") && (rootKey != "") { - r, err := exe_cmd("openssl x509 -noout -subject -in " + rootCert) - if err != nil { - return reportError(err) - } else { - rootSubject = string(r[0 : len(r)-1]) - fmt.Printf("Import root with subject '%s'\n", rootSubject) - } - - r, err = exe_cmd("openssl pkey -noout -in " + rootKey) - if err != nil { - return reportError(err) - } else { - fmt.Println("Import root key") - } - } - - if (issuerCert != "") && (issuerKey != "") { - if ci.IsRoot { - if err := preCreateTasks(path + "issuer/"); err != nil { - return err - } - } - - r, err := exe_cmd("openssl x509 -noout -subject -in " + issuerCert) - if err != nil { - return reportError(err) - } else { - fmt.Printf("Import issuer with subject '%s'\n", string(r[0:len(r)-1])) - } - - r, err = exe_cmd("openssl x509 -noout -issuer -in " + issuerCert) - if err != nil { - return reportError(err) - } else { - issuerIssuer := string(r[0 : len(r)-1]) - fmt.Printf("Issuer certificate issued by CA '%s'\n", issuerIssuer) - - if rootSubject == "" { - r, err := exe_cmd("openssl x509 -noout -subject -in data/root-ca.pem") - if err != nil { - return reportError(err) - } else { - rootSubject = string(r[0 : len(r)-1]) - } - } - - issuerIssuer = strings.Replace(issuerIssuer, "issuer=", "", -1) - rootSubject = strings.Replace(rootSubject, "subject=", "", -1) - if issuerIssuer != rootSubject { - return errors.New("Issuer not issued by our Root CA!") - } - } - - r, err = exe_cmd("openssl pkey -noout -in " + issuerKey) - if err != nil { - return reportError(err) - } else { - fmt.Println("Import issuer key") - } - } - - // All is good now, move files to their permanent location... - if rootCert != "" { - if _, err = exe_cmd("mv " + rootCert + " " + path); err != nil { - return reportError(err) - } - } - if rootKey != "" { - if _, err = exe_cmd("mv " + rootKey + " " + path); err != nil { - return reportError(err) - } - } - if issuerCert != "" { - if _, err = exe_cmd("mv " + issuerCert + " data/issuer/"); err != nil { - return reportError(err) - } - } - if issuerKey != "" { - if _, err = exe_cmd("mv " + issuerKey + " data/issuer/"); err != nil { - return reportError(err) - } - } - - if (issuerCert != "") && (issuerKey != "") && ci.IsRoot { - if err := postCreateTasks(path+"issuer/", "ca-int"); err != nil { - return err - } + err := ci.Extract(path, certBase, tmpDir) + if err != nil { + return err } } diff --git a/gui/dashboard.go b/gui/dashboard.go index 6ae73f1..5748f9f 100644 --- a/gui/dashboard.go +++ b/gui/dashboard.go @@ -20,10 +20,7 @@ type Activity struct { Class string } -func _parseLine(line string, loc *time.Location) Activity { - var activity Activity - - // Remove ansi colors +func _removeAnsiColors(line string) string { b := make([]byte, len(line)) var bl int for i := 0; i < len(line); i++ { @@ -38,6 +35,14 @@ func _parseLine(line string, loc *time.Location) Activity { line = strings.Replace(line, "[1m", "", -1) line = strings.Replace(line, "[0m", "", -1) + return line +} + +func _parseLine(line string, loc *time.Location) Activity { + var activity Activity + + line = _removeAnsiColors(line) + re := regexp.MustCompile("^.*\\|\\s*(\\S)(\\S+) (\\S+) (\\S+) (.*)$") result := re.FindStringSubmatch(line) diff --git a/gui/main.go b/gui/main.go index 3f53ea3..0f75984 100644 --- a/gui/main.go +++ b/gui/main.go @@ -70,21 +70,7 @@ type User struct { Errors map[string]string } -func (reg *User) Validate(isNew bool, isChange bool) bool { - reg.Errors = make(map[string]string) - - if strings.TrimSpace(reg.Name) == "" { - reg.Errors["Name"] = "Please enter a user name" - } - - if isNew || isChange { - re := regexp.MustCompile(".+@.+\\..+") - matched := re.Match([]byte(reg.Email)) - if matched == false { - reg.Errors["Email"] = "Please enter a valid email address" - } - } - +func (reg *User) ValidatePassword(isNew bool, isChange bool) { blacklist := []string{"labca", "acme", reg.Name} if x := strings.Index(reg.Email, "@"); x > 0 { blacklist = append(blacklist, reg.Email[:x]) @@ -131,6 +117,24 @@ func (reg *User) Validate(isNew bool, isChange bool) bool { reg.Errors["Password"] = "Current password is not correct!" } } +} + +func (reg *User) Validate(isNew bool, isChange bool) bool { + reg.Errors = make(map[string]string) + + if strings.TrimSpace(reg.Name) == "" { + reg.Errors["Name"] = "Please enter a user name" + } + + if isNew || isChange { + re := regexp.MustCompile(".+@.+\\..+") + matched := re.Match([]byte(reg.Email)) + if matched == false { + reg.Errors["Email"] = "Please enter a valid email address" + } + } + + reg.ValidatePassword(isNew, isChange) return len(reg.Errors) == 0 } @@ -754,6 +758,256 @@ func _decrypt(ciphertext string) ([]byte, error) { return gcm.Open(nil, ct[:gcm.NonceSize()], ct[gcm.NonceSize():], nil) } +type Result struct { + Success bool + Message string + Timestamp string + TimestampRel string + Class string +} + +func (res *Result) ManageComponents(w http.ResponseWriter, r *http.Request, action string) { + components := _parseComponents(getLog(w, r, "components")) + for i := 0; i < len(components); i++ { + if (components[i].Name == "NGINX Webserver" && (action == "nginx-reload" || action == "nginx-restart")) || + (components[i].Name == "Host Service" && action == "svc-restart") || + (components[i].Name == "Boulder (ACME)" && (action == "boulder-start" || action == "boulder-stop" || action == "boulder-restart")) || + (components[i].Name == "LabCA Application" && action == "labca-restart") { + res.Timestamp = components[i].Timestamp + res.TimestampRel = components[i].TimestampRel + res.Class = components[i].Class + break + } + } +} + +func _managePostDispatch(w http.ResponseWriter, r *http.Request, action string) bool { + if action == "backup-restore" || action == "backup-delete" || action == "backup-now" { + _backupHandler(w, r) + return true + } + + if action == "cert-export" { + _exportHandler(w, r) + return true + } + + if action == "update-account" { + _accountUpdateHandler(w, r) + return true + } + + if action == "update-config" { + _configUpdateHandler(w, r) + return true + } + + if action == "update-email" { + _emailUpdateHandler(w, r) + return true + } + + if action == "send-email" { + _emailSendHandler(w, r) + return true + } + + return false +} + +func _managePost(w http.ResponseWriter, r *http.Request) { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return + } + + action := r.Form.Get("action") + actionKnown := false + for _, a := range []string { + "backup-restore", + "backup-delete", + "backup-now", + "cert-export", + "nginx-reload", + "nginx-restart", + "svc-restart", + "boulder-start", + "boulder-stop", + "boulder-restart", + "labca-restart", + "server-restart", + "server-shutdown", + "update-account", + "update-config", + "update-email", + "send-email", + } { + if a == action { + actionKnown = true + } + } + if !actionKnown { + errorHandler(w, r, errors.New(fmt.Sprintf("Unknown manage action '%s'", action)), http.StatusBadRequest) + return + } + + if _managePostDispatch(w, r, action) { + return + } + + res := &Result{ Success: true } + if !_hostCommand(w, r, action) { + res.Success = false + res.Message = "Command failed - see LabCA log for any details" + } + + if action != "server-restart" && action != "server-shutdown" { + res.ManageComponents(w, r, action) + } + + w.Header().Set("Content-Type", "application/json") + json.NewEncoder(w).Encode(res) +} + +func _manageGet(w http.ResponseWriter, r *http.Request) { + manageData := make(map[string]interface{}) + manageData["RequestBase"] = r.Header.Get("X-Request-Base") + + components := _parseComponents(getLog(w, r, "components")) + for i := 0; i < len(components); i++ { + if components[i].Name == "NGINX Webserver" { + components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/weberr" + components[i].LogTitle = "Web Error Log" + + btn := make(map[string]interface{}) + btn["Class"] = "btn-info" + btn["Id"] = "nginx-reload" + btn["Title"] = "Reload web server configuration with minimal impact to the users" + btn["Label"] = "Reload" + components[i].Buttons = append(components[i].Buttons, btn) + + btn = make(map[string]interface{}) + btn["Class"] = "btn-warning" + btn["Id"] = "nginx-restart" + btn["Title"] = "Restart the web server with some downtime for the users" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + } + + if components[i].Name == "Host Service" { + components[i].LogUrl = "" + components[i].LogTitle = "" + + btn := make(map[string]interface{}) + btn["Class"] = "btn-warning" + btn["Id"] = "svc-restart" + btn["Title"] = "Restart the host service" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + } + + if components[i].Name == "Boulder (ACME)" { + components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/boulder" + components[i].LogTitle = "ACME Log" + + btn := make(map[string]interface{}) + cls := "btn-success" + if components[i].TimestampRel != "stopped" { + cls = cls + " hidden" + } + btn["Class"] = cls + btn["Id"] = "boulder-start" + btn["Title"] = "Start the core ACME application" + btn["Label"] = "Start" + components[i].Buttons = append(components[i].Buttons, btn) + + btn = make(map[string]interface{}) + cls = "btn-warning" + if components[i].TimestampRel == "stopped" { + cls = cls + " hidden" + } + btn["Class"] = cls + btn["Id"] = "boulder-restart" + btn["Title"] = "Stop and restart the core ACME application" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + + btn = make(map[string]interface{}) + cls = "btn-danger" + if components[i].TimestampRel == "stopped" { + cls = cls + " hidden" + } + btn["Class"] = cls + btn["Id"] = "boulder-stop" + btn["Title"] = "Stop the core ACME application; users can no longer use ACME clients to interact with this instance" + btn["Label"] = "Stop" + components[i].Buttons = append(components[i].Buttons, btn) + } + + if components[i].Name == "LabCA Application" { + components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/labca" + components[i].LogTitle = "LabCA Log" + + btn := make(map[string]interface{}) + btn["Class"] = "btn-warning" + btn["Id"] = "labca-restart" + btn["Title"] = "Stop and restart this LabCA admin application" + btn["Label"] = "Restart" + components[i].Buttons = append(components[i].Buttons, btn) + } + } + manageData["Components"] = components + + stats := _parseStats(getLog(w, r, "stats")) + for _, stat := range stats { + if stat.Name == "System Uptime" { + manageData["ServerTimestamp"] = stat.Hint + manageData["ServerTimestampRel"] = stat.Value + break + } + } + + backupFiles := strings.Split(getLog(w, r, "backups"), "\n") + backupFiles = backupFiles[:len(backupFiles)-1] + manageData["BackupFiles"] = backupFiles + + manageData["RootDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/root-ca.pem") + manageData["IssuerDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/issuer/ca-int.pem") + + manageData["Fqdn"] = viper.GetString("labca.fqdn") + manageData["Organization"] = viper.GetString("labca.organization") + manageData["Dns"] = viper.GetString("labca.dns") + domain_mode := viper.GetString("labca.domain_mode") + manageData["DomainMode"] = domain_mode + if domain_mode == "lockdown" { + manageData["LockdownDomains"] = viper.GetString("labca.lockdown") + } + if domain_mode == "whitelist" { + manageData["WhitelistDomains"] = viper.GetString("labca.whitelist") + } + + manageData["DoEmail"] = viper.GetBool("labca.email.enable") + manageData["Server"] = viper.GetString("labca.email.server") + manageData["Port"] = viper.GetInt("labca.email.port") + manageData["EmailUser"] = viper.GetString("labca.email.user") + manageData["EmailPwd"] = "" + if viper.Get("labca.email.pass") != nil { + pwd := viper.GetString("labca.email.pass") + result, err := _decrypt(pwd) + if err == nil { + manageData["EmailPwd"] = string(result) + } else { + log.Printf("WARNING: could not decrypt email password: %s!\n", err.Error()) + } + } + manageData["From"] = viper.GetString("labca.email.from") + + manageData["Name"] = viper.GetString("user.name") + manageData["Email"] = viper.GetString("user.email") + + render(w, r, "manage", manageData) +} + func manageHandler(w http.ResponseWriter, r *http.Request) { if !viper.GetBool("config.complete") { http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusFound) @@ -761,232 +1015,9 @@ func manageHandler(w http.ResponseWriter, r *http.Request) { } if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } - - action := r.Form.Get("action") - switch action { - case "backup-restore": - case "backup-delete": - case "backup-now": - case "cert-export": - case "nginx-reload": - case "nginx-restart": - case "svc-restart": - case "boulder-start": - case "boulder-stop": - case "boulder-restart": - case "labca-restart": - case "server-restart": - case "server-shutdown": - case "update-account": - case "update-config": - case "update-email": - case "send-email": - default: - errorHandler(w, r, errors.New(fmt.Sprintf("Unknown manage action '%s'", action)), http.StatusBadRequest) - return - } - - if action == "backup-restore" || action == "backup-delete" || action == "backup-now" { - _backupHandler(w, r) - return - } - - if action == "cert-export" { - _exportHandler(w, r) - return - } - - if action == "update-account" { - _accountUpdateHandler(w, r) - return - } - - if action == "update-config" { - _configUpdateHandler(w, r) - return - } - - if action == "update-email" { - _emailUpdateHandler(w, r) - return - } - - if action == "send-email" { - _emailSendHandler(w, r) - return - } - - res := struct { - Success bool - Message string - Timestamp string - TimestampRel string - Class string - }{Success: true} - if !_hostCommand(w, r, action) { - res.Success = false - res.Message = "Command failed - see LabCA log for any details" - } - - if action != "server-restart" && action != "server-shutdown" { - components := _parseComponents(getLog(w, r, "components")) - for i := 0; i < len(components); i++ { - if (components[i].Name == "NGINX Webserver" && (action == "nginx-reload" || action == "nginx-restart")) || - (components[i].Name == "Host Service" && action == "svc-restart") || - (components[i].Name == "Boulder (ACME)" && (action == "boulder-start" || action == "boulder-stop" || action == "boulder-restart")) || - (components[i].Name == "LabCA Application" && action == "labca-restart") { - res.Timestamp = components[i].Timestamp - res.TimestampRel = components[i].TimestampRel - res.Class = components[i].Class - break - } - } - } - - w.Header().Set("Content-Type", "application/json") - json.NewEncoder(w).Encode(res) - + _managePost(w,r) } else { - manageData := make(map[string]interface{}) - manageData["RequestBase"] = r.Header.Get("X-Request-Base") - - components := _parseComponents(getLog(w, r, "components")) - for i := 0; i < len(components); i++ { - if components[i].Name == "NGINX Webserver" { - components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/weberr" - components[i].LogTitle = "Web Error Log" - - btn := make(map[string]interface{}) - btn["Class"] = "btn-info" - btn["Id"] = "nginx-reload" - btn["Title"] = "Reload web server configuration with minimal impact to the users" - btn["Label"] = "Reload" - components[i].Buttons = append(components[i].Buttons, btn) - - btn = make(map[string]interface{}) - btn["Class"] = "btn-warning" - btn["Id"] = "nginx-restart" - btn["Title"] = "Restart the web server with some downtime for the users" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - } - - if components[i].Name == "Host Service" { - components[i].LogUrl = "" - components[i].LogTitle = "" - - btn := make(map[string]interface{}) - btn["Class"] = "btn-warning" - btn["Id"] = "svc-restart" - btn["Title"] = "Restart the host service" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - } - - if components[i].Name == "Boulder (ACME)" { - components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/boulder" - components[i].LogTitle = "ACME Log" - - btn := make(map[string]interface{}) - cls := "btn-success" - if components[i].TimestampRel != "stopped" { - cls = cls + " hidden" - } - btn["Class"] = cls - btn["Id"] = "boulder-start" - btn["Title"] = "Start the core ACME application" - btn["Label"] = "Start" - components[i].Buttons = append(components[i].Buttons, btn) - - btn = make(map[string]interface{}) - cls = "btn-warning" - if components[i].TimestampRel == "stopped" { - cls = cls + " hidden" - } - btn["Class"] = cls - btn["Id"] = "boulder-restart" - btn["Title"] = "Stop and restart the core ACME application" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - - btn = make(map[string]interface{}) - cls = "btn-danger" - if components[i].TimestampRel == "stopped" { - cls = cls + " hidden" - } - btn["Class"] = cls - btn["Id"] = "boulder-stop" - btn["Title"] = "Stop the core ACME application; users can no longer use ACME clients to interact with this instance" - btn["Label"] = "Stop" - components[i].Buttons = append(components[i].Buttons, btn) - } - - if components[i].Name == "LabCA Application" { - components[i].LogUrl = r.Header.Get("X-Request-Base") + "/logs/labca" - components[i].LogTitle = "LabCA Log" - - btn := make(map[string]interface{}) - btn["Class"] = "btn-warning" - btn["Id"] = "labca-restart" - btn["Title"] = "Stop and restart this LabCA admin application" - btn["Label"] = "Restart" - components[i].Buttons = append(components[i].Buttons, btn) - } - } - manageData["Components"] = components - - stats := _parseStats(getLog(w, r, "stats")) - for _, stat := range stats { - if stat.Name == "System Uptime" { - manageData["ServerTimestamp"] = stat.Hint - manageData["ServerTimestampRel"] = stat.Value - break - } - } - - backupFiles := strings.Split(getLog(w, r, "backups"), "\n") - backupFiles = backupFiles[:len(backupFiles)-1] - manageData["BackupFiles"] = backupFiles - - manageData["RootDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/root-ca.pem") - manageData["IssuerDetails"] = _doCmdOutput(w, r, "openssl x509 -noout -text -in data/issuer/ca-int.pem") - - manageData["Fqdn"] = viper.GetString("labca.fqdn") - manageData["Organization"] = viper.GetString("labca.organization") - manageData["Dns"] = viper.GetString("labca.dns") - domain_mode := viper.GetString("labca.domain_mode") - manageData["DomainMode"] = domain_mode - if domain_mode == "lockdown" { - manageData["LockdownDomains"] = viper.GetString("labca.lockdown") - } - if domain_mode == "whitelist" { - manageData["WhitelistDomains"] = viper.GetString("labca.whitelist") - } - - manageData["DoEmail"] = viper.GetBool("labca.email.enable") - manageData["Server"] = viper.GetString("labca.email.server") - manageData["Port"] = viper.GetInt("labca.email.port") - manageData["EmailUser"] = viper.GetString("labca.email.user") - manageData["EmailPwd"] = "" - if viper.Get("labca.email.pass") != nil { - pwd := viper.GetString("labca.email.pass") - result, err := _decrypt(pwd) - if err == nil { - manageData["EmailPwd"] = string(result) - } else { - log.Printf("WARNING: could not decrypt email password: %s!\n", err.Error()) - } - } - manageData["From"] = viper.GetString("labca.email.from") - - manageData["Name"] = viper.GetString("user.name") - manageData["Email"] = viper.GetString("user.email") - - render(w, r, "manage", manageData) + _manageGet(w,r) } } @@ -1173,6 +1204,42 @@ func wsHandler(w http.ResponseWriter, r *http.Request) { reader(ws) } +func _buildCI(r *http.Request, session *sessions.Session, isRoot bool) *CertificateInfo { + ci := &CertificateInfo{ + IsRoot: isRoot, + CreateType: "generate", + CommonName: "Root CA", + RequestBase: r.Header.Get("X-Request-Base"), + } + if !isRoot { + ci.CommonName = "CA" + } + ci.Initialize() + + if session.Values["ct"] != nil { + ci.CreateType = session.Values["ct"].(string) + } + if session.Values["kt"] != nil { + ci.KeyType = session.Values["kt"].(string) + } + if session.Values["c"] != nil { + ci.Country = session.Values["c"].(string) + } + if session.Values["o"] != nil { + ci.Organization = session.Values["o"].(string) + } + if session.Values["ou"] != nil { + ci.OrgUnit = session.Values["ou"].(string) + } + if session.Values["cn"] != nil { + ci.CommonName = session.Values["cn"].(string) + ci.CommonName = strings.Replace(ci.CommonName, "Root", "", -1) + ci.CommonName = strings.Replace(ci.CommonName, " ", " ", -1) + } + + return ci +} + func _certCreate(w http.ResponseWriter, r *http.Request, certBase string, isRoot bool) bool { path := "data/" if !isRoot { @@ -1183,37 +1250,7 @@ func _certCreate(w http.ResponseWriter, r *http.Request, certBase string, isRoot session := getSession(w, r) if r.Method == "GET" { - ci := &CertificateInfo{ - IsRoot: isRoot, - CreateType: "generate", - CommonName: "Root CA", - RequestBase: r.Header.Get("X-Request-Base"), - } - if !isRoot { - ci.CommonName = "CA" - } - ci.Initialize() - - if session.Values["ct"] != nil { - ci.CreateType = session.Values["ct"].(string) - } - if session.Values["kt"] != nil { - ci.KeyType = session.Values["kt"].(string) - } - if session.Values["c"] != nil { - ci.Country = session.Values["c"].(string) - } - if session.Values["o"] != nil { - ci.Organization = session.Values["o"].(string) - } - if session.Values["ou"] != nil { - ci.OrgUnit = session.Values["ou"].(string) - } - if session.Values["cn"] != nil { - ci.CommonName = session.Values["cn"].(string) - ci.CommonName = strings.Replace(ci.CommonName, "Root", "", -1) - ci.CommonName = strings.Replace(ci.CommonName, " ", " ", -1) - } + ci := _buildCI(r, session, isRoot) render(w, r, "cert:manage", map[string]interface{}{"CertificateInfo": ci, "Progress": _progress(certBase), "HelpText": _helptext(certBase)}) return false @@ -1491,13 +1528,128 @@ func _helptext(stage string) template.HTML { "you can either generate a fresh certificate or import an existing one, as long as it is signed by\n", "the Root CA from the previous step.

\n", "

If you want to generate a certificate, by default the same key type and strength is selected as\n", - "was choosen in the previous step when generating the root, but you may choose a different one. By\n", + "was chosen in the previous step when generating the root, but you may choose a different one. By\n", "default the common name is the same as the CN for the Root CA, minus the word 'Root'.

")) } else { return template.HTML("") } } +func _setupAdminUser(w http.ResponseWriter, r *http.Request) bool { + if r.Method == "GET" { + reg := &User{ + RequestBase: r.Header.Get("X-Request-Base"), + } + render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) + return false + } else if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } + + reg := &User{ + Name: r.Form.Get("username"), + Email: r.Form.Get("email"), + Password: r.Form.Get("password"), + Confirm: r.Form.Get("confirm"), + RequestBase: r.Header.Get("X-Request-Base"), + } + + if reg.Validate(true, false) == false { + render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) + return false + } + + hash, err := bcrypt.GenerateFromPassword([]byte(reg.Password), bcrypt.MinCost) + if err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } + viper.Set("user.name", reg.Name) + viper.Set("user.email", reg.Email) + viper.Set("user.password", string(hash)) + viper.WriteConfig() + + session := getSession(w, r) + session.Values["user"] = reg.Name + session.Save(r, w) + + // Fake the method to GET as we need to continue in the setupHandler() function + r.Method = "GET" + } else { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + return false + } + + return true +} + +func _setupBaseConfig(w http.ResponseWriter, r *http.Request) bool { + if r.Method == "GET" { + domain := viper.GetString("labca.fqdn") + pos := strings.Index(domain, ".") + if pos > -1 { + pos = pos + 1 + domain = domain[pos:] + } + + cfg := &SetupConfig{ + Fqdn: viper.GetString("labca.fqdn"), + DomainMode: "lockdown", + LockdownDomains: domain, + WhitelistDomains: domain, + RequestBase: r.Header.Get("X-Request-Base"), + } + + render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) + return false + } else if r.Method == "POST" { + if err := r.ParseForm(); err != nil { + errorHandler(w, r, err, http.StatusInternalServerError) + return false + } + + cfg := &SetupConfig{ + Fqdn: r.Form.Get("fqdn"), + Dns: r.Form.Get("dns"), + DomainMode: r.Form.Get("domain_mode"), + LockdownDomains: r.Form.Get("lockdown_domains"), + WhitelistDomains: r.Form.Get("whitelist_domains"), + RequestBase: r.Header.Get("X-Request-Base"), + } + + if cfg.Validate(false) == false { + render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) + return false + } + + matched, err := regexp.MatchString(":\\d+$", cfg.Dns) + if err == nil && !matched { + cfg.Dns += ":53" + } + + viper.Set("labca.fqdn", cfg.Fqdn) + viper.Set("labca.dns", cfg.Dns) + viper.Set("labca.domain_mode", cfg.DomainMode) + if cfg.DomainMode == "lockdown" { + viper.Set("labca.lockdown", cfg.LockdownDomains) + } + if cfg.DomainMode == "whitelist" { + viper.Set("labca.whitelist", cfg.WhitelistDomains) + } + viper.WriteConfig() + + // Fake the method to GET as we need to continue in the setupHandler() function + r.Method = "GET" + } else { + http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + return false + } + + return true +} + func setupHandler(w http.ResponseWriter, r *http.Request) { if viper.GetBool("config.complete") == true { render(w, r, "index:manage", map[string]interface{}{"Message": template.HTML("Setup already completed! Go home")}) @@ -1506,113 +1658,14 @@ func setupHandler(w http.ResponseWriter, r *http.Request) { // 1. Setup admin user if viper.Get("user.password") == nil { - if r.Method == "GET" { - reg := &User{ - RequestBase: r.Header.Get("X-Request-Base"), - } - render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) - return - } else if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } - - reg := &User{ - Name: r.Form.Get("username"), - Email: r.Form.Get("email"), - Password: r.Form.Get("password"), - Confirm: r.Form.Get("confirm"), - RequestBase: r.Header.Get("X-Request-Base"), - } - - if reg.Validate(true, false) == false { - render(w, r, "register:manage", map[string]interface{}{"User": reg, "IsLogin": true, "Progress": _progress("register"), "HelpText": _helptext("register")}) - return - } - - hash, err := bcrypt.GenerateFromPassword([]byte(reg.Password), bcrypt.MinCost) - if err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } - viper.Set("user.name", reg.Name) - viper.Set("user.email", reg.Email) - viper.Set("user.password", string(hash)) - viper.WriteConfig() - - session := getSession(w, r) - session.Values["user"] = reg.Name - session.Save(r, w) - - // Fake the method to GET as we need to continue in the setupHandler() function - r.Method = "GET" - } else { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + if !_setupAdminUser(w, r) { return } } // 2. Setup essential configuration if viper.Get("labca.dns") == nil { - if r.Method == "GET" { - domain := viper.GetString("labca.fqdn") - pos := strings.Index(domain, ".") - if pos > -1 { - pos = pos + 1 - domain = domain[pos:] - } - - cfg := &SetupConfig{ - Fqdn: viper.GetString("labca.fqdn"), - DomainMode: "lockdown", - LockdownDomains: domain, - WhitelistDomains: domain, - RequestBase: r.Header.Get("X-Request-Base"), - } - - render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) - return - } else if r.Method == "POST" { - if err := r.ParseForm(); err != nil { - errorHandler(w, r, err, http.StatusInternalServerError) - return - } - - cfg := &SetupConfig{ - Fqdn: r.Form.Get("fqdn"), - Dns: r.Form.Get("dns"), - DomainMode: r.Form.Get("domain_mode"), - LockdownDomains: r.Form.Get("lockdown_domains"), - WhitelistDomains: r.Form.Get("whitelist_domains"), - RequestBase: r.Header.Get("X-Request-Base"), - } - - if cfg.Validate(false) == false { - render(w, r, "setup:manage", map[string]interface{}{"SetupConfig": cfg, "Progress": _progress("setup"), "HelpText": _helptext("setup")}) - return - } - - matched, err := regexp.MatchString(":\\d+$", cfg.Dns) - if err == nil && !matched { - cfg.Dns += ":53" - } - - viper.Set("labca.fqdn", cfg.Fqdn) - viper.Set("labca.dns", cfg.Dns) - viper.Set("labca.domain_mode", cfg.DomainMode) - if cfg.DomainMode == "lockdown" { - viper.Set("labca.lockdown", cfg.LockdownDomains) - } - if cfg.DomainMode == "whitelist" { - viper.Set("labca.whitelist", cfg.WhitelistDomains) - } - viper.WriteConfig() - - // Fake the method to GET as we need to continue in the setupHandler() function - r.Method = "GET" - } else { - http.Redirect(w, r, r.Header.Get("X-Request-Base")+"/setup", http.StatusSeeOther) + if !_setupBaseConfig(w, r) { return } } @@ -1935,23 +1988,15 @@ type navItem struct { SubMenu []navItem } -func activeNav(active string, uri string, requestBase string) []navItem { - isAcmeActive := (uri == "/accounts" || strings.HasPrefix(uri, "/accounts/") || - uri == "/orders" || strings.HasPrefix(uri, "/orders/") || - uri == "/authz" || strings.HasPrefix(uri, "/authz/") || - uri == "/challenges" || strings.HasPrefix(uri, "/challenges/") || - uri == "/certificates" || strings.HasPrefix(uri, "/certificates/") || - false) +func _matchPrefix(uri string, prefix string) bool { + return (uri == prefix || strings.HasPrefix(uri, prefix + "/")) +} + +func _acmeNav(active string, uri string, requestBase string) navItem { + isAcmeActive := _matchPrefix(uri, "/accounts") || _matchPrefix(uri, "/orders") || + _matchPrefix(uri, "/authz") || _matchPrefix(uri, "/challenges" ) || + _matchPrefix(uri, "/certificates") || false - // create menu items - home := navItem{ - Name: "Dashboard", - Icon: "fa-dashboard", - Attrs: map[template.HTMLAttr]string{ - "href": requestBase + "/", - "title": "Main page with the status of the system", - }, - } accounts := navItem{ Name: "Accounts", Icon: "fa-list-alt", @@ -2002,6 +2047,35 @@ func activeNav(active string, uri string, requestBase string) []navItem { IsActive: isAcmeActive, SubMenu: []navItem{accounts, certificates, orders, authz, challenges}, } + + // set active menu class + switch active { + case "accounts": + accounts.Attrs["class"] = "active" + case "certificates": + certificates.Attrs["class"] = "active" + case "orders": + orders.Attrs["class"] = "active" + case "authz": + authz.Attrs["class"] = "active" + case "challenges": + challenges.Attrs["class"] = "active" + } + + return acme +} + +func activeNav(active string, uri string, requestBase string) []navItem { + // create menu items + home := navItem{ + Name: "Dashboard", + Icon: "fa-dashboard", + Attrs: map[template.HTMLAttr]string{ + "href": requestBase + "/", + "title": "Main page with the status of the system", + }, + } + acme := _acmeNav(active, uri, requestBase) cert := navItem{ Name: "Web Certificate", Icon: "fa-lock", @@ -2057,6 +2131,7 @@ func activeNav(active string, uri string, requestBase string) []navItem { "href": "#", "title": "Log Files", }, + IsActive: strings.HasPrefix(uri, "/logs/"), SubMenu: []navItem{cert, boulder, audit, labca, web, weberr}, } manage := navItem{ @@ -2088,22 +2163,10 @@ func activeNav(active string, uri string, requestBase string) []navItem { switch active { case "about": about.Attrs["class"] = "active" - case "accounts": - accounts.Attrs["class"] = "active" - case "orders": - orders.Attrs["class"] = "active" - case "authz": - authz.Attrs["class"] = "active" - case "challenges": - challenges.Attrs["class"] = "active" - case "certificates": - certificates.Attrs["class"] = "active" case "index": home.Attrs["class"] = "active" case "manage": manage.Attrs["class"] = "active" - case "logs": - logs.Attrs["class"] = "active" } return []navItem{home, acme, logs, manage, about, public} From 653b023759bb7039b615acd928c61130e68beaa8 Mon Sep 17 00:00:00 2001 From: Arjan H Date: Sat, 24 Nov 2018 10:53:06 +0100 Subject: [PATCH 3/3] Update datatables to fix issue with clicking on some rows --- www/js/dataTables.bootstrap.min.js | 12 +- www/js/jquery.dataTables.min.js | 170 +---------------------------- www/js/labca.js | 5 +- 3 files changed, 11 insertions(+), 176 deletions(-) diff --git a/www/js/dataTables.bootstrap.min.js b/www/js/dataTables.bootstrap.min.js index f0d09b9..c27dcb5 100644 --- a/www/js/dataTables.bootstrap.min.js +++ b/www/js/dataTables.bootstrap.min.js @@ -1,8 +1,4 @@ -/*! - DataTables Bootstrap 3 integration - ©2011-2014 SpryMedia Ltd - datatables.net/license -*/ -(function(){var f=function(c,b){c.extend(!0,b.defaults,{dom:"<'row'<'col-sm-6'l><'col-sm-6'f>><'row'<'col-sm-12'tr>><'row'<'col-sm-6'i><'col-sm-6'p>>",renderer:"bootstrap"});c.extend(b.ext.classes,{sWrapper:"dataTables_wrapper form-inline dt-bootstrap",sFilterInput:"form-control input-sm",sLengthSelect:"form-control input-sm"});b.ext.renderer.pageButton.bootstrap=function(g,f,p,k,h,l){var q=new b.Api(g),r=g.oClasses,i=g.oLanguage.oPaginate,d,e,o=function(b,f){var j,m,n,a,k=function(a){a.preventDefault(); -c(a.currentTarget).hasClass("disabled")||q.page(a.data.action).draw(!1)};j=0;for(m=f.length;j",{"class":r.sPageButton+" "+ -e,"aria-controls":g.sTableId,tabindex:g.iTabIndex,id:0===p&&"string"===typeof a?g.sTableId+"_"+a:null}).append(c("",{href:"#"}).html(d)).appendTo(b),g.oApi._fnBindAction(n,{action:a},k))}};o(c(f).empty().html('