diff --git a/.docker/build b/.docker/build index 167048e..bd15c29 100755 --- a/.docker/build +++ b/.docker/build @@ -3,17 +3,14 @@ set -x set -e -build_squid2radius(){ +build_squid2radius_dependency(){ apk add python3 py-pip ca-certificates openssl pip install pyrad==2.1 hurry.filesize==0.9 - cd /opt - wget -O- https://github.com/MirageTurtle/squid2radius/archive/e4443e444082f70789f4ac2f0b8e583d741c7bcf.tar.gz | tar -xzf - - mv squid2radius-* squid2radius } apk update apk add squid squid-lang-zh nghttp2 supervisor tzdata -build_squid2radius +build_squid2radius_dependency mkdir -p \ /var/log/supervisor \ /var/log/nghttpx \ diff --git a/.docker/rootfs/etc/periodic/hourly/squid2radius b/.docker/rootfs/etc/periodic/hourly/squid2radius index 84f6cdf..df4768b 100755 --- a/.docker/rootfs/etc/periodic/hourly/squid2radius +++ b/.docker/rootfs/etc/periodic/hourly/squid2radius @@ -1,7 +1,7 @@ #!/bin/bash cd /opt/squid2radius -python3 squid2radius-py3.py /var/log/squid/access.log {{RADIUS_SERVER}} {{RADIUS_SECRET}} +python3 squid2radius.py /var/log/squid/access.log {{RADIUS_SERVER}} {{RADIUS_SECRET}} cd /var/log/squid/ if [[ -f access.log.23 ]]; then seq 23 -1 0 | xargs -I {} cat access.log.{} > archive_access.log diff --git a/.docker/rootfs/opt/squid2radius/dictionary b/.docker/rootfs/opt/squid2radius/dictionary new file mode 100644 index 0000000..0c07bda --- /dev/null +++ b/.docker/rootfs/opt/squid2radius/dictionary @@ -0,0 +1,104 @@ +# -*- text -*- +# Copyright (C) 2011 The FreeRADIUS Server project and contributors +# +# Version $Id$ +# +# DO NOT EDIT THE FILES IN THIS DIRECTORY +# +# The files in this directory are maintained and updated by +# the FreeRADIUS project. Newer releases of software may update +# or change these files. +# +# Use the main dictionary file (usually /etc/raddb/dictionary) +# for local system attributes and $INCLUDEs. +# +# +# +# This file contains dictionary translations for parsing +# requests and generating responses. All transactions are +# composed of Attribute/Value Pairs. The value of each attribute +# is specified as one of 4 data types. Valid data types are: +# +# text - printable, generally UTF-8 encoded (subset of 'string') +# string - 0-253 octets +# ipaddr - 4 octets in network byte order +# integer - 32 bit value in big endian order (high byte first) +# date - 32 bit value in big endian order - seconds since +# 00:00:00 GMT, Jan. 1, 1970 +# ifid - 8 octets in network byte order +# ipv6addr - 16 octets in network byte order +# ipv6prefix - 18 octets in network byte order +# +# FreeRADIUS includes extended data types which are not defined +# in the RFC's. These data types are: +# +# abinary - Ascend's binary filter format. +# byte - 8 bit unsigned integer +# ether - 6 octets of hh:hh:hh:hh:hh:hh +# where 'h' is hex digits, upper or lowercase. +# short - 16-bit unsigned integer +# octets - raw octets, printed and input as hex strings. +# e.g.: 0x123456789abcdef +# tlv - type-length-value (only for certain WiMAX attributes) +# +# +# Enumerated values are stored in the user file with dictionary +# VALUE translations for easy administration. +# +# Example: +# +# ATTRIBUTE VALUE +# --------------- ----- +# Framed-Protocol = PPP +# 7 = 1 (integer encoding) +# + + +# +# Include the RFC dictionaries next. +# +# For a complete list of the standard attributes and values, +# see: +# http://www.iana.org/assignments/radius-types +# +$INCLUDE dictionary.rfc2865 +$INCLUDE dictionary.rfc2866 +$INCLUDE dictionary.rfc2869 +$INCLUDE dictionary.rfc3162 + + +# +# Miscellaneous attributes defined in weird places that +# don't really belong anywhere else... +# +ATTRIBUTE Originating-Line-Info 94 string + +# As defined in draft-sterman-aaa-sip-00.txt +ATTRIBUTE Digest-Response 206 string +ATTRIBUTE Digest-Attributes 207 octets # stupid format + +# +# Integer Translations +# +VALUE Service-Type Voice 12 +VALUE Service-Type Fax 13 +VALUE Service-Type Modem-Relay 14 +VALUE Service-Type IAPP-Register 15 +VALUE Service-Type IAPP-AP-Check 16 + +VALUE Framed-Protocol GPRS-PDP-Context 7 + +VALUE NAS-Port-Type Wireless-CDMA2000 22 +VALUE NAS-Port-Type Wireless-UMTS 23 +VALUE NAS-Port-Type Wireless-1X-EV 24 +VALUE NAS-Port-Type IAPP 25 + +VALUE NAS-Port-Type FTTP 26 +VALUE NAS-Port-Type Wireless-802.16 27 +VALUE NAS-Port-Type Wireless-802.20 28 +VALUE NAS-Port-Type Wireless-802.22 29 + +VALUE NAS-Port-Type xPON 35 +VALUE NAS-Port-Type Wireless-XGP 36 + +VALUE Framed-Protocol PPTP 9 diff --git a/.docker/rootfs/opt/squid2radius/dictionary.rfc2865 b/.docker/rootfs/opt/squid2radius/dictionary.rfc2865 new file mode 100644 index 0000000..475b382 --- /dev/null +++ b/.docker/rootfs/opt/squid2radius/dictionary.rfc2865 @@ -0,0 +1,138 @@ +# -*- text -*- +# Copyright (C) 2011 The FreeRADIUS Server project and contributors +# +# Attributes and values defined in RFC 2865. +# http://www.ietf.org/rfc/rfc2865.txt +# +# $Id$ +# +ATTRIBUTE User-Name 1 string +ATTRIBUTE User-Password 2 string encrypt=1 +ATTRIBUTE CHAP-Password 3 octets +ATTRIBUTE NAS-IP-Address 4 ipaddr +ATTRIBUTE NAS-Port 5 integer +ATTRIBUTE Service-Type 6 integer +ATTRIBUTE Framed-Protocol 7 integer +ATTRIBUTE Framed-IP-Address 8 ipaddr +ATTRIBUTE Framed-IP-Netmask 9 ipaddr +ATTRIBUTE Framed-Routing 10 integer +ATTRIBUTE Filter-Id 11 string +ATTRIBUTE Framed-MTU 12 integer +ATTRIBUTE Framed-Compression 13 integer +ATTRIBUTE Login-IP-Host 14 ipaddr +ATTRIBUTE Login-Service 15 integer +ATTRIBUTE Login-TCP-Port 16 integer +# Attribute 17 is undefined +ATTRIBUTE Reply-Message 18 string +ATTRIBUTE Callback-Number 19 string +ATTRIBUTE Callback-Id 20 string +# Attribute 21 is undefined +ATTRIBUTE Framed-Route 22 string +ATTRIBUTE Framed-IPX-Network 23 ipaddr +ATTRIBUTE State 24 octets +ATTRIBUTE Class 25 octets +ATTRIBUTE Vendor-Specific 26 octets +ATTRIBUTE Session-Timeout 27 integer +ATTRIBUTE Idle-Timeout 28 integer +ATTRIBUTE Termination-Action 29 integer +ATTRIBUTE Called-Station-Id 30 string +ATTRIBUTE Calling-Station-Id 31 string +ATTRIBUTE NAS-Identifier 32 string +ATTRIBUTE Proxy-State 33 octets +ATTRIBUTE Login-LAT-Service 34 string +ATTRIBUTE Login-LAT-Node 35 string +ATTRIBUTE Login-LAT-Group 36 octets +ATTRIBUTE Framed-AppleTalk-Link 37 integer +ATTRIBUTE Framed-AppleTalk-Network 38 integer +ATTRIBUTE Framed-AppleTalk-Zone 39 string + +ATTRIBUTE CHAP-Challenge 60 octets +ATTRIBUTE NAS-Port-Type 61 integer +ATTRIBUTE Port-Limit 62 integer +ATTRIBUTE Login-LAT-Port 63 string + +# +# Integer Translations +# + +# Service types + +VALUE Service-Type Login-User 1 +VALUE Service-Type Framed-User 2 +VALUE Service-Type Callback-Login-User 3 +VALUE Service-Type Callback-Framed-User 4 +VALUE Service-Type Outbound-User 5 +VALUE Service-Type Administrative-User 6 +VALUE Service-Type NAS-Prompt-User 7 +VALUE Service-Type Authenticate-Only 8 +VALUE Service-Type Callback-NAS-Prompt 9 +VALUE Service-Type Call-Check 10 +VALUE Service-Type Callback-Administrative 11 + +# Framed Protocols + +VALUE Framed-Protocol PPP 1 +VALUE Framed-Protocol SLIP 2 +VALUE Framed-Protocol ARAP 3 +VALUE Framed-Protocol Gandalf-SLML 4 +VALUE Framed-Protocol Xylogics-IPX-SLIP 5 +VALUE Framed-Protocol X.75-Synchronous 6 + +# Framed Routing Values + +VALUE Framed-Routing None 0 +VALUE Framed-Routing Broadcast 1 +VALUE Framed-Routing Listen 2 +VALUE Framed-Routing Broadcast-Listen 3 + +# Framed Compression Types + +VALUE Framed-Compression None 0 +VALUE Framed-Compression Van-Jacobson-TCP-IP 1 +VALUE Framed-Compression IPX-Header-Compression 2 +VALUE Framed-Compression Stac-LZS 3 + +# Login Services + +VALUE Login-Service Telnet 0 +VALUE Login-Service Rlogin 1 +VALUE Login-Service TCP-Clear 2 +VALUE Login-Service PortMaster 3 +VALUE Login-Service LAT 4 +VALUE Login-Service X25-PAD 5 +VALUE Login-Service X25-T3POS 6 +VALUE Login-Service TCP-Clear-Quiet 8 + +# Login-TCP-Port (see /etc/services for more examples) + +VALUE Login-TCP-Port Telnet 23 +VALUE Login-TCP-Port Rlogin 513 +VALUE Login-TCP-Port Rsh 514 + +# Termination Options + +VALUE Termination-Action Default 0 +VALUE Termination-Action RADIUS-Request 1 + +# NAS Port Types + +VALUE NAS-Port-Type Async 0 +VALUE NAS-Port-Type Sync 1 +VALUE NAS-Port-Type ISDN 2 +VALUE NAS-Port-Type ISDN-V120 3 +VALUE NAS-Port-Type ISDN-V110 4 +VALUE NAS-Port-Type Virtual 5 +VALUE NAS-Port-Type PIAFS 6 +VALUE NAS-Port-Type HDLC-Clear-Channel 7 +VALUE NAS-Port-Type X.25 8 +VALUE NAS-Port-Type X.75 9 +VALUE NAS-Port-Type G.3-Fax 10 +VALUE NAS-Port-Type SDSL 11 +VALUE NAS-Port-Type ADSL-CAP 12 +VALUE NAS-Port-Type ADSL-DMT 13 +VALUE NAS-Port-Type IDSL 14 +VALUE NAS-Port-Type Ethernet 15 +VALUE NAS-Port-Type xDSL 16 +VALUE NAS-Port-Type Cable 17 +VALUE NAS-Port-Type Wireless-Other 18 +VALUE NAS-Port-Type Wireless-802.11 19 diff --git a/.docker/rootfs/opt/squid2radius/dictionary.rfc2866 b/.docker/rootfs/opt/squid2radius/dictionary.rfc2866 new file mode 100644 index 0000000..05c4156 --- /dev/null +++ b/.docker/rootfs/opt/squid2radius/dictionary.rfc2866 @@ -0,0 +1,58 @@ +# -*- text -*- +# Copyright (C) 2011 The FreeRADIUS Server project and contributors +# +# Attributes and values defined in RFC 2866. +# http://www.ietf.org/rfc/rfc2866.txt +# +# $Id$ +# +ATTRIBUTE Acct-Status-Type 40 integer +ATTRIBUTE Acct-Delay-Time 41 integer +ATTRIBUTE Acct-Input-Octets 42 integer +ATTRIBUTE Acct-Output-Octets 43 integer +ATTRIBUTE Acct-Session-Id 44 string +ATTRIBUTE Acct-Authentic 45 integer +ATTRIBUTE Acct-Session-Time 46 integer +ATTRIBUTE Acct-Input-Packets 47 integer +ATTRIBUTE Acct-Output-Packets 48 integer +ATTRIBUTE Acct-Terminate-Cause 49 integer +ATTRIBUTE Acct-Multi-Session-Id 50 string +ATTRIBUTE Acct-Link-Count 51 integer + +# Accounting Status Types + +VALUE Acct-Status-Type Start 1 +VALUE Acct-Status-Type Stop 2 +VALUE Acct-Status-Type Alive 3 # dup +VALUE Acct-Status-Type Interim-Update 3 +VALUE Acct-Status-Type Accounting-On 7 +VALUE Acct-Status-Type Accounting-Off 8 +VALUE Acct-Status-Type Failed 15 + +# Authentication Types + +VALUE Acct-Authentic RADIUS 1 +VALUE Acct-Authentic Local 2 +VALUE Acct-Authentic Remote 3 +VALUE Acct-Authentic Diameter 4 + +# Acct Terminate Causes + +VALUE Acct-Terminate-Cause User-Request 1 +VALUE Acct-Terminate-Cause Lost-Carrier 2 +VALUE Acct-Terminate-Cause Lost-Service 3 +VALUE Acct-Terminate-Cause Idle-Timeout 4 +VALUE Acct-Terminate-Cause Session-Timeout 5 +VALUE Acct-Terminate-Cause Admin-Reset 6 +VALUE Acct-Terminate-Cause Admin-Reboot 7 +VALUE Acct-Terminate-Cause Port-Error 8 +VALUE Acct-Terminate-Cause NAS-Error 9 +VALUE Acct-Terminate-Cause NAS-Request 10 +VALUE Acct-Terminate-Cause NAS-Reboot 11 +VALUE Acct-Terminate-Cause Port-Unneeded 12 +VALUE Acct-Terminate-Cause Port-Preempted 13 +VALUE Acct-Terminate-Cause Port-Suspended 14 +VALUE Acct-Terminate-Cause Service-Unavailable 15 +VALUE Acct-Terminate-Cause Callback 16 +VALUE Acct-Terminate-Cause User-Error 17 +VALUE Acct-Terminate-Cause Host-Request 18 diff --git a/.docker/rootfs/opt/squid2radius/dictionary.rfc2869 b/.docker/rootfs/opt/squid2radius/dictionary.rfc2869 new file mode 100644 index 0000000..e4fc792 --- /dev/null +++ b/.docker/rootfs/opt/squid2radius/dictionary.rfc2869 @@ -0,0 +1,40 @@ +# -*- text -*- +# Copyright (C) 2011 The FreeRADIUS Server project and contributors +# +# Attributes and values defined in RFC 2869. +# http://www.ietf.org/rfc/rfc2869.txt +# +# $Id$ +# +ATTRIBUTE Acct-Input-Gigawords 52 integer +ATTRIBUTE Acct-Output-Gigawords 53 integer + +ATTRIBUTE Event-Timestamp 55 date + +ATTRIBUTE ARAP-Password 70 octets # 16 octets of data +ATTRIBUTE ARAP-Features 71 octets # 14 octets of data +ATTRIBUTE ARAP-Zone-Access 72 integer +ATTRIBUTE ARAP-Security 73 integer +ATTRIBUTE ARAP-Security-Data 74 string +ATTRIBUTE Password-Retry 75 integer +ATTRIBUTE Prompt 76 integer +ATTRIBUTE Connect-Info 77 string +ATTRIBUTE Configuration-Token 78 string +ATTRIBUTE EAP-Message 79 octets +ATTRIBUTE Message-Authenticator 80 octets + +ATTRIBUTE ARAP-Challenge-Response 84 octets # 8 octets of data +ATTRIBUTE Acct-Interim-Interval 85 integer +# 86: RFC 2867 +ATTRIBUTE NAS-Port-Id 87 string +ATTRIBUTE Framed-Pool 88 string + +# ARAP Zone Access + +VALUE ARAP-Zone-Access Default-Zone 1 +VALUE ARAP-Zone-Access Zone-Filter-Inclusive 2 +VALUE ARAP-Zone-Access Zone-Filter-Exclusive 4 + +# Prompt +VALUE Prompt No-Echo 0 +VALUE Prompt Echo 1 diff --git a/.docker/rootfs/opt/squid2radius/dictionary.rfc3162 b/.docker/rootfs/opt/squid2radius/dictionary.rfc3162 new file mode 100644 index 0000000..9994a56 --- /dev/null +++ b/.docker/rootfs/opt/squid2radius/dictionary.rfc3162 @@ -0,0 +1,14 @@ +# -*- text -*- +# Copyright (C) 2011 The FreeRADIUS Server project and contributors +# +# Attributes and values defined in RFC 3162. +# http://www.ietf.org/rfc/rfc3162.txt +# +# $Id$ +# +ATTRIBUTE NAS-IPv6-Address 95 ipv6addr +ATTRIBUTE Framed-Interface-Id 96 ifid +ATTRIBUTE Framed-IPv6-Prefix 97 ipv6prefix +ATTRIBUTE Login-IPv6-Host 98 ipv6addr +ATTRIBUTE Framed-IPv6-Route 99 string +ATTRIBUTE Framed-IPv6-Pool 100 string diff --git a/.docker/rootfs/opt/squid2radius/squid2radius.py b/.docker/rootfs/opt/squid2radius/squid2radius.py new file mode 100644 index 0000000..1c51918 --- /dev/null +++ b/.docker/rootfs/opt/squid2radius/squid2radius.py @@ -0,0 +1,125 @@ +#!/usr/bin/env python + +import sys +import argparse +import time +import re +from subprocess import call +import pyrad.packet +from pyrad.client import Client +from pyrad.dictionary import Dictionary + + +parser = argparse.ArgumentParser(description='Analyze squid log by user ' \ + 'and upload result to RADIUS ' \ + 'server.') +parser.add_argument('logfile_path', help='logfile to analyze') +parser.add_argument('radius_server') +parser.add_argument('radius_secret') +parser.add_argument('-p', '--radius-acct-port', default='1813') +parser.add_argument('--radius-nasid', default='squid') +parser.add_argument('--squid-path', default='/usr/sbin/squid') +parser.add_argument('--exclude-pattern', help='do not send to server if ' \ + 'username contains this regexp', + default='') +parser.add_argument('--no-rotation', help='do not rotate squid log files', + action='store_true') +args = parser.parse_args() + + +logfile = open(args.logfile_path) +print(logfile) + +sys.stdout.write("Analyzing.") +sum_bytes = {} +for i, line in enumerate(logfile): + if i % 10000 == 0: sys.stdout.write('.'); sys.stdout.flush() + + # http://wiki.squid-cache.org/Features/LogFormat + try: + _, _, _, code_status, num_bytes, _, _, rfc931, _, _ = line.split()[:10] + except ValueError: + continue + + # unauthorized user + if rfc931 == '-': continue + + # wrong username and/or password + try: + if code_status.split('/')[1] == '407': continue + except IndexError: + continue + + try: + sum_bytes[rfc931] = sum_bytes[rfc931] + int(num_bytes) + except KeyError: + sum_bytes[rfc931] = int(num_bytes) + + +print("\nSending...") +srv = Client(server=args.radius_server, secret=args.radius_secret.encode(), + dict=Dictionary(sys.path[0] + "/dictionary")) + +if args.exclude_pattern: + print("Exclusion check has been enabled.") + exclude_pattern = re.compile(args.exclude_pattern) + +failed_usernames = [] +for username, total_bytes in sum_bytes.items(): + sys.stdout.write(username + ' ' + str(total_bytes)) + sys.stdout.write('.') + sys.stdout.flush() + + if args.exclude_pattern and exclude_pattern.search(username): + sys.stdout.write("..skipped!\n") + sys.stdout.flush() + continue + + session_id = str(time.time()) + + try: + req = srv.CreateAcctPacket() + req['User-Name'] = username + req['NAS-Identifier'] = args.radius_nasid + req['Acct-Session-Id'] = session_id + req['Acct-Status-Type'] = 1 # Start + + reply = srv.SendPacket(req) + if not reply.code == pyrad.packet.AccountingResponse: + raise Exception("Unexpected response from RADIUS server") + + sys.stdout.write('.') + sys.stdout.flush() + + req = srv.CreateAcctPacket() + req['User-Name'] = username + req['NAS-Identifier'] = args.radius_nasid + req['Acct-Session-Id'] = session_id + req['Acct-Status-Type'] = 2 # Stop + req['Acct-Output-Octets'] = total_bytes + + reply = srv.SendPacket(req) + if not reply.code == pyrad.packet.AccountingResponse: + raise Exception("Unexpected response from RADIUS server") + + except Exception as e: + failed_usernames.append((username, e)) + sys.stdout.write("..FAILED!\n") + sys.stdout.flush() + continue + + sys.stdout.write(".\n") + sys.stdout.flush() + + +if not args.no_rotation: + print("\nRotating squid log...") + call([args.squid_path, "-k", "rotate"]) + + +if failed_usernames: + raise Exception("Unable to send stats for the following user(s):\n " + + "\n ".join(fu[0] + + ' (' + fu[1].__class__.__name__ + ': ' + + str(fu[1]) + ')' + for fu in failed_usernames))