From 4ef6e64a456d721b6be21b26457ebcbd3f29c005 Mon Sep 17 00:00:00 2001 From: Thorsten Sommer Date: Wed, 16 Mar 2016 10:11:05 +0100 Subject: [PATCH] Added TLS and HTTP/2 support In order to use TLS and HTTP/2, add the necessary certificate and the key to the staticFiles.zip file and configure Ocean to enable it. --- ConfigurationDB/CheckConfiguration.go | 6 +++ StaticFiles/FindAndReadFile.go | 36 +++++++++++++++++ System/Version/Variables.go | 2 +- WebServer/Init.go | 56 +++++++++++++++++++++++++++ WebServer/Start.go | 19 ++++++++- 5 files changed, 116 insertions(+), 3 deletions(-) diff --git a/ConfigurationDB/CheckConfiguration.go b/ConfigurationDB/CheckConfiguration.go index 5891501..203caaf 100644 --- a/ConfigurationDB/CheckConfiguration.go +++ b/ConfigurationDB/CheckConfiguration.go @@ -14,6 +14,9 @@ func checkConfiguration() { CheckSingleConfigurationPresentsAndAddIfMissing(`DefaultLanguageCode`, `en-GB`) CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerBinding`, `127.0.0.1:60000`) CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerEnabled`, `True`) + CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerUseTLS`, `False`) + CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerTLSCertificateName`, `certificateAdmin.pem`) + CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerTLSPrivateKey`, `privateKeyAdmin.pem`) CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerReadTimeoutSeconds`, `10`) CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerWriteTimeoutSeconds`, `10`) CheckSingleConfigurationPresentsAndAddIfMissing(`AdminWebServerMaxHeaderLenBytes`, `10485760`) @@ -21,6 +24,9 @@ func checkConfiguration() { CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerReadTimeoutSeconds`, `10`) CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerWriteTimeoutSeconds`, `10`) CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerMaxHeaderLenBytes`, `1048576`) + CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerUseTLS`, `False`) + CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerTLSCertificateName`, `certificatePublic.pem`) + CheckSingleConfigurationPresentsAndAddIfMissing(`PublicWebServerTLSPrivateKey`, `privateKeyPublic.pem`) CheckSingleConfigurationPresentsAndAddIfMissing(`InternalCommPassword`, `please replace this with e.g. a random GUID, etc.`) CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBHost`, `localhost:27017`) CheckSingleConfigurationPresentsAndAddIfMissing(`CustomerDBDatabase`, `Ocean`) diff --git a/StaticFiles/FindAndReadFile.go b/StaticFiles/FindAndReadFile.go index d9b3421..c659906 100644 --- a/StaticFiles/FindAndReadFile.go +++ b/StaticFiles/FindAndReadFile.go @@ -3,10 +3,12 @@ package StaticFiles import ( "archive/zip" "bytes" + "github.com/SommerEngineering/Ocean/ConfigurationDB" "github.com/SommerEngineering/Ocean/Log" LM "github.com/SommerEngineering/Ocean/Log/Meta" "github.com/SommerEngineering/Ocean/Shutdown" "io/ioutil" + "strings" ) // Try to read a static file. @@ -17,6 +19,40 @@ func FindAndReadFile(filename string) (result []byte) { return } + // + // Ensure that the TLS keys are secure and save: + // + if strings.ToLower(filename) == strings.ToLower(ConfigurationDB.Read(`AdminWebServerTLSCertificateName`)) { + Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityNone, LM.ImpactNone, LM.MessageNameREQUEST, `Someone tried to read the TLS certificate of the admin server. The attempt was inhibited.`) + return + } + + if strings.ToLower(filename) == strings.ToLower(ConfigurationDB.Read(`AdminWebServerTLSPrivateKey`)) { + Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityNone, LM.ImpactNone, LM.MessageNameREQUEST, `Someone tried to read the TLS certificate's private key of the admin server. The attempt was inhibited.`) + return + } + + if strings.ToLower(filename) == strings.ToLower(ConfigurationDB.Read(`PublicWebServerTLSCertificateName`)) { + Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityNone, LM.ImpactNone, LM.MessageNameREQUEST, `Someone tried to read the TLS certificate of the public server. The attempt was inhibited.`) + return + } + + if strings.ToLower(filename) == strings.ToLower(ConfigurationDB.Read(`PublicWebServerTLSPrivateKey`)) { + Log.LogFull(senderName, LM.CategorySYSTEM, LM.LevelSECURITY, LM.SeverityNone, LM.ImpactNone, LM.MessageNameREQUEST, `Someone tried to read the TLS certificate's private key of the public server. The attempt was inhibited.`) + return + } + + result = FindAndReadFileINTERNAL(filename) + return +} + +func FindAndReadFileINTERNAL(filename string) (result []byte) { + + // Case: The system goes down. + if Shutdown.IsDown() { + return + } + // Prepare the path: path := filename diff --git a/System/Version/Variables.go b/System/Version/Variables.go index 70dccd3..4ae4041 100644 --- a/System/Version/Variables.go +++ b/System/Version/Variables.go @@ -1,5 +1,5 @@ package Version var ( - oceansVersion string = `2.0.8` // Ocean's current version + oceansVersion string = `2.1.0` // Ocean's current version ) diff --git a/WebServer/Init.go b/WebServer/Init.go index 78da85c..2905ff0 100644 --- a/WebServer/Init.go +++ b/WebServer/Init.go @@ -6,8 +6,12 @@ import ( "github.com/SommerEngineering/Ocean/Handlers" "github.com/SommerEngineering/Ocean/Log" LM "github.com/SommerEngineering/Ocean/Log/Meta" + "github.com/SommerEngineering/Ocean/StaticFiles" "github.com/SommerEngineering/Ocean/Tools" + "io/ioutil" "net/http" + "os" + "path/filepath" "strconv" "strings" "time" @@ -64,6 +68,32 @@ func init() { serverPublic.MaxHeaderBytes = maxHeaderBytes } + // Is TLS configured? + if publicTLSEnabled := ConfigurationDB.Read(`PublicWebServerUseTLS`); strings.ToLower(publicTLSEnabled) == `true` { + + // TLS is enabled. Copy the certificate and private key to the source directory. + publicTLSCertificate := StaticFiles.FindAndReadFileINTERNAL(ConfigurationDB.Read(`PublicWebServerTLSCertificateName`)) + publicTLSPrivateKey := StaticFiles.FindAndReadFileINTERNAL(ConfigurationDB.Read(`PublicWebServerTLSPrivateKey`)) + + // Access to the working directory? + currentDir, dirError := os.Getwd() + if dirError != nil { + Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameCONFIGURATION, `Was not able to read the working directory. Thus, cannot store the TLS certificates!`, dirError.Error()) + } else { + // Build the filenames: + pathCertificate := filepath.Join(currentDir, ConfigurationDB.Read(`PublicWebServerTLSCertificateName`)) + pathPrivateKey := filepath.Join(currentDir, ConfigurationDB.Read(`PublicWebServerTLSPrivateKey`)) + + // Write the files: + if writeError := ioutil.WriteFile(pathCertificate, publicTLSCertificate, 0660); writeError != nil { + Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameCONFIGURATION, `Was not able to write the TLS certificate to the working directory.`, writeError.Error()) + } + if writeError := ioutil.WriteFile(pathPrivateKey, publicTLSPrivateKey, 0660); writeError != nil { + Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameCONFIGURATION, `Was not able to write the TLS private key to the working directory.`, writeError.Error()) + } + } + } + // Is the private web server (i.e. administration server) enabled? if strings.ToLower(ConfigurationDB.Read(`AdminWebServerEnabled`)) == `true` { @@ -105,6 +135,32 @@ func init() { Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameCONFIGURATION, fmt.Sprintf("The admin web server's max. header size was set to %d bytes.", maxHeaderBytes)) serverAdmin.MaxHeaderBytes = maxHeaderBytes } + + // Is TLS configured? + if adminTLSEnabled := ConfigurationDB.Read(`AdminWebServerUseTLS`); strings.ToLower(adminTLSEnabled) == `true` { + + // TLS is enabled. Copy the certificate and private key to the source directory. + adminTLSCertificate := StaticFiles.FindAndReadFileINTERNAL(ConfigurationDB.Read(`AdminWebServerTLSCertificateName`)) + adminTLSPrivateKey := StaticFiles.FindAndReadFileINTERNAL(ConfigurationDB.Read(`AdminWebServerTLSPrivateKey`)) + + // Access to the working directory? + currentDir, dirError := os.Getwd() + if dirError != nil { + Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameCONFIGURATION, `Was not able to read the working directory. Thus, cannot store the TLS certificates!`, dirError.Error()) + } else { + // Build the filenames: + pathCertificate := filepath.Join(currentDir, ConfigurationDB.Read(`AdminWebServerTLSCertificateName`)) + pathPrivateKey := filepath.Join(currentDir, ConfigurationDB.Read(`AdminWebServerTLSPrivateKey`)) + + // Write the files: + if writeError := ioutil.WriteFile(pathCertificate, adminTLSCertificate, 0660); writeError != nil { + Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameCONFIGURATION, `Was not able to write the TLS certificate to the working directory.`, writeError.Error()) + } + if writeError := ioutil.WriteFile(pathPrivateKey, adminTLSPrivateKey, 0660); writeError != nil { + Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelERROR, LM.MessageNameCONFIGURATION, `Was not able to write the TLS private key to the working directory.`, writeError.Error()) + } + } + } } else { // Private web server is disabled: Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `The admin web server is disabled.`) diff --git a/WebServer/Start.go b/WebServer/Start.go index 6a5810a..cfe80d5 100644 --- a/WebServer/Start.go +++ b/WebServer/Start.go @@ -2,11 +2,13 @@ package WebServer import ( "fmt" + "github.com/SommerEngineering/Ocean/ConfigurationDB" "github.com/SommerEngineering/Ocean/ICCC" "github.com/SommerEngineering/Ocean/ICCC/SystemMessages" "github.com/SommerEngineering/Ocean/Log" LM "github.com/SommerEngineering/Ocean/Log/Meta" "github.com/SommerEngineering/Ocean/System/Version" + "strings" ) func Start() { @@ -19,14 +21,27 @@ func Start() { if serverPublic != nil { data.PublicIPAddressPort = serverPublicAddressPort Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Public web server is now listening.`, `Configuration for hostname and port.`, serverPublicAddressPort) - go serverPublic.ListenAndServe() + + // Is TLS configured? + if publicTLSEnabled := ConfigurationDB.Read(`PublicWebServerUseTLS`); strings.ToLower(publicTLSEnabled) == `true` { + go serverPublic.ListenAndServeTLS(ConfigurationDB.Read(`PublicWebServerTLSCertificateName`), ConfigurationDB.Read(`PublicWebServerTLSPrivateKey`)) + } else { + go serverPublic.ListenAndServe() + } + } // Start the private web server: if serverAdmin != nil { data.AdminIPAddressPort = serverAdminAddressPort Log.LogShort(senderName, LM.CategorySYSTEM, LM.LevelINFO, LM.MessageNameSTARTUP, `Admin web server is now listening.`, `Configuration for hostname and port.`, serverAdminAddressPort) - go serverAdmin.ListenAndServe() + + // Is TLS configured? + if adminTLSEnabled := ConfigurationDB.Read(`AdminWebServerUseTLS`); strings.ToLower(adminTLSEnabled) == `true` { + go serverAdmin.ListenAndServeTLS(ConfigurationDB.Read(`AdminWebServerTLSCertificateName`), ConfigurationDB.Read(`AdminWebServerTLSPrivateKey`)) + } else { + go serverAdmin.ListenAndServe() + } } // Notify the whole cluster, that this server is now up and ready: