miércoles, 24 de septiembre de 2025

WEBPROPv2 (VI). Instalación en un servidor con nginx (I). Instalar nginx, openresty, Lua y certificados

 1. Instalar nginx

Si lo instalamos en la máquina local

# 1. Instalar nginx en Ubuntu
sudo apt update
sudo apt install nginx

# 2. Arrancar nginx
sudo systemctl start nginx

# 3. Verificar 
systemctl status nginx.service

Pero nos da el problema que elpuerto 80 ya está ocupado:

informatica@srv-glpi-16:~$ systemctl status nginx.service
× nginx.service - A high performance web server and a reverse proxy server
     Loaded: loaded (/usr/lib/systemd/system/nginx.serv
ice; enabled; preset: enabled)
     Active: failed (Result: exit-code) since Wed 2025-09-24 11:58:58 CEST; 1min 18s ago
       Docs: man:nginx(8)
    Process: 73037 ExecStartPre=/usr/sbin/nginx -t -q -g daemon on; master_process on; (code=exited, status=0/SUCC
ESS)
    Process: 73038 ExecStart=/usr/sbin/nginx -g daemon on; master_process on; (code=exited, status=1/FAILU
RE)
        CPU: 11ms

Sep 24 11:58:56 srv-glpi-16 nginx[73038]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Sep 24 11:58:56 srv-glpi-16 nginx[73038]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
Sep 24 11:58:57 srv-glpi-16 nginx[73038]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Sep 24 11:58:57 srv-glpi-16 nginx[73038]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
Sep 24 11:58:57 srv-glpi-16 nginx[73038]: nginx: [emerg] bind() to 0.0.0.0:80 failed (98: Address already in use)
Sep 24 11:58:57 srv-glpi-16 nginx[73038]: nginx: [emerg] bind() to [::]:80 failed (98: Address already in use)
Sep 24 11:58:58 srv-glpi-16 nginx[73038]: nginx: [emerg] still could not bind()
Sep 24 11:58:58 srv-glpi-16 systemd[1]: nginx.service: Control process exited, code=exited
, status=1/FAILURE
Sep 24 11:58:58 srv-glpi-16 systemd[1]: nginx.service: Failed with result 'exi
t-code'.
Sep 24 11:58:58 srv-glpi-16 systemd[1]: Failed to start nginx.service - A high performance
 web server and a reverse proxy server.

Para ello hay que actuar sobre la configuración de nginx y utilizar otro puerto

Para ello buscamos donde puede estar la línea "listen 80"


# 1. Buscar la cadena "listen" en los ficheros de estos directorios
grep -R "listen" /etc/nginx/sites-enabled/ /etc/nginx/conf.d/

Y aparece el fichero "/etc/nginx/sites-enabled/default"

Lo modificamos con nano y cambiamos "80" por "8088"

# 1. Cambiamos 80por 8088
server {
        listen 8088 default_server;
        listen [::]:8088 default_server;


Y arrancamos el servicio de nginx y lo verificamos:


# 1. Arrancar nginx
sudo systemctl start nginx

# 2. Verificar 
systemctl status nginx.service

Y ya lo tenemos en marcha

2. Substituir nginx por openresty

 Veamos los pasos a realizar según Cloudspinx:

#1. Bloquear nginx
sudo systemctl disable nginx
sudo systemctl stop nginx

#2. Instalar prerequisitos
sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates lsb-release

#3. Importar la clave GPG de descarga (para Ubuntu 22 +)
wget -O - https://openresty.org/package/pubkey.gpg | sudo gpg --dearmor -o /usr/share/keyrings/openresty.gpg

#4. Añadir el repositorio en sistemas x86_64 or amd64 para Ubuntu 22 +
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/openresty.gpg] http://openresty.org/package/ubuntu $(lsb_release -sc) main" | sudo tee /etc/apt/sources.list.d/openresty.list > /dev/null

#5. Actualizar el índice APT
sudo apt-get update

#6. Instalar el paquete
sudo apt-get -y install openresty

#6.1. Aquí da un error de que el puerto 80 está ocupado
#     Por tanto hay que entrar en /usr/local/openresty/nginx/conf/nginx.conf
#     y cambiar "listen       80;" 
#           por "listen       8080;" u otro puerto que no esté pillado
#     y volver a ejecutar :
#     sudo apt-get -y install openresty


#7. Verificar la instalación que debe devolver: nginx version: openresty/X.Y.Z
openresty -v

#8. Arrancar/rearrancar/ para rel servicio
sudo systemctl start openresty
sudo systemctl restart openresty
sudo systemctl stop openresty

#9. Permitir el servicio al arrancar (boot)
sudo systemctl enable openresty

#10. Ver el estado del servicio
sudo systemctl status openresty


Ojo, ahora el fichero de configuración esta en una de estas 2 carpetas:
  • /etc/openresty
  • /usr/local/openresty

Y los logs están en:

  • /usr/local/openresty/nginx/logs/error.log

Y aparece el mismo error que el puerto 80 está ocupado (después del punto 6)

Para solucionarlo hay que ir al fichero de configuración y cambiar el listen 80 por el puerto 8088 en el fichero "/usr/local/openresty/nginx/conf/nginx.conf"

Para ver que funciona abrimos el navegador con esta direccion:

http://IP_SERVIDOR:8088/

y vemos que funciona


3. Instalación de luarocks 

Luarocks es un paquete de instación de módulos Lua para openresty

Para instalarlo hacemos:

# 1.Prerequisitos
sudo apt install build-essential libreadline-dev unzip

# 2. Descargar
wget https://luarocks.org/releases/luarocks-3.12.2.tar.gz

# 3. Descomprimir
tar zxpf luarocks-3.12.2.tar.gz

# 4. Compilar e instalar 
cd luarocks-3.12.2
sudo ./configure && make && sudo make install
sudo luarocks install luasocket
lua

# Debe de aparecer
Lua 5.4.7  Copyright (C) 1994-2024 Lua.org, PUC-Rio

# 1.Instalar paquetes.
# En este caso instala el paquete lua-python para poder ejecutar modulos python
sudo luarocks install lua-resty-http

sudo luarocks install lua-resty-openssl

sudo luarocks install lua-resty-iputils

# 2. Ver los módulos instalados
luarocks list


Veamos algunos usos de luarocks

A la hora de instalar lua han aparecido algunos problemas que se han solucionado con chatgpt

4. Copiar los ficheros de configuración de openresty

Los ficheros a copiar al directorio /usr/local/openresty/nginx/conf són:

1. nginx.conf
que es el fichero de configuración principal. Para cada una de las rutas permitidas, se redirige a otros ficheros de configuración. En principio actua en http://localhost:5001/ que se encuentra en la entrada upstream softpropsrv {server 127.0.0.1:5001;

Utiliza el fichero eu_filter.lua para filtrar las IPs de los usuarios de Europa
Entre otras cosas realiza:
  • con el fichero mime.types definimos los tipos de ficheros
  • se definen las rutas con location (/, /login, /softprop,/ /static,/protected,/logout ...) y sus ficheros de includes
  • un "rate limiting" para evitar un número de peticiones excesivas
  • filtra para solo los usuarios de Europa
  • definimos los ficheros donde se guardarán los accesos y los errores
  • definimos donde estan los certificados y claves para ssl
  • exigimos al cliente que presente certifiacado
  • en ca_all_certificates.cer tenemos todos los certificados de las CA de confianza
  • esd importante epecificar los ficheros "static" que se recogerán

worker_processes  auto;

#error_log  logs/error.log;
error_log  logs/error.log  notice;
#error_log  logs/error.log  info;

#pid        logs/nginx.pid;


events {
    worker_connections  1024;
}


http {
    include       mime.types;
    default_type  application/octet-stream;

    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    error_log   /usr/local/openresty/nginx/logs/error.log info;

    access_log  logs/access.log  main;

    sendfile        on;
    #tcp_nopush     on;

    #keepalive_timeout  0;
    keepalive_timeout  65;

    #gzip  on;
    
    #--- BEGIN EDU
    # Enable Lua shared dictionary for sessions
    lua_shared_dict sessions 10m;
    
    # Load the resty modules we need
    lua_package_path '/usr/local/share/lua/5.1/?.lua;/usr/local/lib/lua/5.1/?.lua;/usr/local/share/lua/5.1/resty/?.lua;/usr/local/openresty/lualib/?.lua;/usr/local/openresty/nginx/conf/?.lua;/usr/local/openresty/site/lualib/?.lua;/usr/local/openresty/site/lualib/resty/?.lua;;';
    lua_package_cpath './?.so;/usr/local/lib/lua/5.1/?.so;/usr/lib/x86_64-linux-gnu/lua/5.1/?.so;/usr/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so;/home/eduard/.luarocks/lib/lua/5.1/?.so;;';
        
    # Initialize the IP filter module at the http level
    init_by_lua_block {
        require("resty.core")
        require("resty.iputils").enable_lrucache()
    }
    # Enable cache
    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=100m inactive=60m use_temp_path=off;

    # Creamos una bateria de servidores para softprop con un solo servidor
    upstream softpropsrv {
	server 127.0.0.1:5001;
        #server 192.168.10.5:5001;
#server edu.tavernes.es:5001; #server 0.0.0.0:5001; ##server 127.0.0.1:5001; ##server el_que_sea.com; } # Hacemos un "rate limiting" para evitar un número de peticiones excesivas # Define rate limit (10 requests per second, with a burst of 20) por IP limit_req_zone $binary_remote_addr zone=my_api_limit:10m rate=10r/s; #resolver 192.168.10.5; # Servidor de DNS #--- END EDU server { #listen 8080; listen 8449 ssl; #->New server_name localhost; #server_name edu.tavernes.es; #charset koi8-r; #access_log logs/host.access.log main; #-> Begin new # SSL certificate configuration #ssl_certificate /usr/local/openresty/nginx/conf/wildcard2023Nginx.crt; #ssl_certificate_key /usr/local/openresty/nginx/conf/wildcard2023Nginx.rsa; ssl_certificate /usr/local/openresty/nginx/conf/wildcard.tavernes.es.crt; ssl_certificate_key /usr/local/openresty/nginx/conf/wildcard.tavernes.es.key; # Client certificate part ssl_client_certificate /usr/local/openresty/nginx/conf/ca_all.cer; # Set of all the CA certrificates ssl_verify_client on; # Require client certificate ssl_verify_depth 2; # Optional SSL settings for security and performance ssl_protocols TLSv1.2 TLSv1.3; ssl_ciphers HIGH:!aNULL:!MD5; # Common proxy settings (optional) proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $host; proxy_set_header X-Forwarded-Proto https; location / { # Aplicamos el rate limiting a esta ruta limit_req zone=my_api_limit burst=30 delay=10; # Get info from client certificate #proxy_pass https://0.0.0.0:5001/login; # Forward request to Python app proxy_set_header X-SSL-Client-Cert $ssl_client_cert; proxy_set_header X-SSL-Client-Verify $ssl_client_verify; proxy_set_header X-SSL-Client-DN $ssl_client_s_dn; proxy_set_header X-SSL-Client-Serial $ssl_client_serial; # Filter by UE country content_by_lua_block{ local filter = require("eu_filter") local client_ip = ngx.var.remote_addr if not filter.filter() then ngx.status = ngx.HTTP_FORBIDDEN ngx.say("Access denied: Your location is not permitted.") return ngx.exit(ngx.HTTP_FORBIDDEN) end } #root html; #index index.html index.htm; return 301 /login; } # Location of static files location /static/ { #1. Solo ficheros dentro del servidor openresty (nginx) ## Are good both root and alias, but in alias you must append "static/" to the path ##root /home/eduard/MyPython/11.softprop-01; # Path where static files are stored #alias /home/eduard/MyPython/11.softprop-01/static/; # Path where static files are stored ## Use browser cache !!! #expires max; #access_log off; #gzip on; #gzip_types text/css application/javascript image/svg+xml; #2. Solo ficheros dentro del uno de los servidores que actuan como web servers del upstream "softpropsrv" proxy_pass https://softpropsrv/static/; proxy_set_header Host $host; proxy_ssl_verify off; # If using SSL proxy_cache my_cache; proxy_cache_valid 200 30d; proxy_cache_use_stale error timeout updating; add_header X-Cache-Status $upstream_cache_status; } # Serve the login form location = /login { include nginx.conf.login; } location = /initial { include nginx.conf.initial; } # Protected proxy location location /protected { include nginx.conf.protected; } location /softprop { # Timeout settings # Problems with slow processes #proxy_connect_timeout 90s; # Default is 60s #proxy_send_timeout 90s; # Default is 60s # Time for waiting a response proxy_read_timeout 1000s; # Default is 60s #send_timeout 90s; # Default is 60s include nginx.conf.softprop; } #location = /success { # include nginx.conf.success; #} location = /error { include nginx.conf.error; } location = /logout { include nginx.conf.logout; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }


2. nging.conf.login:
se aplica a la ruta /login y muestra un formulario que pide usuario y contraseña; para validar envía a la ruta /inicial. Si ya se había validado anteriormente reenvia a /protected/softprop/tree. Si detecta la cookie que se ha generado en /initial te da como ya validado.
Lee tres ficheros que definen la pantalla (form_style.css, form_script.js y form_body.html) y tambien el fichero xmauth.lua

 default_type text/html;
            content_by_lua_block {
                -- Check if already authenticated
                -- local session_id = ngx.var.cookie_session_id
                -- local tab_id = ngx.var.cookie_tab_id
                -- local sessions = ngx.shared.sessions
                -- local tabs = ngx.shared.tabs
                
                -- if session_id and sessions:get(session_id) and tab_id and tabs:get(tab_id) then 
                --     return ngx.redirect("/protected/softprop/tree")
                -- end
                
                -- Helper function to read the entire content of a file.
                local function read_file(filename)
                    local mypath="/usr/local/openresty/nginx/conf/"
                    local new_filename = mypath .. filename
                    local f, err = io.open(new_filename, "r")
                    if not f then
                        ngx.log(ngx.ERR, "failed to open file ", new_filename, ": ", err)
                        return nil
                    end
                    local content = f:read("*a")
                    f:close()
                    return content
                end
                -- End Helper function
                
                -- Create the tab_id
                --local resty_random = require "resty.random"
                --local tab_id = ngx.encode_base64(resty_random.bytes(32))
                local xmauth = require "xmauth"
                local tab_id = xmauth.random_string(32)
                local n = 0
                
                -- Read content of three files into three string variables.
                local form_style  = read_file("form_style.css","r")
                local form_script = read_file("form_script.js","r")
                local form_body =   read_file("form_body.html","r")
                local login_form = [[
                    <!DOCTYPE html>
                    <html>    
                        <head>
                            <title>Login</title>
                            <style>
                                $STYLE$
                            </style
                            
                        </head>
                        <body>
                            <script>
                                $SCRIPT$ 
                            </script>
                            $BODY$
                        </body>    
                    </html>
                ]]   
                login_form, n =string.gsub(login_form, "%$STYLE%$" , form_style)  
                login_form, n =string.gsub(login_form, "%$SCRIPT%$", form_script)  
                    
                form_body , n =string.gsub(form_body , "%$TAB_ID%$", tab_id)                            
                
                login_form, n =string.gsub(login_form, "%$BODY%$"  , form_body) 
                -- ngx.log(ngx.INFO, login_form)
                
                ngx.say(login_form)
            }


3. nginx.conf.initial

            content_by_lua_block {
                -- Get authentication credentials
                ngx.req.read_body()
                local args = ngx.req.get_post_args() or {}
                local username = args["username"]
                local password = args["password"]
                local tab_id   = args["tab_id"]
                
                -- certificate info
                -- local cert = ngx.var.ssl_client_cert
                local cert_verify=ngx.var.ssl_client_verify
                local cert_dn=ngx.var.ssl_client_s_dn
                
                ngx.log(ngx.INFO, "initial username " .. username )
                ngx.log(ngx.INFO, "initial password " .. password )
                ngx.log(ngx.INFO, "initial tab_id " .. tab_id )
                ngx.log(ngx.INFO, "initial cert_dn " .. cert_dn )
                
                if not username or not password or not tab_id then
                    ngx.redirect("/login")
                    return
                end
                
                -- Create HTTP connection
                ngx.log(ngx.INFO, "initial validating user to python service" )
                local http = require "resty.http"
                local cjson = require "cjson"
                local httpc = http.new()
                
                -- Connect to Python auth service
                -- local res, err = httpc:request_uri("http://127.0.0.1:5000/auth", {
                -- Per a que accepte un nom de domini (com "edu.tavernes.es" cal tenir 
                --    una sentencia "resolver IP_del_servidor_DNS".
                --    sinó no pot trobar el domini. Per tant cal ficar directament la Ip
                --    La sentencia següent no va, perquè bno troba "edu.tavernes.es" 
                --    local res, err = httpc:request_uri("https://edu.tavernes.es:5000/auth", {
                local res, err = httpc:request_uri("https://192.168.10.5:5000/auth", {
                    method = "POST",
                    body = cjson.encode({
                        username = username,
                        password = password,
                        cert_verify = cert_verify,
                        cert_dn = cert_dn                        
                    }),
                    headers = {
                        ["Content-Type"] = "application/json",
                    },
                    -- if we have problems with the certificates ???
                    ssl_verify = false  -- Disable SSL verification (useful for self-signed certs)
                })
                ngx.log(ngx.INFO, "initial after validating user to python service" )
                if not res then
                    ngx.log(ngx.ERR, "Failed to request: ", err)
                    ngx.redirect("/error")
                    return
                end
                
                local body = cjson.decode(res.body)
                local body_json=cjson.encode(body)
                ngx.log(ngx.INFO, "initial body=" .. body_json )
                
                if body.authenticated then
                    --- ngx.redirect("/success")
                    --- ngx.redirect("https://www.gva.es")
                    -- Create session
                    local sessions = ngx.shared.sessions
                    --local resty_random = require "resty.random"
                    local xmauth = require "xmauth"
                    --local session_id = ngx.encode_base64(resty_random.bytes(32))
                    --local session_id = xmauth.random_string(32)
                    local my_data = { username = username }
                    local my_data_json = cjson.encode(my_data)
                     
                    --sessions:set(session_id, my_data_json , 3600) -- 1 hour expiration
                    sessions:set(tab_id, my_data_json , 3600) -- 1 hour expiration

                    local user_tab = sessions:get(tab_id) or "5"
                    ngx.log(ngx.INFO,"Initail tab_id + user_tab: " .. tab_id .. '    ' .. user_tab)
                    
                    -- Set session and tab_id cookies for authentication
                    ngx.header["Set-Cookie"] = {
                        -- "session_id=" .. my_data_json .. "; HttpOnly; Path=/",
                        tab_id .. "=" .. my_data_json .. "; HttpOnly; Path=/",
                        -- "username=" .. username .. "; HttpOnly; Path=/"
                    }
                    
                    local cookies = ngx.header["Set-Cookie"]
                    if type(cookies) == "table" then
                        for _, cookie in ipairs(cookies) do
                            ngx.log(ngx.INFO,"Set-Cookie Header: " .. cookie)
                        end
                    else
                        ngx.log(ngx.INFO,"Set-Cookie Header: " .. cookies)
                    end
                    
                    --ngx.log(ngx.INFO, "initial redirecting to /protected/softprop/tree" )
                    ngx.log(ngx.INFO, "initial redirecting to /softprop/tree" )
                    
                    -- Redirect to protected area
                    --local my_url="/protected/softprop/tree?tab_id=" .. ngx.escape_uri(tab_id)
                    -- Redirect to softprop instead of protected area
                    local my_url="/softprop/tree?tab_id=" .. ngx.escape_uri(tab_id)
                    ngx.log(ngx.INFO, "initial my_url=" .. my_url)
                    
                    return ngx.redirect(my_url)
                else
                    ngx.redirect("/error")
                end
            }


nginx.conf.protected

            #root /home/eduard/MyPython/05.fasthtml/menus;  # Base directory for static files
            access_by_lua_block {
                local xmauth = require "xmauth"
                local tab_id = ngx.var.arg_tab_id  -- Get the 'tab_id' parameter
                if not tab_id then
                   ngx.log(ngx.INFO, "nginx.conf.protected: 1 NO parameter tab_id")
                   return ngx.redirect("/login")
                end
                ngx.log(ngx.INFO, "nginx.conf.protected: 2 parameter tab_id= '" .. tab_id )
                local user = xmauth.is_authenticated(tab_id)
                if not user then
                   ngx.log(ngx.INFO, "nginx.conf.protected: 3 NO user")
                   return ngx.redirect("/login")
                end
                -- If the user is authenticated, you can proceed:
                -- ngx.say("Welcome, " .. user .. "!")
                ngx.log(ngx.INFO, "Protected: Welcome authenticated," .. user .. "!")
                return ngx.redirect("/login")
            
                
            }
            
            # Remove /protected from the forwarded URI
            #rewrite ^/protected/(.*) /$1 break;
            rewrite ^/protected/(.*) /$1 redirect;
            
            #include nginx.conf.softprop;
            
            #proxy_pass http://softpropsrv;  # Forward to a backend if needed


4. nginx.conf.error

            default_type text/html;
            content_by_lua_block {
                local error_page = [[
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>Error</title>
                        <style>
                            body { 
                                font-family: Arial, sans-serif;
                                display: flex;
                                justify-content: center;
                                align-items: center;
                                height: 100vh;
                                margin: 0;
                                background-color: #f0f2f5;
                            }
                            .error-container {
                                background: white;
                                padding: 20px;
                                border-radius: 8px;
                                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
                                text-align: center;
                                color: #f44336;
                            }
                            .back-button {
                                margin-top: 15px;
                                padding: 8px 16px;
                                background-color: #f44336;
                                color: white;
                                border: none;
                                border-radius: 4px;
                                text-decoration: none;
                                display: inline-block;
                            }
                            .back-button:hover {
                                background-color: #da190b;
                            }
                        </style>
                    </head>
                    <body>
                        <div class="error-container">
                            <h2>Authentication Failed</h2>
                            <p>Invalid username or password.</p>
                            <a href="/login" class="back-button">Try Again</a>
                        </div>
                    </body>
                    </html>
                ]]
                ngx.say(error_page)
            }


5. nginx.conf.softprop

           access_by_lua_block {
               
           
               local url = ngx.var.request_uri
	           ngx.log (ngx.INFO, "nginx.conf - location /softprop: 0 The current URL is: " .. url)
               local xmauth = require "xmauth"
               local tab_id = ngx.var.arg_tab_id  -- Get the 'tab_id' parameter
               if tab_id then
                   tab_id= tab_id:gsub("[^%w]","")
                   ngx.log(ngx.INFO, "0. tab_id=" .. tab_id .. "0") 
               end    
               if url ~= "/softprop/login" then
                   if not tab_id then 
                       -- Test if the tab_id is a post parameter
                       local method = ngx.req.get_method()
                       if method == "POST" then
                           -- Read request body
                           ngx.req.read_body()
                           local post_args = ngx.req.get_post_args()  -- Get POST parameters
                           ngx.log(ngx.INFO, "nginx.conf - AAAAA-> post_args",table.concat(post_args, ", "))
                           local body_data = ngx.req.get_body_data() 
                           ngx.log(ngx.INFO, "nginx.conf - BBBBB-> body_data",body_data)
                           -- 1. Assume the `hx-vals` passed a parameter named "tab_id"
                           tab_id = post_args["tab_id"]
                           if tab_id then
                               tab_id= tab_id:gsub("[^%w]","")
                               ngx.log(ngx.INFO, "1. tab_id=" .. tab_id .. "1") 
                           end
                           
                           -- 2. Maybe the it is a multipart/form-data
                           if not tab_id and body_data then
                               tab_id = body_data:match('name="tab_id"%s*(.-)%-%-')
                               if tab_id then
                                   tab_id= tab_id:gsub("[^%w]","")
                                   ngx.log(ngx.INFO, "2. tab_id=" .. tab_id .. "2") 
                               end  
                           end    
                       end    
                       if not tab_id then
                           ngx.log(ngx.INFO, "nginx.conf - location /softprop: 1 NO parameter tab_id redirecting to /login")
                           return ngx.redirect("/login")
                       end
                   end
                   --else 
                       ngx.log(ngx.INFO, "nginx.conf - location /softprop: 2 parameter tab_id= " .. tab_id )
                       local user = xmauth.is_authenticated(tab_id)
                       if not user then
                           ngx.log(ngx.INFO, "nginx.conf - location /softprop: 3 NO user redirectiong to /login")
                           return ngx.redirect("/login")
                       else    
                           -- If the user is authenticated, you can proceed:
                           -- ngx.say("Welcome, " .. user .. "!")
                           ngx.log(ngx.INFO, "/SOFTPROP: Welcome authenticated," .. user .. "!")
                       end        
                   --end    
               else
                   ngx.log(ngx.INFO, "/SOFTPROP: Anem a /softprop/login per autehenticar-nos al python !")
               end
            }
            
           
        
            # Proxy to the backend server with the decoded path
            #proxy_pass http://0.0.0.0:5001;
            #proxy_pass http://softpropsrv;
            proxy_pass https://softpropsrv;
            proxy_set_header Host $host;
            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;
            
            # Ensure redirects from the backend work properly
            #proxy_redirect http://127.0.0.1:5001/ /;
            
            


6. nginx.conf.error:

            default_type text/html;
            content_by_lua_block {
                local error_page = [[
                    <!DOCTYPE html>
                    <html>
                    <head>
                        <title>Error</title>
                        <style>
                            body { 
                                font-family: Arial, sans-serif;
                                display: flex;
                                justify-content: center;
                                align-items: center;
                                height: 100vh;
                                margin: 0;
                                background-color: #f0f2f5;
                            }
                            .error-container {
                                background: white;
                                padding: 20px;
                                border-radius: 8px;
                                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
                                text-align: center;
                                color: #f44336;
                            }
                            .back-button {
                                margin-top: 15px;
                                padding: 8px 16px;
                                background-color: #f44336;
                                color: white;
                                border: none;
                                border-radius: 4px;
                                text-decoration: none;
                                display: inline-block;
                            }
                            .back-button:hover {
                                background-color: #da190b;
                            }
                        </style>
                    </head>
                    <body>
                        <div class="error-container">
                            <h2>Authentication Failed</h2>
                            <p>Invalid username or password.</p>
                            <a href="/login" class="back-button">Try Again</a>
                        </div>
                    </body>
                    </html>
                ]]
                ngx.say(error_page)
            }

7. nginx.conf.logout:

            content_by_lua_block {
                local session_store = ngx.shared.sessions
                local tab_id     = ngx.var.cookie_tab_id
                -- local session_id = ngx.var.http_cookie and ngx.var.http_cookie:match("session=([^;]+)")

                -- if session_id then
                if tab_id then
                    session_store:delete(tab_id)
                    ngx.header["Set-Cookie"] = "tab_id=; Path=/; Expires=Thu, 01 Jan 1970 00:00:00 GMT"
                end
                
                ngx.redirect("/login")
            }

5. Ficheros adicionales para el nginx.conf

1. mime.types:

types {
    text/html                                        html htm shtml;
    text/css                                         css;
    text/xml                                         xml;
    image/gif                                        gif;
    image/jpeg                                       jpeg jpg;
    application/javascript                           js;
    application/atom+xml                             atom;
    application/rss+xml                              rss;

    text/mathml                                      mml;
    text/plain                                       txt;
    text/vnd.sun.j2me.app-descriptor                 jad;
    text/vnd.wap.wml                                 wml;
    text/x-component                                 htc;

    image/avif                                       avif;
    image/png                                        png;
    image/svg+xml                                    svg svgz;
    image/tiff                                       tif tiff;
    image/vnd.wap.wbmp                               wbmp;
    image/webp                                       webp;
    image/x-icon                                     ico;
    image/x-jng                                      jng;
    image/x-ms-bmp                                   bmp;

    font/woff                                        woff;
    font/woff2                                       woff2;

    application/java-archive                         jar war ear;
    application/json                                 json;
    application/mac-binhex40                         hqx;
    application/msword                               doc;
    application/pdf                                  pdf;
    application/postscript                           ps eps ai;
    application/rtf                                  rtf;
    application/vnd.apple.mpegurl                    m3u8;
    application/vnd.google-earth.kml+xml             kml;
    application/vnd.google-earth.kmz                 kmz;
    application/vnd.ms-excel                         xls;
    application/vnd.ms-fontobject                    eot;
    application/vnd.ms-powerpoint                    ppt;
    application/vnd.oasis.opendocument.graphics      odg;
    application/vnd.oasis.opendocument.presentation  odp;
    application/vnd.oasis.opendocument.spreadsheet   ods;
    application/vnd.oasis.opendocument.text          odt;
    application/vnd.openxmlformats-officedocument.presentationml.presentation
                                                     pptx;
    application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
                                                     xlsx;
    application/vnd.openxmlformats-officedocument.wordprocessingml.document
                                                     docx;
    application/vnd.wap.wmlc                         wmlc;
    application/wasm                                 wasm;
    application/x-7z-compressed                      7z;
    application/x-cocoa                              cco;
    application/x-java-archive-diff                  jardiff;
    application/x-java-jnlp-file                     jnlp;
    application/x-makeself                           run;
    application/x-perl                               pl pm;
    application/x-pilot                              prc pdb;
    application/x-rar-compressed                     rar;
    application/x-redhat-package-manager             rpm;
    application/x-sea                                sea;
    application/x-shockwave-flash                    swf;
    application/x-stuffit                            sit;
    application/x-tcl                                tcl tk;
    application/x-x509-ca-cert                       der pem crt;
    application/x-xpinstall                          xpi;
    application/xhtml+xml                            xhtml;
    application/xspf+xml                             xspf;
    application/zip                                  zip;

    application/octet-stream                         bin exe dll;
    application/octet-stream                         deb;
    application/octet-stream                         dmg;
    application/octet-stream                         iso img;
    application/octet-stream                         msi msp msm;

    audio/midi                                       mid midi kar;
    audio/mpeg                                       mp3;
    audio/ogg                                        ogg;
    audio/x-m4a                                      m4a;
    audio/x-realaudio                                ra;

    video/3gpp                                       3gpp 3gp;
    video/mp2t                                       ts;
    video/mp4                                        mp4;
    video/mpeg                                       mpeg mpg;
    video/quicktime                                  mov;
    video/webm                                       webm;
    video/x-flv                                      flv;
    video/x-m4v                                      m4v;
    video/x-mng                                      mng;
    video/x-ms-asf                                   asx asf;
    video/x-ms-wmv                                   wmv;
    video/x-msvideo                                  avi;
}


2. eu_filter.lua:

-- EU IP Filtering for OpenResty with internal network detection
-- Save this as /usr/local/openresty/nginx/conf/eu_filter.lua

local _M = {}

-- Define EU country codes
local eu_countries = {
    AT = true, BE = true, BG = true, HR = true, CY = true, CZ = true, 
    DK = true, EE = true, FI = true, FR = true, DE = true, GR = true, 
    HU = true, IE = true, IT = true, LV = true, LT = true, LU = true, 
    MT = true, NL = true, PL = true, PT = true, RO = true, SK = true, 
    SI = true, ES = true, SE = true
}

-- Define your internal network ranges
local internal_networks = {
    "10.0.0.0/8",     -- RFC1918 private IPv4 range
    "172.16.0.0/12",  -- RFC1918 private IPv4 range
    "192.168.0.0/16", -- RFC1918 private IPv4 range
    "127.0.0.0/8"     -- Localhost
}

-- Precompile internal networks using iputils
local iputils = require("resty.iputils")
local internal_cidrs = iputils.parse_cidrs(internal_networks)

-- Function to get real client IP when behind proxies
function _M.get_client_ip()
    -- Check for X-Forwarded-For header (common in proxy setups)
    local xff = ngx.req.get_headers()["X-Forwarded-For"]
    if xff then
        -- Extract the original client IP (first in the chain)
        local ip = xff:match("^([^,]+)")
        if ip then
            return ip
        end
    end
    
    -- Check for other common proxy headers
    local real_ip = ngx.req.get_headers()["X-Real-IP"]
    if real_ip then
        return real_ip
    end
    
    -- Fall back to direct connection IP
    return ngx.var.remote_addr
end

-- Function to check if IP is in internal networks
function _M.is_internal_ip(ip)
    -- ngx.log(ngx.ERR, "is_internal_ip: " .. ip)
    -- ngx.log(ngx.ERR, "iputils.ip_in_cidrs: " .. tostring(iputils.ip_in_cidrs(ip, internal_cidrs)))
    return iputils.ip_in_cidrs(ip, internal_cidrs)
end

-- Function to check if IP is from EU using MaxMind GeoIP
function _M.is_eu_ip(ip)
    local geoip = require("resty.maxminddb")
    local mmdb, err = geoip.open("/usr/local/openresty/geoip/GeoLite2-Country.mmdb")
    
    if not mmdb then
        ngx.log(ngx.ERR, "Failed to open GeoIP database: ", err)
        return false
    end
    
    local res, err = mmdb:lookup(ip)
    if not res then
        ngx.log(ngx.ERR, "IP lookup failed: ", err)
        return false
    end
    
    local country_code = res:get("country", "iso_code")
    if not country_code then
        return false
    end
    
    return eu_countries[country_code] or false
end

-- Main filter function
function _M.filter()
    local client_ip = _M.get_client_ip()
    
    -- Allow internal network IPs
    if _M.is_internal_ip(client_ip) then
        return true
    end
    
    -- Allow EU IPs
    if _M.is_eu_ip(client_ip) then
        return true
    end
    
    -- Reject all other IPs
    return false
end

return _M


6. Ficheros adicionales para el nginx.conf.login

1. form_body.html

                    
                        <div class="login-container">
                            <h2>Login</h2>
                            <form action="/initial" method="POST">
                                <div class="form-group">
                                    <label for="username">Username:</label>
                                    <input type="text" id="username" name="username" required>
                                </div>
                                <div class="form-group">
                                    <label for="password">Password:</label>
                                    <input type="password" id="password" name="password" required>
                                </div>
                                <input type="hidden" id="tab_id" name="tab_id" value="$TAB_ID$"> <!-- hidden -->
                                <button type="submit">Login</button>
                            </form>
                        </div>
       


2. form_style.css:

                            body { 
                                font-family: Arial, sans-serif; 
                                display: flex;
                                justify-content: center;
                                align-items: center;
                                height: 100vh;
                                margin: 0;
                                background-color: #f0f2f5;
                            }
                            .login-container {
                                background: white;
                                padding: 20px;
                                border-radius: 8px;
                                box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
                            }
                            .form-group {
                                margin-bottom: 15px;
                            }
                            input {
                                width: 100%;
                                padding: 8px;
                                margin: 5px 0;
                                border: 1px solid #ddd;
                                border-radius: 4px;
                                box-sizing: border-box;
                            }
                            button {
                                width: 100%;
                                padding: 10px;
                                background-color: #4CAF50;
                                color: white;
                                border: none;
                                border-radius: 4px;
                                cursor: pointer;
                            }
                            button:hover {
                                background-color: #45a049;
                            }

3. form_script.js :

                                // Save to session storage the id_tab sent by the server
                                window.onload = () => {
                                	tabId = document.getElementById("tab_id").value;
                                	sessionStorage.setItem("id_tab", tabId);
                                };

4. xmauth.lua:

-- auth.lua
------------------------------------------------
-- Funció per a veure si s'esta autenticat
------------------------------------------------
local _M = {}

-- Function to test if the user is authenticated.
-- Returns the username if authenticated, or nil if not.
function _M.is_authenticated( tab_id )
    -- HttpOnly cookies cannot be accessed by ngx.var.http_cookie
    -- HttpOnly cookies must be accessed by ngx.var.cookie_<cookie_name> 
    -- So the names of the cookies must be known beforehand
    -- This code is for not HttpOnly cookies
    --    local cookie_header = ngx.var.http_cookie 
    --    local cookies = {}
    --    local cookie_header = ngx.var.http_cookie  -- Get all cookies from the request
    --    if cookie_header then
    --        for key, value in string.gmatch(cookie_header, "([^=]+)=([^;]+)") do
    --            cookies[key] = value  -- Store key-value pairs in a table
    --            ngx.log(ngx.INFO, "Cookie: " .. key .. " = " .. value)
    --        end
    --    end
    
      
    -- Let's access individual cookies: session_id
    --local session_id = ngx.var.cookie_session_id or "1"
    -- ngx.log(ngx.INFO, "xmauth: session_id=" .. session_id )
    --local tab_id     = ngx.var.cookie_tab_id or "2"
    -- ngx.log(ngx.INFO, "xmauth: tab_id=" .. tab_id )
    --local username   = ngx.var.cookie_username or "3"
    -- ngx.log(ngx.INFO, "xmauth: username=" .. username )
    
    --local my_cookie_key = "session_id"  -- Example dynamic cookie name

    -- Get the full cookie string from request headers
    
    if not tab_id then
    	ngx.log(ngx.INFO, "xmauth: -1 param tab_id NOT found")
    else
        ngx.log(ngx.INFO, "xmauth: 0 param tab_id= '" .. tab_id )
    end
    	
    local cookie_string = ngx.var.http_cookie

    if not cookie_string then
        ngx.log(ngx.INFO, "xmauth: 1  No cookies found")
        return nil   
    else
        -- Build the pattern to match the dynamic cookie name
        local pattern = tab_id .. "=([^;]+)"
    
        -- Extract the cookie value
        local cookie_value = cookie_string:match(pattern)

        if not cookie_value then
            ngx.log(ngx.INFO, "xmauth: 2 Cookie '" .. tab_id .. "' not found")
            return nil
        end
    end

    local sessions = ngx.shared.sessions
    local user_data_json = sessions:get(tab_id) 
    
    if not user_data_json then
        ngx.log(ngx.INFO, "xmauth: 3 Cookie '" .. tab_id .. "' has no data")
    end
    
    local cjson = require "cjson"
    local success_user, user_data = pcall(cjson.decode, user_data_json)
    
    if not success_user then
        ngx.log(ngx.INFO, "xmauth: 4 Error getting data from Cookie '" .. tab_id )
        return nil
    else
        return user_data.username
    end    

end



function _M.random_string(length)
    math.randomseed(ngx.time() + ngx.worker.pid())  -- Seed RNG with time & worker ID
    local chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
    local str = {}
    
    for i = 1, length do
        local rand_index = math.random(1, #chars)
        str[i] = chars:sub(rand_index, rand_index)
    end

    return table.concat(str)
end

return _M


5. Copiar los certificados y rearrancar el servicio

Copiamos los certificados que tenemos

ca_all.cer, widlcard*

y rearrancamos el servicio

sudo systemctl restart openresty

y vamos a https://IP_SERVIDOR:8449/softprop/openresty y nos pide un certificado para acceder


No hay comentarios :

Publicar un comentario