Первый вариант. Внешний ACL-helper для идентификации пользователей прозрачного squid...
authorRoman Bazalevsky <rvb@rvb.name>
Thu, 26 Sep 2019 18:45:36 +0000 (21:45 +0300)
committerRoman Bazalevsky <rvb@rvb.name>
Thu, 26 Sep 2019 18:45:36 +0000 (21:45 +0300)
src/arpcache.lua [new file with mode: 0644]
src/config.json [new file with mode: 0644]
src/config.lua [new file with mode: 0644]
src/connection.lua [new file with mode: 0644]
src/helper.lua [new file with mode: 0755]
src/ident.lua [new file with mode: 0644]

diff --git a/src/arpcache.lua b/src/arpcache.lua
new file mode 100644 (file)
index 0000000..1ae8136
--- /dev/null
@@ -0,0 +1,51 @@
+-- module to access linux ARP cache
+
+config = require "config"
+
+_arpcache = {}
+_ARPCACHE4 = '/sbin/ip -4 n'
+_ARPCACHE6 = '/sbin/ip -6 n'
+
+function _match_v4(ip)
+  return string.match(ip,"%d*%.%d*%.%d*%.%d*")
+end
+
+function _arpcache.get_mac(ip)
+
+  local cmd
+  if _match_v4(ip) then
+    cmd = _ARPCACHE4
+  else
+    cmd = _ARPCACHE6
+  end
+
+  local f = io.popen(cmd)
+  local res = nil
+  local line, w
+  
+  while true do
+
+    line = f:read()
+    if not line then 
+      break
+    end
+
+    w = {}
+
+    for k in string.gmatch(line, "(%S+)") do
+      table.insert(w, k)
+    end
+
+    if w[1]==ip then
+      res = w[5]
+      break
+    end
+
+  end 
+
+  f:close()
+  return res 
+
+end
+
+return _arpcache
diff --git a/src/config.json b/src/config.json
new file mode 100644 (file)
index 0000000..72f54e8
--- /dev/null
@@ -0,0 +1,39 @@
+{
+
+"ident": {
+  "timeout": 2,
+  "default": ""
+},
+
+"portmap" : { 
+
+  "3128" : "80",
+  "3130" : "80",
+  "3131" : "443",
+  "3132" : "443"
+
+},
+
+"hosts" : {
+  "ip": {
+
+    "192.168.1.100": "*ident",
+    "2001:470:6f:9d5::100": "*ident",
+
+    "192.168.1.4":   "net"
+
+    },
+
+  "mac": {
+
+    "00:1f:c6:86:f7:9c": "*ident",
+
+    "90:a2:da:f8:0b:73": "media",
+
+    "ea:cf:b0:f9:24:43": "camera"
+
+    }
+
+  }
+  
+}
diff --git a/src/config.lua b/src/config.lua
new file mode 100644 (file)
index 0000000..6c427cb
--- /dev/null
@@ -0,0 +1,64 @@
+json = require "json"
+
+_config = {}
+
+function _config.read(file)
+
+  local file = file or "/opt/squid-auth-helper/config.json"
+  local f = assert(io.open(file))
+  local cfg = json.decode(f:read("*all"))
+
+  _config.pmap = cfg["portmap"] or {}  
+
+  _config.ident_timeout = 2
+  _config.ident_default = nil
+
+  if cfg["ident"] then
+    _config.ident_timeout = cfg["ident"]["timeout"] or _config.ident_timeout
+    _config.ident_default = cfg["ident"]["default"] or _config.ident_default
+  end
+
+  _config.ipmap = {}
+  _config.macmap = {}
+  
+  if cfg["hosts"] then
+    if cfg["hosts"]["ip"] then
+      for k, v in pairs(cfg["hosts"]["ip"]) do
+        local k, n = string.gsub(k, "%.", "%%.")
+        local k, n = string.gsub(k,"%*",".*")
+        _config.ipmap["^"..k.."$"] = v 
+      end
+    end
+    if cfg["hosts"]["mac"] then
+      for k, v in pairs(cfg["hosts"]["mac"]) do
+        local k, n = string.gsub(k,"%*",".*")
+        _config.macmap["^"..k.."$"] = v 
+      end
+    end 
+  end
+  
+end
+
+function _config.map_port(port)
+  return _config.pmap[tostring(port)] or port
+end
+
+function _config.map_ip(ip)
+  for k,v in pairs(_config.ipmap) do
+    if string.match(ip,k) then
+      return v
+    end  
+  end
+  return nil
+end
+
+function _config.map_mac(mac)
+  for k,v in pairs(_config.macmap) do
+    if string.match(mac,k) then
+      return v
+    end  
+  end
+  return nil
+end
+
+return _config
diff --git a/src/connection.lua b/src/connection.lua
new file mode 100644 (file)
index 0000000..e1eff46
--- /dev/null
@@ -0,0 +1,26 @@
+_connection = {}
+
+config   = require "config"
+ident    = require "ident" 
+arpcache = require "arpcache"
+
+function _connection.auth(serv, localport, remoteport)
+
+  local user = config.map_ip(serv)
+  
+  if not user then
+    local mac = arpcache.get_mac(serv)
+    if mac then
+      user = config.map_mac(mac)
+    end  
+  end
+    
+  if user == "*ident" then
+    user = ident.resolve(serv,localport,config.map_port(remoteport))
+  end  
+
+  return user
+
+end
+
+return _connection
diff --git a/src/helper.lua b/src/helper.lua
new file mode 100755 (executable)
index 0000000..79711e2
--- /dev/null
@@ -0,0 +1,42 @@
+#!/usr/bin/lua
+
+config     = require "config"
+connection = require "connection"
+
+io.stdout:setvbuf 'no'
+io.stdin:setvbuf 'no'
+
+if arg then
+  cf = arg[1]
+else 
+  cf = nil
+end    
+
+config.read(cf)
+
+function string:split(sep)
+   local sep, fields = sep or ":", {}
+   local pattern = string.format("([^%s]+)", sep)
+   self:gsub(pattern, function(c) fields[#fields+1] = c end)
+   return fields
+end
+
+function main()
+  while true do
+    str=io.read()
+    vals = str:split(" ")
+    status, res = pcall(connection.auth,vals[1],vals[2],vals[3])
+    if status then
+      if res and res ~= "" then
+        io.write("OK user="..res.."\n")
+      else 
+        io.write("OK\n")
+      end
+    else
+      io.write("BH\n")
+    end    
+  end
+end
+
+main()
+
diff --git a/src/ident.lua b/src/ident.lua
new file mode 100644 (file)
index 0000000..1567fc0
--- /dev/null
@@ -0,0 +1,35 @@
+config = require("config")
+
+local socket = require("socket")
+
+_ident = {}
+
+_IDENTD_PORT = 113
+
+function _ident.resolve(serv, port, remote)
+
+    local tcp = assert(socket.tcp())
+    tcp:settimeout(config.ident_timeout, 't')
+    tcp:connect(serv,_IDENTD_PORT)
+    tcp:send(tostring(port) .. ", "..tostring(remote).."\n")
+       
+    res = ""
+    while true do
+        local s, status, partial = tcp:receive()
+        res = res..(s or partial)
+        if status == "closed" then break end
+    end
+
+    tcp:close()
+    
+    local uname = config.ident_default
+
+    for user in string.gmatch(res,"%d*,%d*:USERID:UNIX:(.*)") do
+      uname = user
+    end
+    
+    return uname
+       
+end
+
+return _ident