1. Introducción
Los pasos a seguir son:
- Arreglar el módulo python (mnu_main.py) que ejecuta el servidor web (uvicorn) para que vaya en https al puerto 5001
- Arreglar el módulo python autenticación LDAP (xmopenresty.py) para que se ejecute sobre https en el puerto 5000
- En nginx.conf, cambiar el puerto al 5001 del servidor cuando se define la batería de servidores en "upstream softpropsrv"
- Cambiar en nginx.conf y todos sus "includes" (nginx.conf.softprop) aquellas sentencias proxy_pass http://softpropsrv por proxy_pass https://softpropsrv
- Cambiar en nginx.conf.initial la sentencia
- local res, err = httpc:request_uri("http://127.0.0.1:5000/auth", .... por
- local res, err = httpc:request_uri("https://127.0.0.1:5000/auth", ...
- Añadir el módulo lua xmauth.lua
2. mnu_main.py
Veamos el nuevo código del programa principal donde se ha cambiado fh.serve() por el código marcado en amarillo, donde se le indica el host, puerto y la ruta de los ficheros de la clave y el certificado de servidor
#!/home/eduard/MyPython/11.softprop-01/venv_softprop/bin/python3 #1. Imports #------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path from sqlalchemy import Table path_root1 = Path(__file__).parents[1] # Damos un salto de directorio hacia arriba-> ximoutilsmod sys.path.append(str(path_root1)) path_root0 = Path(__file__).parents[0] # El mismo directorio sys.path.append(str(path_root0)) from menus.mnu_fh import fh, app #OJO: No eliminar las dependencias marcadas en gris (routes01mnu, routes02form, routes03grid,app) pues, sinó falla el programa from menus import routes00comp, routes01mnu, routes02form, routes03grid # No eliminar !!! from menus.mnu_fh import fh, app # No eliminar app !!! from basicutils import xmdb from session.xmsessionmodels import SessionStoreAbst, SessionStore, SessionStoreHst # No eliminar !!! # ------Fin imprescindible from basicutils import xmdb # Create database tables xmdb.Base.metadata.create_all(bind=xmdb.engine) # Execute the web server, but now not using fh.serve but uvicorm.run instead #fh.serve() if __name__ == "__main__": import uvicorn uvicorn.run(app, host="0.0.0.0", port=5001, ssl_keyfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.rsa", ssl_certfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.crt")
Y si vamos a la consola y ejecutamos ./menus/mnu_main.py podemos ejecutar el programa gracias al shebang de la primera línea y vemos que se esta ejecutando sobre https
3. xmoprenresty.py
Veamos el modulo python de autenticación LDAP donde ya habíamos utilizado uvicorn directamente para utilizar el puerto 5000 y ahora le añadimos el protocolo https
#!/home/eduard/MyPython/11.softprop-01/venv_softprop/bin/python3 # auth_service.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel import uvicorn import os import sys import base64 #------Imprescindible para poder importar de otras carpetas (de basicutils) import sys from pathlib import Path path_root1 = Path(__file__).parents[1] # Damos un salto de directorio hacia arriba-> ximoutilsmod sys.path.append(str(path_root1)) path_root0 = Path(__file__).parents[0] # El mismo directorio sys.path.append(str(path_root0)) from basicutils import xmldap # ------Fin imprescindible app = FastAPI() class AuthRequest(BaseModel): username: str password: str class AuthResponse(BaseModel): authenticated: bool def authenticate(username: str, password: str) -> bool: """ Your existing authentication function. Replace this implementation with your actual authentication logic. """ # Example implementation #if username == "admin" and password == "secret": # return True #return False return xmldap.autheticate_by_login(username, password) def is_valid_user(username: str) -> bool: """ Your existing authentication function. Replace this implementation with your actual authentication logic. """ return xmldap.exists_user(username) @app.post("/auth", response_model=AuthResponse) async def auth(request: AuthRequest): is_valid = authenticate(request.username, request.password) return AuthResponse(authenticated=is_valid) @app.post("/auth_username", response_model=AuthResponse) async def auth_username(request: AuthRequest): is_valid = is_valid_user(request.username) return AuthResponse(authenticated=is_valid) if __name__ == "__main__": #uvicorn.run(app, host="127.0.0.1", port=5000) uvicorn.run(app, host="0.0.0.0", port=5000, ssl_keyfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.rsa", ssl_certfile="/home/eduard/MyPython/11.softprop-01/static/certs/wildcard2023Nginx.crt")
Y si vamos a la consola y ejecutamos dentro su carpeta ./xmopenresty.py obtenemos
Pues el shebang de la primer línea permite obtener el entorno virtual de python y además lo más importante es que se está ejecutando bajo el protocolo https
4. Cambios en nginx.conf y nginx.conf.softprop de la carpeta /usr/local/openresty/nginx/conf/
Como no hemos cambiado la dirección del servidor ni el puerto (aunque si el protocolo http por https), el fichero nginx.conf queda igual que estaba, y solo cambia nginx.conf.softprop que se ha cambiado en proxy_pass el http por https
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 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/ /;
5. Cambios en nginx.conf.initial para la utenticación LDAP
Se cambia la sentencia
local res, err = http:request_uri("http://127.0.0.1:5000/auth", ....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"] ngx.log(ngx.INFO, "initial username " .. username ) ngx.log(ngx.INFO, "initial password " .. password ) ngx.log(ngx.INFO, "initial tab_id " .. tab_id ) 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("https://127.0.0.1:5000/auth", { method = "POST", body = cjson.encode({ username = username, password = password }), headers = { ["Content-Type"] = "application/json", } }) 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) return ngx.redirect(my_url) else ngx.redirect("/error") end }
6. El módulo 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
No hay comentarios :
Publicar un comentario