The diagram below shows the architecture of our Python Security Toolkit. The toolkit connects to target IP hosts using TCP sockets to determine which ports are open (port scanner module), and also reads authentication log files using regex patterns to flag suspicious IPs with repeated login failures (log analyzer module). Both modules are controlled through a single CLI menu interface.
First, you need to create a dedicated folder for this project and an empty Python file inside it.
security-scripts inside your home folder. The -p flag means "create parent directories if needed" and prevents errors if the directory already exists.| Part | What It Does |
|---|---|
mkdir | Make Directory — creates a new folder |
-p | Parents — creates parent directories as needed and doesn't error if the directory already exists |
~/security-scripts | ~ is a shortcut for your home directory (e.g., /home/kali). So this creates /home/kali/security-scripts |
security-scripts folder so all subsequent commands and file operations happen inside this project folder.toolkit.py:touch command creates a new empty file. If the file already exists, it updates its modification timestamp without changing its contents.nano text editor and creates a new file called auth.log. You will paste sample log data into this file.Jun 15 10:22:31 server sshd[1234]: Failed password for root from 192.168.1.50 port 22 ssh2 Jun 15 10:22:35 server sshd[1235]: Failed password for admin from 10.0.0.99 port 22 ssh2 Jun 15 10:23:01 server sshd[1236]: Failed password for root from 192.168.1.50 port 22 ssh2 Jun 15 10:23:15 server sshd[1237]: Accepted password for user1 from 172.16.0.10 port 22 ssh2 Jun 15 10:24:02 server sshd[1238]: Failed password for root from 192.168.1.50 port 22 ssh2 Jun 15 10:24:30 server sshd[1239]: Failed password for admin from 10.0.0.99 port 22 ssh2 Jun 15 10:25:11 server sshd[1240]: Failed password for root from 192.168.1.50 port 22 ssh2 Jun 15 10:26:05 server sshd[1241]: Failed password for admin from 10.0.0.99 port 22 ssh2 Jun 15 10:26:45 server sshd[1242]: Failed password for admin from 10.0.0.99 port 22 ssh2
ls, you should see both toolkit.py and auth.log listed.The port scanner connects to a target IP address on specified ports using TCP sockets. If the connection succeeds, the port is OPEN (a service is listening). If it fails or times out, the port is CLOSED.
Now let's understand each line of the port scanner before typing it:
# symbol tells Python to ignore everything after it on this line. This helps other developers (and your future self) understand what the file does.argparse module, which provides tools for parsing command-line arguments. This lets users run the script with flags like --scan and --ports instead of hardcoding values. Think of it as creating a menu system for your terminal command.socket module, which provides low-level networking operations. A "socket" is like a virtual plug that connects two computers over a network. We use it to attempt TCP connections to target ports — if the connection succeeds, the port is open.re (Regular Expressions) module. Regular expressions are powerful text-matching patterns. We use them to search through log files and find lines matching specific patterns (like "Failed password from [IP address]").Counter class from Python's collections module. Counter is a special dictionary that automatically counts how many times each item appears in a list. We use it to count how many failed login attempts each IP address has.scan_ports that accepts two parameters: target_ip (the IP address to scan, like "192.168.56.20") and ports (a list of port numbers to check, like [21, 22, 80, 445]).f"..." is an f-string that inserts the value of target_ip into the message. The [*] prefix is a security tool convention meaning "informational".ports list. For example, if ports is [21, 22, 80, 445], this loop runs 4 times — once for each port.socket.AF_INET means we're using IPv4 addresses (like 192.168.x.x). socket.SOCK_STREAM means we're using TCP protocol (reliable, connection-oriented). Together, this creates a TCP/IPv4 socket — the same type your browser uses to connect to websites.connect_ex() is different from connect() — instead of throwing an error on failure, it returns a numeric error code. 0 means success (port is open), any other number means failure (port is closed or filtered). The target is passed as a tuple (ip, port).0 means the TCP three-way handshake completed successfully — the port is open and a service is listening on it.[+] prefix means "positive finding". This output tells you a service is actively listening on that port number.parse_logs function with two parameters: log_path (the file path to the log file, like "auth.log") and threshold (the minimum number of failures before triggering an alert, like 3).(\d+\.\d+\.\d+\.\d+) part captures the IP address: \d+ matches one or more digits, \. matches a literal dot. The parentheses () create a "capture group" so we can extract just the IP address from each matching line."r"). The with keyword ensures the file is automatically closed when we're done, even if an error occurs. The file object is assigned to variable f.match will be a match object containing the captured IP. If the line doesn't match (like a successful login), match will be None.None is "falsy" and match objects are "truthy", so this only executes if the line contained a failed login attempt.match.group(1) extracts the text captured by the first set of parentheses in our regex pattern — which is the IP address. .append() adds this IP to the end of our failed_ips list.counts["192.168.1.50"] will be 4..items() returns pairs of (key, value), so ip gets the address and count gets the number of failures.[ALERT] prefix signals a critical finding that requires attention.__name__ is a special Python variable that equals "__main__" when you run the file with python toolkit.py.description parameter sets the help text shown when users run python toolkit.py --help.--scan that accepts a target IP address. When the user runs python toolkit.py --scan 192.168.56.20, the value "192.168.56.20" is stored in args.scan.--ports flag for specifying which ports to scan, as a comma-separated list (e.g., "21,22,80,445").--analyze flag for specifying the log file path to parse for failed logins.--threshold flag with type=int (converts input to an integer) and default=3 (uses 3 if the user doesn't specify a value).args object. After this, you can access values like args.scan, args.ports, args.analyze, and args.threshold.--scan and --ports flags. Both are required for port scanning — you need a target IP AND a list of ports to check..split(",") splits the string at each comma, and int(p) converts each piece from a string to a number. This is a "list comprehension" — Python's compact way of creating lists.scan_ports function with the target IP and parsed port list.--analyze, switch to log analysis mode instead.parse_logs function with the log file path and the failure threshold value.toolkit.py file.
nano toolkit.py# toolkit.py - Python Security Automation Toolkit import argparse import socket import re from collections import Counter def scan_ports(target_ip, ports): print(f"[*] Scanning target {target_ip}...") for port in ports: s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.settimeout(1.0) result = s.connect_ex((target_ip, port)) if result == 0: print(f"[+] Port {port}: OPEN") s.close() def parse_logs(log_path, threshold): print(f"[*] Parsing log file: {log_path}...") fail_pattern = re.compile(r"Failed password for.*from (\d+\.\d+\.\d+\.\d+)") failed_ips = [] with open(log_path, "r") as f: for line in f: match = fail_pattern.search(line) if match: failed_ips.append(match.group(1)) counts = Counter(failed_ips) for ip, count in counts.items(): if count >= threshold: print(f"[ALERT] IP {ip} reached {count} login failures!") if __name__ == "__main__": parser = argparse.ArgumentParser(description="Security Toolkit") parser.add_argument("--scan", help="Target IP to scan") parser.add_argument("--ports", help="Comma-separated ports") parser.add_argument("--analyze", help="Path to log file") parser.add_argument("--threshold", type=int, default=3, help="Failure threshold") args = parser.parse_args() if args.scan and args.ports: port_list = [int(p) for p in args.ports.split(",")] scan_ports(args.scan, port_list) elif args.analyze: parse_logs(args.analyze, args.threshold) else: parser.print_help()
[*] Scanning target 127.0.0.1...[+] Port 22: OPEN (if SSH is running)| Flag | What It Does |
|---|---|
--analyze auth.log | Specifies the log file to parse |
--threshold 3 | Only alert if an IP has 3+ failed attempts |
[*] Parsing log file: auth.log...[ALERT] IP 192.168.1.50 reached 4 login failures![ALERT] IP 10.0.0.99 reached 4 login failures!The diagram below shows the automated execution pipeline: from accepting IP and port inputs via CLI, to opening socket connections, parsing log files with regex, triggering threshold-based alerts, and exporting text reports.
~/security-scripts/toolkit.py — Complete Python security toolkit with port scanner and log analyzer~/security-scripts/auth.log — Sample authentication log file for testing