diff --git a/Dockerfile b/Dockerfile index 912db45..f85850c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -30,7 +30,6 @@ RUN pip3 install -r requirements.txt WORKDIR /bumper # Copy only required folders instead of all -COPY create_certs/ create_certs/ COPY bumper/ bumper/ ENTRYPOINT ["python3", "-m", "bumper"] diff --git a/bumper/__init__.py b/bumper/__init__.py index 667232a..8bd1b65 100644 --- a/bumper/__init__.py +++ b/bumper/__init__.py @@ -189,54 +189,6 @@ async def shutdown(): bumperlog.info("Shutdown complete") -def create_certs(): - import platform - import os - import subprocess - import sys - - path = os.path.dirname(sys.modules[__name__].__file__) - path = os.path.join(path, "..") - sys.path.insert(0, path) - - print("Creating certificates") - odir = os.path.realpath(os.curdir) - os.chdir("certs") - if str(platform.system()).lower() == "windows": - # run for win - subprocess.run([os.path.join("..", "create_certs", "create_certs_windows.exe")]) - elif str(platform.system()).lower() == "darwin": - # run on mac - subprocess.run([os.path.join("..", "create_certs", "create_certs_osx")]) - elif str(platform.system()).lower() == "linux": - if "arm" in platform.machine().lower() or "aarch64" in platform.machine().lower(): - # run for pi - subprocess.run([os.path.join("..", "create_certs", "create_certs_rpi")]) - else: - # run for linux - subprocess.run([os.path.join("..", "create_certs", "create_certs_linux")]) - - else: - os.chdir(odir) - bumperlog.fatal("Can't determine platform. Create certs manually and try again.") - return - - print("Certificates created") - os.chdir(odir) - - if "__main__.py" in sys.argv[0]: - os.execv( - sys.executable, ["python", "-m", "bumper"] + sys.argv[1:] - ) # Start again - - else: - os.execv(sys.executable, ["python"] + sys.argv) # Start again - - -def first_run(): - create_certs() - - def main(argv=None): import argparse @@ -252,8 +204,9 @@ def main(argv=None): and os.path.exists(server_cert) and os.path.exists(server_key) ): - first_run() - return + msg = "No certs found! Please generate them (More infos in the docs)" + bumperlog.fatal(msg) + sys.exit(msg) if not ( os.path.exists(os.path.join(data_dir, "passwd")) diff --git a/create_certs/Bumper_SAN.txt b/create_certs/Bumper_SAN.txt deleted file mode 100644 index 2ffe727..0000000 --- a/create_certs/Bumper_SAN.txt +++ /dev/null @@ -1,10 +0,0 @@ -ecovacs.com -*.ecovacs.com -ecouser.net -*.ecouser.net -ecovacs.net -*.ecovacs.net -*.ww.ecouser.net -*.dc-eu.ww.ecouser.net -*.dc.ww.ecouser.net -*.area.ww.ecouser.net \ No newline at end of file diff --git a/create_certs/create_certs_linux b/create_certs/create_certs_linux deleted file mode 100755 index c1a0448..0000000 Binary files a/create_certs/create_certs_linux and /dev/null differ diff --git a/create_certs/create_certs_osx b/create_certs/create_certs_osx deleted file mode 100755 index e29d2dd..0000000 Binary files a/create_certs/create_certs_osx and /dev/null differ diff --git a/create_certs/create_certs_rpi b/create_certs/create_certs_rpi deleted file mode 100755 index 76d2201..0000000 Binary files a/create_certs/create_certs_rpi and /dev/null differ diff --git a/create_certs/create_certs_windows.exe b/create_certs/create_certs_windows.exe deleted file mode 100755 index 244385a..0000000 Binary files a/create_certs/create_certs_windows.exe and /dev/null differ diff --git a/create_certs/src/build.sh b/create_certs/src/build.sh deleted file mode 100755 index 03e85bb..0000000 --- a/create_certs/src/build.sh +++ /dev/null @@ -1,12 +0,0 @@ -#!/bin/bash -SCRIPTPATH=$(dirname "$BASH_SOURCE") -OUTPUTPATH=$(dirname $(dirname "$BASH_SOURCE")) -echo "Building for Windows: $OUTPUTPATH/create_certs_windows.exe" -GOOS=windows go build -o $OUTPUTPATH/create_certs_windows.exe $SCRIPTPATH/create_certs.go -echo "Building for OSX $OUTPUTPATH/create_certs_osx" -GOOS=darwin go build -o $OUTPUTPATH/create_certs_osx $SCRIPTPATH/create_certs.go -echo "Building for Linux $OUTPUTPATH/create_certs_linux" -GOOS=linux go build -o $OUTPUTPATH/create_certs_linux $SCRIPTPATH/create_certs.go -echo "Building for ARM (Raspberry Pi) $OUTPUTPATH/create_certs_rpi" -GOOS=linux GOARCH=arm GOARM=5 go build -o $OUTPUTPATH/create_certs_rpi $SCRIPTPATH/create_certs.go -echo "Build Complete" \ No newline at end of file diff --git a/create_certs/src/create_certs.go b/create_certs/src/create_certs.go deleted file mode 100644 index 8607c5d..0000000 --- a/create_certs/src/create_certs.go +++ /dev/null @@ -1,262 +0,0 @@ -package main - -import ( - "bufio" - "crypto/rand" - "crypto/rsa" - "crypto/sha1" - "crypto/tls" - "crypto/x509" - "crypto/x509/pkix" - "encoding/pem" - "flag" - "fmt" - "log" - "math/big" - "net" - "os" - "path/filepath" - "time" -) - -var InBumperSan string -var OutCertDirectory string - -func setFlags() { - - exePath, _ := os.Executable() - currentDir, _ := os.Getwd() - - //certPath will be current working directory by defaultß - certPath, err := filepath.Abs(currentDir) - if err != nil { - log.Printf("Error: %v", err) - } - - //sanPath will be exe path /Bumper_SAN.txt by default - sanPath, err := filepath.Abs(filepath.Join(exePath, "..", "/Bumper_SAN.txt")) - if err != nil { - log.Printf("Error: %v", err) - } - - flag.StringVar(&InBumperSan, "inSAN", sanPath, "Input file containing a list of Subject Alternate Names (line separated)") - flag.StringVar(&OutCertDirectory, "out", certPath, "Directory to output certificates to") - - flag.Parse() - -} -func main() { - - setFlags() - - fmt.Printf("-------- Create_Certs --------\n") - - //get absolute path - outCertDirectory, _ := filepath.Abs(OutCertDirectory) - dexists, isdfile := pathExistsType(outCertDirectory) - if !dexists { - log.Fatalf("Certs directory doesn't exist: %v", outCertDirectory) - } - if isdfile { - log.Fatalf("Certs directory is a file, not a directory: %v", outCertDirectory) - } - - //get absolute path - inBumperSan, _ := filepath.Abs(InBumperSan) - bexists, isbfile := pathExistsType(inBumperSan) - if !bexists { - log.Printf("Bumper SAN doesn't exist, certificate won't contain Subject Alternate Names: %v\n", inBumperSan) - inBumperSan = "" - } - if bexists && !isbfile { - log.Printf("Bumper SAN is a directory instead of file, certificate won't contain Subject Alternate Names: %v\n", inBumperSan) - inBumperSan = "" - } - - fmt.Printf("-------- Starting Certificate Creation --------\n") - fmt.Printf("Options: \n Output Directory: %v\n Input SAN List: %v\n", outCertDirectory, inBumperSan) - - make_CA(outCertDirectory) - signCert(outCertDirectory, inBumperSan) - - fmt.Printf("-------- Certificate Creation Complete --------\n") -} - -func make_CA(outCertDirectory string) { - - fmt.Printf("-------- Creating CA Cert --------\n") - priv, _ := rsa.GenerateKey(rand.Reader, 2048) - pub := &priv.PublicKey - - ca := &x509.Certificate{ - SerialNumber: big.NewInt(1653), - Subject: pkix.Name{ - CommonName: string("Bumper CA"), - Organization: []string{"Bumper"}, - }, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(2, 0, 0), - SubjectKeyId: bigIntHash(priv.N), - AuthorityKeyId: bigIntHash(priv.N), - KeyUsage: x509.KeyUsageCertSign | x509.KeyUsageCRLSign, - ExtKeyUsage: []x509.ExtKeyUsage{}, - IsCA: true, - BasicConstraintsValid: true, - MaxPathLen: -1, - } - - ca_b, err := x509.CreateCertificate(rand.Reader, ca, ca, pub, priv) - if err != nil { - log.Fatalf("Create ca failed: %v", err) - } - - // Public key - certOut, err := os.Create(filepath.Join(outCertDirectory, "ca.crt")) - if err != nil { - log.Fatalf("Create ca.crt failed: %v", err) - } - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: ca_b}) - certOut.Close() - log.Printf("ca.crt created at %v\n", filepath.Join(outCertDirectory, "ca.crt")) - - // Private key - keyOut, err := os.OpenFile(filepath.Join(outCertDirectory, "ca.key"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - log.Fatalf("Create ca.key failed: %v", err) - } - pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)}) - keyOut.Close() - log.Printf("ca.key created at %v\n", filepath.Join(outCertDirectory, "ca.key")) -} - -func signCert(outCertDirectory string, inBumperSan string) { - - fmt.Printf("-------- Creating Server Cert --------\n") - // Load CA - catls, err := tls.LoadX509KeyPair(filepath.Join(outCertDirectory, "ca.crt"), filepath.Join(outCertDirectory, "ca.key")) - if err != nil { - log.Fatalf("Error loading ca cert: %v", err) - } - - ca, err := x509.ParseCertificate(catls.Certificate[0]) - if err != nil { - log.Fatalf("Error parsing ca cert: %v", err) - } - - hostname, _ := os.Hostname() - ifaces, err := net.Interfaces() - var ips []net.IP - // handle err - for _, i := range ifaces { - addrs, _ := i.Addrs() - // handle err - for _, addr := range addrs { - var ip net.IP - switch v := addr.(type) { - case *net.IPNet: - ip = v.IP - case *net.IPAddr: - ip = v.IP - } - ips = append(ips, ip) - // process IP address - } - } - ips = append(ips, net.ParseIP("127.0.0.1")) - - privateKey, _ := rsa.GenerateKey(rand.Reader, 2048) - pubKey := &privateKey.PublicKey - - //DNS/SAN names for cert - dnsNames := []string{hostname, "localhost"} - //Read SANs from file - //get absolute path - - bexists, isbfile := pathExistsType(inBumperSan) - if !bexists { - log.Print("Bumper SAN doesn't exist, certificate won't contain Subject Alternate Names") - } - if bexists && !isbfile { - log.Print("Bumper SAN is a directory instead of file, certificate won't contain Subject Alternate Names") - } - if bexists && isbfile { - sans, err := readLines(inBumperSan) - if err != nil { - log.Printf("Error reading %v certificates will be created without Subject Alternate Names: %v", inBumperSan, err) - } - dnsNames = append(dnsNames, sans...) - } - - // Prepare certificate - template := x509.Certificate{ - SerialNumber: big.NewInt(1658), - Issuer: ca.Subject, - Subject: pkix.Name{ - CommonName: "Bumper Server", - Organization: []string{"Bumper"}, - }, - SubjectKeyId: bigIntHash(privateKey.N), - AuthorityKeyId: ca.RawSubject, - NotBefore: time.Now(), - NotAfter: time.Now().AddDate(2, 0, 0), - IsCA: false, - BasicConstraintsValid: true, - ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}, - KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment, - DNSNames: dnsNames, - IPAddresses: ips, - } - - // Sign the certificate - cert_b, err := x509.CreateCertificate(rand.Reader, &template, ca, pubKey, catls.PrivateKey) - - // Public key - certOut, err := os.Create(filepath.Join(outCertDirectory, "bumper.crt")) - if err != nil { - log.Fatalf("Create bumper.crt failed: %v", err) - } - pem.Encode(certOut, &pem.Block{Type: "CERTIFICATE", Bytes: cert_b}) - certOut.Close() - log.Printf("bumper.crt created at %v\n", filepath.Join(outCertDirectory, "bumper.crt")) - - // Private key - keyOut, err := os.OpenFile(filepath.Join(outCertDirectory, "bumper.key"), os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600) - if err != nil { - log.Fatalf("create bumper.key failed: %v", err) - } - pem.Encode(keyOut, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(privateKey)}) - keyOut.Close() - log.Printf("bumper.key created at %v\n", filepath.Join(outCertDirectory, "bumper.key")) -} - -func bigIntHash(n *big.Int) []byte { - h := sha1.New() - h.Write(n.Bytes()) - return h.Sum(nil) -} - -// readLines reads a whole file into memory -// and returns a slice of its lines. -func readLines(path string) ([]string, error) { - file, err := os.Open(path) - if err != nil { - return nil, err - } - defer file.Close() - - var lines []string - scanner := bufio.NewScanner(file) - for scanner.Scan() { - lines = append(lines, scanner.Text()) - } - return lines, scanner.Err() -} - -// pathexistsType checks if a path exists and is a file or directory -func pathExistsType(filename string) (exists bool, isfile bool) { - info, err := os.Stat(filename) - if os.IsNotExist(err) { - return false, false - } - return true, !info.IsDir() -} diff --git a/create_certs/src/create_certs_test.go b/create_certs/src/create_certs_test.go deleted file mode 100644 index e645dc6..0000000 --- a/create_certs/src/create_certs_test.go +++ /dev/null @@ -1,41 +0,0 @@ -package main - -import ( - "flag" - "os" - "testing" -) - -func TestArgs(t *testing.T) { - - orArgs := os.Args - - flag.CommandLine = flag.NewFlagSet(orArgs[0], flag.ContinueOnError) - os.Args = []string{"cmd", "-inSAN", "123"} - setFlags() - - if InBumperSan != "123" { - t.Error("InBumperSan not set by arg") - } - - flag.CommandLine = flag.NewFlagSet(orArgs[0], flag.ContinueOnError) - os.Args = []string{"cmd", "-out", "456"} - setFlags() - - if OutCertDirectory != "456" { - t.Error("Out path not set by arg") - } - - flag.CommandLine = flag.NewFlagSet(orArgs[0], flag.ContinueOnError) - os.Args = []string{"cmd", "-inSAN", "san1", "-out", "out2"} - setFlags() - - if InBumperSan != "san1" { - t.Error("InBumperSan not set by arg") - } - if OutCertDirectory != "out2" { - t.Error("Out path not set by arg") - } - - os.Args = orArgs -} diff --git a/docs/Create_Certs.md b/docs/Create_Certs.md index 626c994..e7436f9 100644 --- a/docs/Create_Certs.md +++ b/docs/Create_Certs.md @@ -8,7 +8,6 @@ Users can generate certificates in the following ways: | Method | Description | | ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------- | -| [Create_Certs](#creating-certs-using-createcerts) | ***(Preferred)*** This is a utility that has been created to assist in generating the certificates required easily. | | [OpenSSL](#manually-create-certs-with-openssl) | Users can manually create the same certificates as Create_Certs by utilizing OpenSSL. | | [Custom CA/Self](#using-a-custom-caself) | If a user has their own CA the certificates can be generated there and used within Bumper. | @@ -17,39 +16,9 @@ Users can generate certificates in the following ways: * A CA Cert must be provided that can be imported into devices (phones, browsers, etc). * Server certificate should include [SANs (Subject Alternate Names)](#subject-alternative-name) for all of the *.ecovacs, etc domains. -## Creating certs using Create_Certs - -Create_Certs was created to ease creation of certificates specifically for Bumper. Binaries are provided for Windows/Linux/OSX/RPi (ARMv5) in the Create_Certs directory. - -| Binary | Platform | -| ------------------------ | -------------------- | -| create_certs_linux | Linux | -| create_certs_osx | macOS/x | -| create_certs_windows.exe | Windows | -| create_certs_rpi | RaspberryPi (ARM v5) | - -Create_Certs is written in Go which allows cross-platform compiling. If the binaries don't work on your platform, install Go. - -With Go installed you can: - -* Execute the go code - `go run create_certs/src/create_certs.go` -* Build a new binary for your platform - `go build create_certs/src/create_certs.go` - -### Usage - -Create_Certs will automatically create the required certificates in the directory it is executed from. For best results change to the {bumper_home}/certs directory prior to executing, otherwise you'll need to move the certs after. - -1. `cd certs` -2. Execute create_certs (using the binary fitting your platform) - `../create_certs/create_certs_{platform}` -3. The certificates are generated and should be available in the current directory (certs) - -## Subject Alternative Name - -The server certificate requires a number of SAN (Subject Alternative Names) be added. Create_Certs handles this automatically by loading any SANs listed in the `create_certs/Bumper_SAN.txt` file. If creating certificates manually via OpenSSl/Custom CA these will need to be added. - ## Manually create certs with OpenSSL -I get it, you don't trust create_certs and want to do it manually. The easiest way to create the required certs is at https://certificatetools.com/. In fact the below OpenSSL commands come straight from that site, post creation via the GUI. +The easiest way to create the required certs is at https://certificatetools.com/. In fact the below OpenSSL commands come straight from that site, post creation via the GUI. ### Create a Root CA @@ -179,4 +148,4 @@ DNS.9 = *.area.ww.ecouser.net ## Using a Custom CA/Self -This should work siimilar to the OpenSSL method. Ensure the server certificate has the proper [SANs](#subject-alternative-name) in place. \ No newline at end of file +This should work siimilar to the OpenSSL method. Ensure the server certificate has the proper SANs (see above) in place. \ No newline at end of file diff --git a/tests/test_z_problem.py b/tests/test_z_problem.py index 3bd2ca5..9600e50 100644 --- a/tests/test_z_problem.py +++ b/tests/test_z_problem.py @@ -1,16 +1,6 @@ -import mock from mock import patch -import pytest -from tinydb.storages import MemoryStorage -from tinydb import TinyDB, Query + import bumper -import os -import datetime, time -import platform -import json -import asyncio -from testfixtures import LogCapture -import sys def mock_subrun(*args): @@ -40,63 +30,3 @@ def test_argparse(mock_start): assert bumper.bumper_announce_ip == "127.0.0.1" assert bumper.bumper_listen == "127.0.0.1" assert mock_start.called == True - - -@patch("subprocess.run") -@patch("platform.system") -@patch("platform.machine") -@patch("os.execv") -def test_createcert(mock_run, mock_platform, mock_machine, mock_exec): - mock_run.side_effect = mock_subrun - platform.system.return_value = "darwin" - bumper.create_certs() - assert mock_run.called == True - assert ( - os.path.join("..", "create_certs", "create_certs_osx") - in mock_exec.call_args.args[0] - ) - - platform.system.return_value = "windows" - bumper.create_certs() - assert mock_run.called == True - assert ( - os.path.join("..", "create_certs", "create_certs_windows.exe") - in mock_exec.call_args.args[0] - ) - - platform.system.return_value = "linux" - bumper.create_certs() - assert mock_run.called == True - assert ( - os.path.join("..", "create_certs", "create_certs_linux") - in mock_exec.call_args.args[0] - ) - - platform.system.return_value = "linux" - platform.machine.return_value = "arm" - bumper.create_certs() - assert mock_run.called == True - assert ( - os.path.join("..", "create_certs", "create_certs_rpi") - in mock_exec.call_args.args[0] - ) - - with LogCapture() as l: - platform.system.return_value = "nixbad" - bumper.create_certs() - - l.check_present( - ( - "root", - "CRITICAL", - "Can't determine platform. Create certs manually and try again.", - ) - ) - - -@patch("bumper.first_run") -def test_main(mock_firstrun): - bumper.ca_cert = "sf" - bumper.main() - assert mock_firstrun.called == True - bumper.ca_cert = "tests/test_certs/ca.crt"