From ec187dbb3910b741f3da12aa738a3f528a1ab2f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Mon, 25 Nov 2024 04:56:19 +0100 Subject: [PATCH 1/3] generate cert inside gateway enclave --- .../manual-deploy-obscuro-gateway.yml | 13 ++++++--- go.mod | 8 ++--- go.sum | 8 +++++ lib/gethfork/node/config.go | 4 +++ lib/gethfork/node/node.go | 1 + lib/gethfork/node/rpc_server.go | 3 ++ lib/gethfork/node/rpcstack.go | 14 ++++++++- tools/walletextension/common/config.go | 2 ++ tools/walletextension/main/cli.go | 12 ++++++++ .../walletextension_container.go | 29 +++++++++++++++++++ 10 files changed, 85 insertions(+), 9 deletions(-) diff --git a/.github/workflows/manual-deploy-obscuro-gateway.yml b/.github/workflows/manual-deploy-obscuro-gateway.yml index 324364beab..158ab4c125 100644 --- a/.github/workflows/manual-deploy-obscuro-gateway.yml +++ b/.github/workflows/manual-deploy-obscuro-gateway.yml @@ -91,6 +91,7 @@ jobs: "GATEWAY_RATE_LIMIT_WINDOW" "GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER" "GATEWAY_KEY_EXCHANGE_URL" + "GATEWAY_TLS_DOMAIN" ) for VAR_NAME in "${VAR_NAMES[@]}"; do @@ -121,6 +122,7 @@ jobs: echo "GATEWAY_RATE_LIMIT_WINDOW: $GATEWAY_RATE_LIMIT_WINDOW" echo "GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER: $GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER" echo "GATEWAY_KEY_EXCHANGE_URL: $GATEWAY_KEY_EXCHANGE_URL" + echo "GATEWAY_TLS_DOMAIN: $GATEWAY_TLS_DOMAIN" - name: "Print GitHub variables" # This is a useful record of what the environment variables were at the time the job ran, for debugging and reference @@ -200,7 +202,7 @@ jobs: uses: azure/CLI@v1 with: inlineScript: | - az vm open-port -g Testnet -n "${{ env.VM_NAME }}" --port 80,81 + az vm open-port -g Testnet -n "${{ env.VM_NAME }}" --port 80,81,443,444 # To overcome issues with critical VM resources being unavailable, we need to wait for the VM to be ready - name: "Allow time for VM initialization" @@ -357,20 +359,22 @@ jobs: docker volume create "${{ env.VM_NAME }}-data" # Start Ten Gateway Container - docker run -d -p 80:80 -p 81:81 --name "${{ env.VM_NAME }}" \ + docker run -d -p 80:80 -p 81:81 -p 443:443 -p 444:444 --name "${{ env.VM_NAME }}" \ --device /dev/sgx_enclave --device /dev/sgx_provision \ -v "${{ env.VM_NAME }}-data:/data" \ -e OBSCURO_GATEWAY_VERSION="${{ github.run_number }}-${{ github.sha }}" \ -e OE_SIMULATION=0 \ "${{ env.DOCKER_BUILD_TAG_GATEWAY }}" \ ego run /home/ten/go-ten/tools/walletextension/main/main \ - -host=0.0.0.0 -port=80 -portWS=81 -nodeHost="${{ env.L2_RPC_URL_VALIDATOR }}" -verbose=true \ + -host=0.0.0.0 -port=443 -portWS=444 -nodeHost="${{ env.L2_RPC_URL_VALIDATOR }}" -verbose=true \ -logPath=sys_out -dbType=cosmosDB -dbConnectionURL="${{ secrets.COSMOS_DB_CONNECTION_STRING }}" \ -rateLimitUserComputeTime="${{ env.GATEWAY_RATE_LIMIT_USER_COMPUTE_TIME }}" \ -rateLimitWindow="${{ env.GATEWAY_RATE_LIMIT_WINDOW }}" \ -maxConcurrentRequestsPerUser="${{ env.GATEWAY_MAX_CONCURRENT_REQUESTS_PER_USER }}" \ -keyExchangeURL="${{ env.GATEWAY_KEY_EXCHANGE_URL }}" \ -insideEnclave=true \ + -enableTLS=true \ + -tlsDomain="${{ env.GATEWAY_TLS_DOMAIN }}" # After starting the container, verify the volume mount @@ -387,4 +391,5 @@ jobs: ps aux; " ' - \ No newline at end of file + + diff --git a/go.mod b/go.mod index c01113d511..956d97fcc2 100644 --- a/go.mod +++ b/go.mod @@ -49,9 +49,9 @@ require ( github.com/urfave/cli/v2 v2.27.5 github.com/valyala/fasthttp v1.57.0 gitlab.com/NebulousLabs/fastrand v0.0.0-20181126182046-603482d69e40 - golang.org/x/crypto v0.28.0 + golang.org/x/crypto v0.29.0 golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 - golang.org/x/sync v0.8.0 + golang.org/x/sync v0.9.0 google.golang.org/grpc v1.67.1 google.golang.org/protobuf v1.35.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1 @@ -173,8 +173,8 @@ require ( go.uber.org/multierr v1.9.0 // indirect golang.org/x/arch v0.11.0 // indirect golang.org/x/net v0.30.0 // indirect - golang.org/x/sys v0.26.0 // indirect - golang.org/x/text v0.19.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect gopkg.in/ini.v1 v1.67.0 // indirect gotest.tools/v3 v3.5.1 // indirect diff --git a/go.sum b/go.sum index 026692c79c..6ff33c87b8 100644 --- a/go.sum +++ b/go.sum @@ -415,6 +415,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81 h1:6R2FC06FonbXQ8pK11/PDFY6N6LWlf9KlzibaCapmqc= golang.org/x/exp v0.0.0-20240318143956-a85f2c67cd81/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= @@ -437,6 +439,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -460,12 +464,16 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/lib/gethfork/node/config.go b/lib/gethfork/node/config.go index c275292890..ef8a1fa9b8 100644 --- a/lib/gethfork/node/config.go +++ b/lib/gethfork/node/config.go @@ -17,6 +17,7 @@ package node import ( + "crypto/tls" "fmt" "net" "os" @@ -171,6 +172,9 @@ type Config struct { // TEN ExposedURLParamNames []string + + // TLS + TLSConfig *tls.Config } // IPCEndpoint resolves an IPC endpoint based on a configured value, taking into diff --git a/lib/gethfork/node/node.go b/lib/gethfork/node/node.go index 66233713ba..7d06261fc5 100644 --- a/lib/gethfork/node/node.go +++ b/lib/gethfork/node/node.go @@ -372,6 +372,7 @@ func (n *Node) startRPC() error { }); err != nil { return err } + server.tlsConfig = n.config.TLSConfig servers = append(servers, server) return nil } diff --git a/lib/gethfork/node/rpc_server.go b/lib/gethfork/node/rpc_server.go index 74f1dbe02f..259787712c 100644 --- a/lib/gethfork/node/rpc_server.go +++ b/lib/gethfork/node/rpc_server.go @@ -1,6 +1,7 @@ package node import ( + "crypto/tls" "net/http" gethlog "github.com/ethereum/go-ethereum/log" @@ -20,6 +21,7 @@ type RPCConfig struct { WsPort int WsPath string HTTPPath string + TLSConfig *tls.Config // ExposedURLParamNames - url prams that are available in the services ExposedURLParamNames []string @@ -49,6 +51,7 @@ func NewServer(config *RPCConfig, logger gethlog.Logger) Server { rpcConfig := Config{ Logger: logger, ExposedURLParamNames: config.ExposedURLParamNames, + TLSConfig: config.TLSConfig, } if config.EnableHTTP { rpcConfig.HTTPHost = config.Host diff --git a/lib/gethfork/node/rpcstack.go b/lib/gethfork/node/rpcstack.go index 76d58863ba..254611cccb 100644 --- a/lib/gethfork/node/rpcstack.go +++ b/lib/gethfork/node/rpcstack.go @@ -19,6 +19,7 @@ package node import ( "compress/gzip" "context" + "crypto/tls" "errors" "fmt" "io" @@ -92,6 +93,8 @@ type httpServer struct { port int handlerNames map[string]string + + tlsConfig *tls.Config } const ( @@ -152,7 +155,16 @@ func (h *httpServer) start() error { } // Start the server. - listener, err := net.Listen("tcp", h.endpoint) + var listener net.Listener + var err error + + if h.tlsConfig != nil { + // If TLS is enabled, use tls.Listen to create a TLS listener + listener, err = tls.Listen("tcp", h.endpoint, h.tlsConfig) + } else { + listener, err = net.Listen("tcp", h.endpoint) + } + if err != nil { // If the server fails to start, we need to clear out the RPC and WS // configuration so they can be configured another time. diff --git a/tools/walletextension/common/config.go b/tools/walletextension/common/config.go index c01d471c72..6f2eb11ed1 100644 --- a/tools/walletextension/common/config.go +++ b/tools/walletextension/common/config.go @@ -21,4 +21,6 @@ type Config struct { RateLimitMaxConcurrentRequests int InsideEnclave bool // Indicates if the program is running inside an enclave KeyExchangeURL string + EnableTLS bool + TLSDomain string } diff --git a/tools/walletextension/main/cli.go b/tools/walletextension/main/cli.go index 60aa3897df..6a02e3c3e3 100644 --- a/tools/walletextension/main/cli.go +++ b/tools/walletextension/main/cli.go @@ -80,6 +80,14 @@ const ( keyExchangeURLFlagName = "keyExchangeURL" keyExchangeURLFlagDefault = "" keyExchangeURLFlagUsage = "URL to exchange the key with another enclave. Default: empty" + + enableTLSFlagName = "enableTLS" + enableTLSFlagDefault = false + enableTLSFlagUsage = "Flag to enable TLS/HTTPS" + + tlsDomainFlagName = "tlsDomain" + tlsDomainFlagDefault = "" + tlsDomainFlagUsage = "Domain name for TLS certificate" ) func parseCLIArgs() wecommon.Config { @@ -101,6 +109,8 @@ func parseCLIArgs() wecommon.Config { rateLimitMaxConcurrentRequests := flag.Int(rateLimitMaxConcurrentRequestsName, rateLimitMaxConcurrentRequestsDefault, rateLimitMaxConcurrentRequestsUsage) insideEnclaveFlag := flag.Bool(insideEnclaveFlagName, insideEnclaveFlagDefault, insideEnclaveFlagUsage) keyExchangeURL := flag.String(keyExchangeURLFlagName, keyExchangeURLFlagDefault, keyExchangeURLFlagUsage) + enableTLSFlag := flag.Bool(enableTLSFlagName, enableTLSFlagDefault, enableTLSFlagUsage) + tlsDomainFlag := flag.String(tlsDomainFlagName, tlsDomainFlagDefault, tlsDomainFlagUsage) flag.Parse() return wecommon.Config{ @@ -121,5 +131,7 @@ func parseCLIArgs() wecommon.Config { RateLimitMaxConcurrentRequests: *rateLimitMaxConcurrentRequests, InsideEnclave: *insideEnclaveFlag, KeyExchangeURL: *keyExchangeURL, + EnableTLS: *enableTLSFlag, + TLSDomain: *tlsDomainFlag, } } diff --git a/tools/walletextension/walletextension_container.go b/tools/walletextension/walletextension_container.go index e9c58efec7..ec79844a65 100644 --- a/tools/walletextension/walletextension_container.go +++ b/tools/walletextension/walletextension_container.go @@ -1,6 +1,8 @@ package walletextension import ( + "crypto/tls" + "net/http" "os" "time" @@ -21,6 +23,7 @@ import ( wecommon "github.com/ten-protocol/go-ten/tools/walletextension/common" "github.com/ten-protocol/go-ten/tools/walletextension/keymanager" "github.com/ten-protocol/go-ten/tools/walletextension/storage" + "golang.org/x/crypto/acme/autocert" ) type Container struct { @@ -67,6 +70,32 @@ func NewContainerFromConfig(config wecommon.Config, logger gethlog.Logger) *Cont HTTPPath: wecommon.APIVersion1 + "/", Host: config.WalletExtensionHost, } + + // check if TLS is enabled + if config.EnableTLS { + // Create autocert manager for automatic certificate management + certManager := &autocert.Manager{ + Prompt: autocert.AcceptTOS, + HostPolicy: autocert.HostWhitelist(config.TLSDomain), + // Cache: autocert.DirCache("certs"), // TODO: We can add cache for certs (+ don't forget to include the directory in enclave.json) + } + + // Create HTTP-01 challenge handler + httpServer := &http.Server{ + Addr: ":http", // Port 80 + Handler: certManager.HTTPHandler(nil), + } + go httpServer.ListenAndServe() // Start HTTP server for ACME challenges + + tlsConfig := &tls.Config{ + GetCertificate: certManager.GetCertificate, + MinVersion: tls.VersionTLS12, + } + + // Update RPC server config to use TLS + cfg.TLSConfig = tlsConfig + } + rpcServer := node.NewServer(cfg, logger) rpcServer.RegisterRoutes(httpapi.NewHTTPRoutes(walletExt)) From 62dfc0ed11606953bfa71a69438918c016e9f191 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Mon, 2 Dec 2024 13:42:20 +0100 Subject: [PATCH 2/3] store certificate in cache --- tools/walletextension/walletextension_container.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/walletextension/walletextension_container.go b/tools/walletextension/walletextension_container.go index ec79844a65..5ecc512461 100644 --- a/tools/walletextension/walletextension_container.go +++ b/tools/walletextension/walletextension_container.go @@ -77,7 +77,7 @@ func NewContainerFromConfig(config wecommon.Config, logger gethlog.Logger) *Cont certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(config.TLSDomain), - // Cache: autocert.DirCache("certs"), // TODO: We can add cache for certs (+ don't forget to include the directory in enclave.json) + Cache: autocert.DirCache("/data/certs"), } // Create HTTP-01 challenge handler From 620500495eaa83a8d6a6e1be473f1315a00cc43d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=BDiga=20Kokelj?= Date: Mon, 2 Dec 2024 16:01:20 +0100 Subject: [PATCH 3/3] comment added --- tools/walletextension/walletextension_container.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/walletextension/walletextension_container.go b/tools/walletextension/walletextension_container.go index 5ecc512461..b9cbbc2a58 100644 --- a/tools/walletextension/walletextension_container.go +++ b/tools/walletextension/walletextension_container.go @@ -74,6 +74,14 @@ func NewContainerFromConfig(config wecommon.Config, logger gethlog.Logger) *Cont // check if TLS is enabled if config.EnableTLS { // Create autocert manager for automatic certificate management + // Generating a certificate consists of the following steps: + // generating a new private key + // domain ownership verification (HTTP-01 challenge since certManager.HTTPHandler(nil) is set) + // Certificate Signing Request (CRS) is generated + // CRS is sent to CA (Let's Encrypt) via ACME (automated certificate management environment) client + // CA verifies CRS and issues a certificate + // we store store certificate and private key (in memory and also in on a mounted volume attached to docker container - /data/certs/) + certManager := &autocert.Manager{ Prompt: autocert.AcceptTOS, HostPolicy: autocert.HostWhitelist(config.TLSDomain),