From cb85ed355ac56f05368013405a7ce316d82fd297 Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 13:06:49 +0200 Subject: [PATCH 01/12] use skynet server api env variable to rewrite proxy response on /tus endpoint --- docker/nginx/conf.d/client.conf | 2 +- docker/nginx/nginx.conf | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docker/nginx/conf.d/client.conf b/docker/nginx/conf.d/client.conf index e77a54a8..69c6fef4 100644 --- a/docker/nginx/conf.d/client.conf +++ b/docker/nginx/conf.d/client.conf @@ -334,7 +334,7 @@ server { proxy_pass http://siad; # rewrite tus headers to use correct uri - proxy_redirect https://siad/ https://$domain.$tld/; + proxy_redirect https://siad $SKYNET_SERVER_API; # extract skylink from base64 encoded upload metadata and assign to a proper header header_filter_by_lua_block { diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index fe14b0a4..75f807f9 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -25,8 +25,10 @@ worker_processes 1; #pid logs/nginx.pid; -env SKYNET_PORTAL_API; # declare env variable to use it in config -env ACCOUNTS_ENABLED; # declare env variable to use it in config +# declare env variables to use it in config +env SKYNET_PORTAL_API; +env SKYNET_SERVER_API; +env ACCOUNTS_ENABLED; events { worker_connections 1024; From e463199022a82b594c52b992fc387dd90994478d Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 13:16:41 +0200 Subject: [PATCH 02/12] use perl_set to expose env variables --- docker/nginx/nginx.conf | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 75f807f9..c71ead18 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -66,6 +66,11 @@ http { #gzip on; + # expose env variables as regular variables (env variables are not easily accessible in nginx) + perl_set $SKYNET_PORTAL_API 'sub { return $ENV{"SKYNET_PORTAL_API"}; }'; + perl_set $SKYNET_SERVER_API 'sub { return $ENV{"SKYNET_SERVER_API"}; }'; + perl_set $ACCOUNTS_ENABLED 'sub { return $ENV{"ACCOUNTS_ENABLED"}; }'; + # include skynet-portal-api header on every request header_filter_by_lua 'ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")'; From db1a50a99929861d3facb0484330ad4d5b39adae Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 13:21:05 +0200 Subject: [PATCH 03/12] use set_by_lua instead --- docker/nginx/nginx.conf | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index c71ead18..22dcd4a7 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -67,9 +67,9 @@ http { #gzip on; # expose env variables as regular variables (env variables are not easily accessible in nginx) - perl_set $SKYNET_PORTAL_API 'sub { return $ENV{"SKYNET_PORTAL_API"}; }'; - perl_set $SKYNET_SERVER_API 'sub { return $ENV{"SKYNET_SERVER_API"}; }'; - perl_set $ACCOUNTS_ENABLED 'sub { return $ENV{"ACCOUNTS_ENABLED"}; }'; + set_by_lua $SKYNET_PORTAL_API 'return os.getenv("SKYNET_PORTAL_API")'; + set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; + set_by_lua $ACCOUNTS_ENABLED 'return os.getenv("ACCOUNTS_ENABLED")'; # include skynet-portal-api header on every request header_filter_by_lua 'ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")'; From 4c3aa724245d061ea5e8f2a47c55cecd4944ee87 Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 13:23:08 +0200 Subject: [PATCH 04/12] use in context of location --- docker/nginx/conf.d/client.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/nginx/conf.d/client.conf b/docker/nginx/conf.d/client.conf index 69c6fef4..18244968 100644 --- a/docker/nginx/conf.d/client.conf +++ b/docker/nginx/conf.d/client.conf @@ -333,7 +333,8 @@ server { # proxy /skynet/tus requests to siad endpoint with all arguments proxy_pass http://siad; - # rewrite tus headers to use correct uri + # rewrite tus headers to use correct uri from env variable + set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; proxy_redirect https://siad $SKYNET_SERVER_API; # extract skylink from base64 encoded upload metadata and assign to a proper header From 308e37f63fffcf2a219ecdfab981e9f669ff8b1a Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 13:24:51 +0200 Subject: [PATCH 05/12] clean up failing part --- docker/nginx/nginx.conf | 5 ----- 1 file changed, 5 deletions(-) diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 22dcd4a7..75f807f9 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -66,11 +66,6 @@ http { #gzip on; - # expose env variables as regular variables (env variables are not easily accessible in nginx) - set_by_lua $SKYNET_PORTAL_API 'return os.getenv("SKYNET_PORTAL_API")'; - set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; - set_by_lua $ACCOUNTS_ENABLED 'return os.getenv("ACCOUNTS_ENABLED")'; - # include skynet-portal-api header on every request header_filter_by_lua 'ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")'; From 5214d2d2986e43463091e39eeb1318d3fea52651 Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 13:31:09 +0200 Subject: [PATCH 06/12] use host overwrite instead of proxy_redirect --- docker/nginx/conf.d/client.conf | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/nginx/conf.d/client.conf b/docker/nginx/conf.d/client.conf index 18244968..30887570 100644 --- a/docker/nginx/conf.d/client.conf +++ b/docker/nginx/conf.d/client.conf @@ -335,7 +335,8 @@ server { # rewrite tus headers to use correct uri from env variable set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; - proxy_redirect https://siad $SKYNET_SERVER_API; + # proxy_redirect https://siad $SKYNET_SERVER_API; + proxy_set_header Host $SKYNET_SERVER_API; # extract skylink from base64 encoded upload metadata and assign to a proper header header_filter_by_lua_block { From fc2a3b42909fd27b827cf062fb17aec44429c67a Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 14:38:18 +0200 Subject: [PATCH 07/12] expose Skynet-Server-Api header --- docker/nginx/conf.d/client.conf | 9 ++++----- docker/nginx/conf.d/include/cors | 2 +- docker/nginx/nginx.conf | 7 +++++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/docker/nginx/conf.d/client.conf b/docker/nginx/conf.d/client.conf index 30887570..48d5037e 100644 --- a/docker/nginx/conf.d/client.conf +++ b/docker/nginx/conf.d/client.conf @@ -330,14 +330,13 @@ server { proxy_request_buffering off; # stream uploaded files through the proxy as it comes in proxy_set_header Expect $http_expect; + # rewrite proxy request to use correct host uri from env variable (required to return correct location header) + set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; + proxy_rewrite https://siad $SKYNET_SERVER_API; + # proxy /skynet/tus requests to siad endpoint with all arguments proxy_pass http://siad; - # rewrite tus headers to use correct uri from env variable - set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; - # proxy_redirect https://siad $SKYNET_SERVER_API; - proxy_set_header Host $SKYNET_SERVER_API; - # extract skylink from base64 encoded upload metadata and assign to a proper header header_filter_by_lua_block { if ngx.header["Upload-Metadata"] then diff --git a/docker/nginx/conf.d/include/cors b/docker/nginx/conf.d/include/cors index 1f7e4411..5245c33f 100644 --- a/docker/nginx/conf.d/include/cors +++ b/docker/nginx/conf.d/include/cors @@ -13,4 +13,4 @@ more_set_headers 'Access-Control-Allow-Origin: $http_origin'; more_set_headers 'Access-Control-Allow-Credentials: true'; more_set_headers 'Access-Control-Allow-Methods: GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE'; more_set_headers 'Access-Control-Allow-Headers: DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range,X-HTTP-Method-Override,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,location'; -more_set_headers 'Access-Control-Expose-Headers: Content-Length,Content-Range,Skynet-File-Metadata,Skynet-Skylink,Skynet-Portal-Api,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,location'; +more_set_headers 'Access-Control-Expose-Headers: Content-Length,Content-Range,Skynet-File-Metadata,Skynet-Skylink,Skynet-Portal-Api,Skynet-Server-Api,upload-offset,upload-metadata,upload-length,tus-version,tus-resumable,tus-extension,tus-max-size,location'; diff --git a/docker/nginx/nginx.conf b/docker/nginx/nginx.conf index 75f807f9..41c15010 100644 --- a/docker/nginx/nginx.conf +++ b/docker/nginx/nginx.conf @@ -66,8 +66,11 @@ http { #gzip on; - # include skynet-portal-api header on every request - header_filter_by_lua 'ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API")'; + # include skynet-portal-api and skynet-server-api header on every request + header_filter_by_lua_block { + ngx.header["Skynet-Portal-Api"] = os.getenv("SKYNET_PORTAL_API") + ngx.header["Skynet-Server-Api"] = os.getenv("SKYNET_SERVER_API") + } include /etc/nginx/conf.d/*.conf; include /etc/nginx/conf.extra.d/*.conf; From f6299e4e9dcf0c951b0f2fa315860d6c0312d27c Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 15:58:52 +0200 Subject: [PATCH 08/12] use SKYNET_SERVER_API in discord bot --- setup-scripts/bot_utils.py | 2 +- setup-scripts/setup-docker-services.sh | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/setup-scripts/bot_utils.py b/setup-scripts/bot_utils.py index 9e918955..841d9ccd 100644 --- a/setup-scripts/bot_utils.py +++ b/setup-scripts/bot_utils.py @@ -53,7 +53,7 @@ def setup(): bot_token = os.environ["DISCORD_BOT_TOKEN"] global portal_name - portal_name = os.getenv("PORTAL_NAME") + portal_name = os.getenv("SKYNET_SERVER_API") # Get a port or use default global port diff --git a/setup-scripts/setup-docker-services.sh b/setup-scripts/setup-docker-services.sh index c2270b3c..84b75ddd 100755 --- a/setup-scripts/setup-docker-services.sh +++ b/setup-scripts/setup-docker-services.sh @@ -22,15 +22,16 @@ docker-compose --version # sanity check # Create dummy .env file for docker-compose usage with variables # * SSL_CERTIFICATE_STRING - certificate string that will be used to generate ssl certificates, read more in docker/caddy/Caddyfile -# * SKYNET_PORTAL_API - absolute url to the portal api ie. https://example.com -# * SKYNET_DASHBOARD_URL - (optional) absolute url to the portal dashboard ie. https://account.example.com +# * SKYNET_PORTAL_API - absolute url to the portal api ie. https://siasky.net (general portal address) +# * SKYNET_SERVER_API - absolute url to the server api ie. https://eu-ger-1.siasky.net (direct server address, if this is single server portal use the same address as SKYNET_PORTAL_API) +# * SKYNET_DASHBOARD_URL - (optional) absolute url to the portal dashboard ie. https://account.siasky.net # * EMAIL_ADDRESS - this is the administrator contact email you need to supply for communication regarding SSL certification # * HSD_API_KEY - this is auto generated secure key for your handshake service integration # * CLOUDFLARE_AUTH_TOKEN - (optional) if using cloudflare as dns loadbalancer (need to change it in Caddyfile too) # * AWS_ACCESS_KEY_ID - (optional) if using route53 as a dns loadbalancer # * AWS_SECRET_ACCESS_KEY - (optional) if using route53 as a dns loadbalancer # * API_PORT - (optional) the port on which siad is listening, defaults to 9980 -# * PORTAL_NAME - the name of the portal, required by the discord bot +# * PORTAL_NAME - the name of the portal # * DISCORD_BOT_TOKEN - (optional) only required if you're using the discord notifications integration # * SKYNET_DB_USER - (optional) if using `accounts` this is the MongoDB username # * SKYNET_DB_PASS - (optional) if using `accounts` this is the MongoDB password @@ -43,7 +44,7 @@ docker-compose --version # sanity check # * CR_CLUSTER_NODES - (optional) if using `accounts` the list of servers (with ports) which make up your CockroachDB cluster, e.g. `helsinki.siasky.net:26257,germany.siasky.net:26257,us-east.siasky.net:26257` if ! [ -f /home/user/skynet-webportal/.env ]; then HSD_API_KEY=$(openssl rand -base64 32) # generate safe random key for handshake - printf "SSL_CERTIFICATE_STRING=example.com, *.example.com, *.hns.example.com\nSKYNET_PORTAL_API=https://example.com\nSKYNET_DASHBOARD_URL=https://account.example.com\nEMAIL_ADDRESS=email@example.com\nSIA_WALLET_PASSWORD=\nHSD_API_KEY=${HSD_API_KEY}\nCLOUDFLARE_AUTH_TOKEN=\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nPORTAL_NAME=\nDISCORD_BOT_TOKEN=\n" > /home/user/skynet-webportal/.env + printf "SSL_CERTIFICATE_STRING=siasky.net, *.siasky.net, *.hns.siasky.net\nSKYNET_PORTAL_API=https://siasky.net\nSKYNET_SERVER_API=https://siasky.net\nSKYNET_DASHBOARD_URL=https://account.example.com\nEMAIL_ADDRESS=email@example.com\nSIA_WALLET_PASSWORD=\nHSD_API_KEY=${HSD_API_KEY}\nCLOUDFLARE_AUTH_TOKEN=\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nPORTAL_NAME=\nDISCORD_BOT_TOKEN=\n" > /home/user/skynet-webportal/.env fi # Start docker container with nginx and client From 6594dbf5a6e50e8bf851b64802b719506941e3d7 Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 16:26:36 +0200 Subject: [PATCH 09/12] add a check for SKYNET_SERVER_API --- packages/health-check/src/checks/critical.js | 48 ++++++++++++++++---- 1 file changed, 40 insertions(+), 8 deletions(-) diff --git a/packages/health-check/src/checks/critical.js b/packages/health-check/src/checks/critical.js index 69cfa1f9..a72af022 100644 --- a/packages/health-check/src/checks/critical.js +++ b/packages/health-check/src/checks/critical.js @@ -33,35 +33,60 @@ async function uploadCheck(done) { // websiteCheck checks whether the main website is working async function websiteCheck(done) { - return genericAccessCheck("website", process.env.SKYNET_PORTAL_API, done); + return done(await genericAccessCheck("website", process.env.SKYNET_PORTAL_API)); } // downloadCheck returns the result of downloading the hard coded link async function downloadCheck(done) { const url = await skynetClient.getSkylinkUrl(exampleSkylink); - return genericAccessCheck("skylink", url, done); + return done(await genericAccessCheck("skylink", url)); } // skylinkSubdomainCheck returns the result of downloading the hard coded link via subdomain async function skylinkSubdomainCheck(done) { const url = await skynetClient.getSkylinkUrl(exampleSkylink, { subdomain: true }); - return genericAccessCheck("skylink_via_subdomain", url, done); + return done(await genericAccessCheck("skylink_via_subdomain", url)); } // handshakeSubdomainCheck returns the result of downloading the skylink via handshake domain async function handshakeSubdomainCheck(done) { const url = await skynetClient.getHnsUrl("note-to-self", { subdomain: true }); - return genericAccessCheck("hns_via_subdomain", url, done); + return done(await genericAccessCheck("hns_via_subdomain", url)); } // accountWebsiteCheck returns the result of accessing account dashboard website async function accountWebsiteCheck(done) { const url = `${process.env.SKYNET_DASHBOARD_URL}/auth/login`; - return genericAccessCheck("account_website", url, done); + return done(await genericAccessCheck("account_website", url)); +} + +// directServerApiAccessCheck returns the basic server api check on direct server address +async function directServerApiAccessCheck(done) { + if (!process.env.SKYNET_SERVER_API) { + return done({ up: false, info: { message: "SKYNET_SERVER_API env variable not configured" } }); + } + + const [domainAccessCheck, directAccessCheck] = await Promise.all([ + genericAccessCheck("domain", process.env.SKYNET_PORTAL_API), + genericAccessCheck("direct", process.env.SKYNET_SERVER_API), + ]); + + if (domainAccessCheck.ip !== directAccessCheck.ip) { + directAccessCheck.up = false; + directAccessCheck.info = { + message: "Access ip mismatch between domain and direct access", + response: { + domain: { name: process.env.SKYNET_PORTAL_API, ip: domainAccessCheck.ip }, + domain: { name: process.env.SKYNET_SERVER_API, ip: directAccessCheck.ip }, + }, + }; + } + + return done(directAccessCheck); } // accountHealthCheck returns the result of accounts service health checks @@ -86,7 +111,7 @@ async function accountHealthCheck(done) { done({ name: "accounts", time: calculateElapsedTime(time), ...data }); } -async function genericAccessCheck(name, url, done) { +async function genericAccessCheck(name, url) { const time = process.hrtime(); const data = { up: false, url }; @@ -103,10 +128,17 @@ async function genericAccessCheck(name, url, done) { data.ip = error?.response?.ip ?? null; } - done({ name, time: calculateElapsedTime(time), ...data }); + return { name, time: calculateElapsedTime(time), ...data }; } -const checks = [uploadCheck, websiteCheck, downloadCheck, skylinkSubdomainCheck, handshakeSubdomainCheck]; +const checks = [ + uploadCheck, + websiteCheck, + downloadCheck, + skylinkSubdomainCheck, + handshakeSubdomainCheck, + directServerApiAccessCheck, +]; if (process.env.ACCOUNTS_ENABLED === "1") { checks.push(accountHealthCheck, accountWebsiteCheck); From 9729bf7cff383a8c30129f0b4558769ea4a19c99 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Karol=20Wypch=C5=82o?= Date: Wed, 16 Jun 2021 16:39:25 +0200 Subject: [PATCH 10/12] proxy_redirect typo --- docker/nginx/conf.d/client.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/nginx/conf.d/client.conf b/docker/nginx/conf.d/client.conf index 48d5037e..3c20dd13 100644 --- a/docker/nginx/conf.d/client.conf +++ b/docker/nginx/conf.d/client.conf @@ -332,7 +332,7 @@ server { # rewrite proxy request to use correct host uri from env variable (required to return correct location header) set_by_lua $SKYNET_SERVER_API 'return os.getenv("SKYNET_SERVER_API")'; - proxy_rewrite https://siad $SKYNET_SERVER_API; + proxy_redirect https://siad $SKYNET_SERVER_API; # proxy /skynet/tus requests to siad endpoint with all arguments proxy_pass http://siad; From 4689a7d92ec2f584df7be555dd20a127effd7717 Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Wed, 16 Jun 2021 17:02:52 +0200 Subject: [PATCH 11/12] rename check --- packages/health-check/src/checks/critical.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/health-check/src/checks/critical.js b/packages/health-check/src/checks/critical.js index a72af022..c8abdbff 100644 --- a/packages/health-check/src/checks/critical.js +++ b/packages/health-check/src/checks/critical.js @@ -71,8 +71,8 @@ async function directServerApiAccessCheck(done) { } const [domainAccessCheck, directAccessCheck] = await Promise.all([ - genericAccessCheck("domain", process.env.SKYNET_PORTAL_API), - genericAccessCheck("direct", process.env.SKYNET_SERVER_API), + genericAccessCheck("portal_api_access", process.env.SKYNET_PORTAL_API), + genericAccessCheck("direct_server_api_access", process.env.SKYNET_SERVER_API), ]); if (domainAccessCheck.ip !== directAccessCheck.ip) { From 34eb822cf8b5d0fd118f597e4b9eac8af2e9bddb Mon Sep 17 00:00:00 2001 From: Karol Wypchlo Date: Thu, 17 Jun 2021 13:45:53 +0200 Subject: [PATCH 12/12] explain env vars --- setup-scripts/setup-docker-services.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup-scripts/setup-docker-services.sh b/setup-scripts/setup-docker-services.sh index 84b75ddd..7ae43e85 100755 --- a/setup-scripts/setup-docker-services.sh +++ b/setup-scripts/setup-docker-services.sh @@ -31,7 +31,7 @@ docker-compose --version # sanity check # * AWS_ACCESS_KEY_ID - (optional) if using route53 as a dns loadbalancer # * AWS_SECRET_ACCESS_KEY - (optional) if using route53 as a dns loadbalancer # * API_PORT - (optional) the port on which siad is listening, defaults to 9980 -# * PORTAL_NAME - the name of the portal +# * PORTAL_NAME - a string representing name of your portal e.g. `siasky.xyz` or `my skynet portal` (internal use only) # * DISCORD_BOT_TOKEN - (optional) only required if you're using the discord notifications integration # * SKYNET_DB_USER - (optional) if using `accounts` this is the MongoDB username # * SKYNET_DB_PASS - (optional) if using `accounts` this is the MongoDB password @@ -44,7 +44,7 @@ docker-compose --version # sanity check # * CR_CLUSTER_NODES - (optional) if using `accounts` the list of servers (with ports) which make up your CockroachDB cluster, e.g. `helsinki.siasky.net:26257,germany.siasky.net:26257,us-east.siasky.net:26257` if ! [ -f /home/user/skynet-webportal/.env ]; then HSD_API_KEY=$(openssl rand -base64 32) # generate safe random key for handshake - printf "SSL_CERTIFICATE_STRING=siasky.net, *.siasky.net, *.hns.siasky.net\nSKYNET_PORTAL_API=https://siasky.net\nSKYNET_SERVER_API=https://siasky.net\nSKYNET_DASHBOARD_URL=https://account.example.com\nEMAIL_ADDRESS=email@example.com\nSIA_WALLET_PASSWORD=\nHSD_API_KEY=${HSD_API_KEY}\nCLOUDFLARE_AUTH_TOKEN=\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nPORTAL_NAME=\nDISCORD_BOT_TOKEN=\n" > /home/user/skynet-webportal/.env + printf "SSL_CERTIFICATE_STRING=siasky.net, *.siasky.net, *.hns.siasky.net\nSKYNET_PORTAL_API=https://siasky.net\nSKYNET_SERVER_API=https://eu-dc-1.siasky.net\nSKYNET_DASHBOARD_URL=https://account.example.com\nEMAIL_ADDRESS=email@example.com\nSIA_WALLET_PASSWORD=\nHSD_API_KEY=${HSD_API_KEY}\nCLOUDFLARE_AUTH_TOKEN=\nAWS_ACCESS_KEY_ID=\nAWS_SECRET_ACCESS_KEY=\nPORTAL_NAME=\nDISCORD_BOT_TOKEN=\n" > /home/user/skynet-webportal/.env fi # Start docker container with nginx and client