Cross-platform desktop application for greenhouse
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

157 lines
4.5 KiB

package main
import (
"crypto/x509"
"encoding/pem"
"fmt"
"io/ioutil"
"log"
"net"
"net/http"
"os"
"path"
"runtime"
"time"
"git.sequentialread.com/forest/easypki.git/pkg/easypki"
easypkiStore "git.sequentialread.com/forest/easypki.git/pkg/store"
greenhousePKI "git.sequentialread.com/forest/greenhouse/pki"
errors "git.sequentialread.com/forest/pkg-errors"
)
type daemonAPI struct {
HTTPClient *http.Client
}
type ThresholdConfig struct {
DebugLog bool
ClientId string
GreenhouseDomain string
GreenhouseAPIKey string
GreenhouseThresholdPort int
CaCertificate string
// TODO rename Tls to TLS
ClientTlsKey string
ClientTlsCertificate string
AdminAPIPort int
AdminAPICACertificate string
AdminAPITlsKey string
AdminAPITlsCertificate string
// TODO Metrics MetricsConfig
}
const mainCAName = "greenhouse_daemon_localhost_ca"
func main() {
var err error
daemonPath := ""
if runtime.GOOS == "linux" || runtime.GOOS == "bsd" {
daemonPath = "/opt/greenhouse-daemon"
} else if runtime.GOOS == "darwin" {
daemonPath = "/Library/Application Support/greenhouse-daemon"
} else if runtime.GOOS == "windows" {
daemonPath = fmt.Sprintf(`%s\greenhouse-daemon`, os.Getenv("ProgramData"))
} else {
log.Fatalf("can't start the greenhouse-daemon because operating system '%s' is not supported yet", runtime.GOOS)
}
missingAnyCertsOrKeys := false
certsAndKeysToCheck := []string{
fmt.Sprintf("%s.crt", mainCAName),
"daemon.crt",
"daemon.key",
}
for _, filename := range certsAndKeysToCheck {
_, err := os.Stat(path.Join(daemonPath, filename))
if os.IsNotExist(err) {
missingAnyCertsOrKeys = true
break
}
}
if missingAnyCertsOrKeys {
err = GenerateTLSCertificatesAndKeys(daemonPath)
if err != nil {
log.Fatalf("can't start the greenhouse-daemon because GenerateTLSCertificatesAndKeys returned %+v", err)
}
}
certPath := path.Join(daemonPath, "daemon.crt")
keyPath := path.Join(daemonPath, "daemon.key")
daemonAPIInstance := daemonAPI{
HTTPClient: &http.Client{
Timeout: time.Second * 10,
},
}
err = http.ListenAndServeTLS("127.0.0.1:9572", certPath, keyPath, daemonAPIInstance)
log.Fatalf("http.ListenAndServe returned %+v", err)
}
func (handler daemonAPI) ServeHTTP(response http.ResponseWriter, request *http.Request) {
switch path.Clean(request.URL.Path) {
case "/register":
if request.Method == "POST" {
query := request.URL.Query()
nodeId := query.Get("nodeId")
apiKey := query.Get("apiKey")
request, err := http.NewRequest("POST", fmt.Sprintf("https://forest-n-johnson.greenhouseusers.com/api/register/%s", nodeId), nil)
if err != nil {
http.Error(response, "greenhouse-daemon: 500 internal server error", http.StatusBadGateway)
return
}
request.Header().Add("Authorization", fmt.Sprintf("Bearer %s", apiKey))
client.Do(request)
} else {
response.Header().Set("Allow", "POST")
http.Error(response, "greenhouse-daemon: 405 method not allowed, try POST", http.StatusMethodNotAllowed)
}
default:
http.Error(response, "greenhouse-daemon: 404 not found, try POST /register", http.StatusNotFound)
}
}
func GenerateTLSCertificatesAndKeys(daemonPath string) error {
pkiService := greenhousePKI.NewPKIService(&easypki.EasyPKI{Store: &easypkiStore.InMemory{}})
mainCA, err := pkiService.GetCACertificate(mainCAName)
if err != nil {
return errors.Wrap(err, "GetCACertificate")
}
mainCABytes := pem.EncodeToMemory(&pem.Block{
Bytes: mainCA.Raw,
Type: "CERTIFICATE",
})
err = ioutil.WriteFile(path.Join(daemonPath, fmt.Sprintf("%s.crt", mainCAName)), mainCABytes, 0600)
if err != nil {
return errors.Wrap(err, "Write daemon CA certificate")
}
expiry := time.Now().Add(time.Hour * time.Duration(24*31*12*99))
daemonKey, daemonCert, err := pkiService.GetServerKeyPair(mainCAName, "localhost", []net.IP{net.ParseIP("127.0.0.1")}, expiry)
if err != nil {
return errors.Wrap(err, "GetServerKeyPair")
}
daemonKeyBytes := pem.EncodeToMemory(&pem.Block{
Bytes: x509.MarshalPKCS1PrivateKey(daemonKey),
Type: "RSA PRIVATE KEY",
})
daemonCertBytes := pem.EncodeToMemory(&pem.Block{
Bytes: daemonCert.Raw,
Type: "CERTIFICATE",
})
err = ioutil.WriteFile(path.Join(daemonPath, "daemon.crt"), daemonCertBytes, 0600)
if err != nil {
return errors.Wrap(err, "Write daemon TLS certificate")
}
err = ioutil.WriteFile(path.Join(daemonPath, "daemon.key"), daemonKeyBytes, 0600)
if err != nil {
return errors.Wrap(err, "Write daemon TLS key")
}
return nil
}