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.