UNPKG

netget

Version:

Rette Adepto/ Recibido Directamente.

471 lines (394 loc) 16.9 kB
import inquirer from 'inquirer'; import fs from 'fs'; import path from 'path'; import chalk from 'chalk'; /** * Configuration file in order to set the nginx.conf file for OpenResty. * The file will be created at /usr/local/openresty/nginx/conf/nginx.conf * @module NetGetX * @submodule OpenResty */ const configPath = '/usr/local/openresty/nginx/conf'; const nginxConfigPath = path.join(configPath, 'nginx.conf'); const sslSelfSignedCertPath = '/etc/ssl/certs/cert.pem'; const sslSelfSignedKeyPath = '/etc/ssl/private/privkey.key'; const sqliteDatabasePath = '/opt/.get/domains.db'; /** * The content of the nginx.conf file. * The file contains the configuration for the OpenResty server, including the SSL certificate and key loading logic. * @memberof module:NetGetX.OpenResty */ const nginxConfigContent = ` user root; events { worker_connections 1024; } http { resolver 8.8.8.8 8.8.4.4 valid=300s; include mime.types; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 65; types_hash_max_size 2048; default_type application/octet-stream; # Gzip settings gzip on; gzip_disable "msie6"; add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always; lua_shared_dict ssl_cache 10m; lua_package_path "/usr/local/share/lua/5.1/?.lua;/usr/local/openresty/lualib/?.lua;;"; error_log /usr/local/openresty/nginx/logs/error.log; access_log /usr/local/openresty/nginx/logs/access.log; server { listen 80; listen [::]:80; server_name _; # Redirect all HTTP traffic to HTTPS return 301 https://$host$request_uri; } server { listen 443 ssl; listen [::]:443 ssl; server_name _; # Default SSL configuration ssl_certificate ${sslSelfSignedCertPath}; # Updated to use self-signed certificate ssl_certificate_key ${sslSelfSignedKeyPath}; # Updated to use self-signed key set $target_url ""; set $root ""; access_by_lua_block { -- Get the requested domain local ssl_cache = ngx.shared.ssl_cache local host = ngx.var.host local wildcard_domain = "*." .. host:match("[^.]+%.(.+)") -- Store the requested domain in the shared dictionary if host then ssl_cache:set("host", host) ssl_cache:set("wildcard_domain", wildcard_domain) end if not host then ngx.log(ngx.ERR, "Host is nil") return ngx.exit(ngx.HTTP_BAD_REQUEST) end } ssl_certificate_by_lua_block { local ssl = require "ngx.ssl" local sqlite = require "lsqlite3" local ok, err = ssl.clear_certs() if not ok then ngx.log(ngx.ERR, "failed to clear existing (fallback) certificates") return ngx.exit(ngx.ERROR) end -- Get the requested SNI (Server Name Indication) local host, err = ssl.server_name() if not host then ngx.log(ngx.ERR, "Failed to retrieve SNI: ", err) return ngx.exit(ngx.HTTP_BAD_REQUEST) end ngx.log(ngx.ERR, "Requested host: ", host) -- Open the SQLite database local db, err = sqlite.open("${sqliteDatabasePath}") if not db then ngx.log(ngx.ERR, "Failed to open SQLite database: ", err) return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- Prepare SQL query to find SSL certificate file paths local stmt, err = db:prepare("SELECT sslCertificate, sslCertificateKey FROM domains WHERE domain = ?") if not stmt then ngx.log(ngx.ERR, "Failed to prepare SQL statement: ", err) db:close() return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- Bind the requested host to the query local res, err = stmt:bind_values(host) if not res then ngx.log(ngx.ERR, "Failed to bind values to SQL statement: ", err) stmt:finalize() db:close() return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- Execute the query local result = stmt:step() local cert_path, key_path if result == sqlite.ROW then cert_path = stmt:get_value(0) key_path = stmt:get_value(1) else ngx.log(ngx.ERR, "No exact match for host: ", host, ", trying wildcard domain") stmt:finalize() -- Prepare a new query for wildcard domain stmt, err = db:prepare("SELECT sslCertificate, sslCertificateKey FROM domains WHERE domain = ?") if not stmt then ngx.log(ngx.ERR, "Failed to prepare SQL statement for wildcard domain: ", err) db:close() return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- Construct wildcard domain local wildcard_domain = "*." .. host:match("[^.]+%.(.+)") res, err = stmt:bind_values(wildcard_domain) if not res then ngx.log(ngx.ERR, "Failed to bind values to SQL statement for wildcard domain: ", err) stmt:finalize() db:close() return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- Execute the query for wildcard domain result = stmt:step() if result == sqlite.ROW then cert_path = stmt:get_value(0) key_path = stmt:get_value(1) else ngx.log(ngx.ERR, "Wildcard domain not configured: ", wildcard_domain) stmt:finalize() db:close() return ngx.exit(ngx.HTTP_NOT_FOUND) end end stmt:finalize() db:close() if not cert_path or not key_path then ngx.log(ngx.ERR, "SSL configuration missing for domain: ", host) return ngx.exit(ngx.HTTP_INTERNAL_SERVER_ERROR) end -- Load the SSL certificate local my_load_certificate_chain = function(cert_path) local file, err = io.open(cert_path, "r") if not file then ngx.log(ngx.ERR, "Failed to open certificate file: ", err) return nil end local pem_cert_chain = file:read("*a") file:close() return pem_cert_chain end --Load the SSL private key local my_load_private_key = function(key_path) local file, err = io.open(key_path, "r") if not file then ngx.log(ngx.ERR, "Failed to open private key file: ", err) return nil end local pem_private_key = file:read("*a") file:close() return pem_private_key end local pem_cert_chain = assert(my_load_certificate_chain(cert_path), "Failed to load certificate chain") local der_cert_chain, err = ssl.cert_pem_to_der(pem_cert_chain) if not der_cert_chain then ngx.log(ngx.ERR, "Failed to convert certificate chain to DER: ", err) return ngx.exit(ngx.ERROR) end local ok, err = ssl.set_der_cert(der_cert_chain) if not ok then ngx.log(ngx.ERR, "Failed to set DER certificate: ", err) return ngx.exit(ngx.ERROR) end local pem_pkey = assert(my_load_private_key(key_path), "Failed to load private key") local der_pkey, err = ssl.priv_key_pem_to_der(pem_pkey) if not der_pkey then ngx.log(ngx.ERR, "Failed to convert private key to DER: ", err) return ngx.exit(ngx.ERROR) end local ok, err = ssl.set_der_priv_key(der_pkey) if not ok then ngx.log(ngx.ERR, "Failed to set DER private key: ", err) return ngx.exit(ngx.ERROR) end } location / { # Lua block to handle dynamic content content_by_lua_block { local sqlite = require "lsqlite3" local db, err = sqlite.open("${sqliteDatabasePath}") -- Corrected method call if not db then ngx.say("Failed to open SQLite database: ", err) return end local ssl_cache = ngx.shared.ssl_cache local host = ssl_cache:get("host") local wildcard_domain = ssl_cache:get("wildcard_domain") local stmt, err = db:prepare("SELECT type, target FROM domains WHERE domain = ?") if not stmt then ngx.say("Failed to prepare SQL statement: ", err) return end local res, err = stmt:bind_values(host) if not res then ngx.say("Failed to bind values to SQL statement: ", err) return end res = stmt:step() if res == sqlite.ROW then type = stmt:get_value(0) ngx.log(ngx.ERR, "Type: ", type) target = stmt:get_value(1) ngx.log(ngx.ERR, "Target: ", target) stmt:finalize() db:close() else -- ngx.log(ngx.ERR, "Host not found, trying wildcard domain: ", wildcard_domain) stmt:finalize() stmt, err = db:prepare("SELECT type, target FROM domains WHERE domain = ?") if not stmt then ngx.say("Failed to prepare SQL statement for wildcard domain: ", err) db:close() return end res, err = stmt:bind_values(wildcard_domain) if not res then ngx.say("Failed to bind values to SQL statement for wildcard domain: ", err) stmt:finalize() db:close() return end res = stmt:step() if res == sqlite.ROW then type = stmt:get_value(0) -- ngx.log(ngx.ERR, "Type: ", type) target = stmt:get_value(1) -- ngx.log(ngx.ERR, "Target: ", target) stmt:finalize() db:close() else ngx.status = ngx.HTTP_NOT_FOUND ngx.say("Wildcard domain not configured") stmt:finalize() db:close() return end end if type == "static" then ngx.var.root = target ngx.exec("@dynamic_root") elseif type == "server" then ngx.var.target_url = "http://127.0.0.1:" .. target -- ngx.log(ngx.ERR, "Proxying to: ", ngx.var.target_url) ngx.exec("@server") else ngx.status = ngx.HTTP_NOT_FOUND ngx.say("Invalid configuration for domain") end } } # Internal location block to serve static content location @dynamic_root { internal; root $root; # allow all; # autoindex on; index index.html index.htm; try_files $uri /index.html; } # Internal location block to proxy dynamic content location @server { internal; proxy_pass $target_url; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; # Proxy SSL proxy_ssl_server_name on; # Proxy headers proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host; proxy_set_header X-Forwarded-Port $server_port; # Proxy timeouts proxy_connect_timeout 60s; proxy_send_timeout 60s; proxy_read_timeout 60s; } # Bloquear acceso a archivos sensibles location ~* \.(conf|sh|sql|env|log)$ { deny all; } # Error page redirection error_page 500 /500.html; location = /500.html { root /opt/.get/html; } error_page 502 /502.html; location = /502.html { root /opt/.get/html; } } }`; /** * Creates the main nginx.conf file with the specified content. * @param {string} configPath - The path where the nginx.conf file will be created. */ const createNginxConfig = (configPath) => { const nginxConfigPath = path.join(configPath, 'nginx.conf'); fs.writeFileSync(nginxConfigPath, nginxConfigContent, 'utf8'); console.log(chalk.green(`nginx.conf created successfully at ${nginxConfigPath}`)); }; const validateNginxConfig = () => { if (fs.existsSync(nginxConfigPath)) { const existingContent = fs.readFileSync(nginxConfigPath, 'utf8'); return existingContent === nginxConfigContent; } return false; }; const setNginxConfigFile = async () => { if (!validateNginxConfig()) { try{ console.log(chalk.yellow('The existing nginx.conf file is different from the expected configuration. This may cause unexpected behavior.')); const { createConfig } = await inquirer.prompt([ { type: 'confirm', name: 'createConfig', message: `Do you want to proceed and overwrite the current nginx.conf file at ${configPath}?`, default: false, }, ]); if (createConfig) { if (validateNginxConfig()) { console.log(chalk.yellow('nginx.conf already exists and is up to date.')); } else { createNginxConfig(configPath); } } else { console.log(chalk.red('Operation cancelled by the user.')); } } catch (error) { console.log(chalk.red('An error occurred: ', error)); } } }; /** * Checks if nginx.conf exists, and if not, prompts the user to create it, * explaining why it is important. */ const ensureNginxConfigFile = async () => { if (!fs.existsSync(nginxConfigPath)) { console.log(chalk.yellow( `The nginx.conf file does not exist at ${nginxConfigPath}.\n` + 'This file is essential for OpenResty to function properly, as it contains the main server configuration, including security rules, SSL certificates, and proxy routes.\n' + 'Without this file, the server will not be able to start or securely manage domains.' )); try { const { createConfig } = await inquirer.prompt([ { type: 'confirm', name: 'createConfig', message: `Do you want to create the nginx.conf file now at ${configPath}?`, default: true, }, ]); if (createConfig) { createNginxConfig(configPath); } else { console.log(chalk.red('Operation cancelled by the user. The server will not work without nginx.conf.')); } } catch (error) { console.log(chalk.red('An error occurred: ', error)); } } else { console.log(chalk.green('The nginx.conf file already exists.')); } }; export { setNginxConfigFile as setNginxConfigFile, ensureNginxConfigFile };