1

BACKGROUND I created a simple http webserver in go. Below is a snippet of the code where *Static, Addr and port are the frontend files, the server address, and the port respectively

func fileServerRedirect(fs http.FileSystem) http.Handler {
    server := http.FileServer(fs)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        _, err := fs.Open(path.Clean(r.URL.Path))
        if os.IsNotExist(err) {
            log.Println(r.URL.Path)
            r.URL.Path = "/"
        }
        server.ServeHTTP(w, r)
    })
}

func StartServer() error {

    http.Handle("/", fileServerRedirect(http.Dir(*Static)))

    server := &http.Server{Addr: ":" + *Port}

    // Start server in goroutine and wait for shutdown
    go func() {
        if err := server.ListenAndServe(); err != nil {
            log.Printf("Server error: %s\n", err)
        }
        stop <- struct{}{}
    }()
    <-stop
.
.
.
    return nil
}

This code runs as expected. It creates a server and handles http requests. I run this webserver on my raspberry-PI and connect to it via my PC. When I use the webbrowser to connect to the address, it shows me the webUI and I can interact with it. So far so good.

PROBLEM I wanted to add some TLS stuff to my webserver, so I created a self signed certificate and key and passed it to the webserver. It compiles fine and I don't see any runtime errors, but I cannot connect to the server anymore. I get an 'Unable to connect' error on the webbrowser. Is there something that I am missing? Below is the code I used to create the certificate and private key and attach it to the server.

func createSelfCertKey(certFilePath, keyFilePath string) (err error) {
    // set up our CA certificate
    ca := &x509.Certificate{
        SerialNumber: big.NewInt(2019),
        Subject: pkix.Name{
            Organization:  []string{"World Domination"},
            Country:       []string{"Moon"},
            Province:      []string{""},
            Locality:      []string{"Dark Side"},
            StreetAddress: []string{"Near the big crator"},
            PostalCode:    []string{"10010"},
        },
        NotBefore:             time.Now(),
        NotAfter:              time.Now().AddDate(10, 0, 0),
        IsCA:                  true,
        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
        KeyUsage:              x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
        BasicConstraintsValid: true,
    }

    caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
        log.Printf("web: cert: failed to generate CA key: %s\n", err)
        return err
    }

    // create the CA
    caBytes, err := x509.CreateCertificate(rand.Reader, ca, ca, &caPrivKey.PublicKey, caPrivKey)
    if err != nil {
        log.Printf("web: cert: failed to create CA: %s\n", err)
        return err
    }

    // pem encode
    caPEM := new(bytes.Buffer)
    pem.Encode(caPEM, &pem.Block{
        Type:  "CERTIFICATE",
        Bytes: caBytes,
    })

    caPrivKeyPEM := new(bytes.Buffer)
    pem.Encode(caPrivKeyPEM, &pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
    })

    // set up our server certificate
    selfcert := &x509.Certificate{
        SerialNumber: big.NewInt(2019),
        Subject: pkix.Name{
            Organization:  []string{"World Domination"},
            Country:       []string{"Moon"},
            Province:      []string{""},
            Locality:      []string{"Dark Side"},
            StreetAddress: []string{"Near the big crator"},
            PostalCode:    []string{"10010"},
        },
        IPAddresses:  []net.IP{net.IPv4(127, 0, 0, 1), net.IPv6loopback},
        NotBefore:    time.Now(),
        NotAfter:     time.Now().AddDate(10, 0, 0),
        SubjectKeyId: []byte{1, 2, 3, 4, 6},
        ExtKeyUsage:  []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
        KeyUsage:     x509.KeyUsageDigitalSignature,
    }

    certPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
    if err != nil {
        log.Printf("web: cert: failed to generate cert key: %s\n", err)
        return err
    }

    certBytes, err := x509.CreateCertificate(rand.Reader, selfcert, ca, &certPrivKey.PublicKey, caPrivKey)
    if err != nil {
        log.Printf("web: cert: failed to create cert: %s\n", err)
        return err
    }

    certPEM := new(bytes.Buffer)
    pem.Encode(certPEM, &pem.Block{
        Type:  "CERTIFICATE",
        Bytes: certBytes,
    })

    err = os.WriteFile(certFilePath, certPEM.Bytes(), 0)
    if err != nil {
        log.Printf("web: cert: failed to write cert to file\n")
        return err
    }

    certPrivKeyPEM := new(bytes.Buffer)
    pem.Encode(certPrivKeyPEM, &pem.Block{
        Type:  "RSA PRIVATE KEY",
        Bytes: x509.MarshalPKCS1PrivateKey(certPrivKey),
    })

    err = os.WriteFile(keyFilePath, certPrivKeyPEM.Bytes(), 0)
    if err != nil {
        log.Printf("web: cert: failed to write key to file\n")
    }

    return
}


func StartServer(certKeyFilePath string) error {
    http.Handle("/", fileServerRedirect(http.Dir(*Static)))

    certFilePath := Workdir + "/cert.pem"
    keyFilePath := Workdir + "/key.pem"

    err := createSelfCertKey(certFilePath, keyFilePath)
    if err != nil {
        return err
    }

    server := &http.Server{Addr: ":" + *Port}

    // Start server in goroutine and wait for shutdown
    go func() {
        if err := server.ListenAndServeTLS(certFilePath, keyFilePath); err != nil {
            log.Printf("Server error: %s\n", err)
        }
        stop <- struct{}{}
    }()
    <-stop
.
.
.
    return nil
}

The code basically creates the cert.pem and key.pem files in the locations specified in the parameters and then uses server.ListenAndServeTLS() to pass the file locations.

Any idea why it doesn't work? I should atleast see a notification on my browser that the website is not trusted or something because it is selfsigned, but I don't see that either. I think I am missing something in my code. Would appreciate any hint or solution. Thank you.

I used some online tutorials to create the TLS stuff for the webserver.

2
  • 2
    1. "it doesn't work?" is not an actionable problem description. 2. "NotBefore: Now" requires fancy timing. Commented Nov 21, 2023 at 12:01
  • @Volker thanks. The certificate date was the issue. It 'works' now :). Also worth pointing out for other newbies like myself who maybe facing similar issues, the server Port should also be changed from 80 to 443, otherwise the webbrowser may get a response 'Client sent an HTTP request to an HTTPS server.' or similar. Commented Nov 22, 2023 at 9:33

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.