Update check_gitea.py
This commit is contained in:
+190
-86
@@ -1,147 +1,251 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""
|
|
||||||
Gitea Repository Check Script
|
|
||||||
|
|
||||||
This script verifies access to a Gitea repository by:
|
|
||||||
1. Authenticating with provided credentials
|
|
||||||
2. Checking repository availability
|
|
||||||
3. Verifying file access
|
|
||||||
|
|
||||||
Example usage:
|
|
||||||
./check_gitea.py --server gitea.example.com --repo-owner owner --repo-name repo --username user --password pass --file path/to/file.txt
|
|
||||||
"""
|
|
||||||
|
|
||||||
import requests
|
|
||||||
import argparse
|
import argparse
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
|
import requests
|
||||||
from urllib.parse import quote
|
from urllib.parse import quote
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
|
||||||
|
|
||||||
def debug_print(debug, message):
|
def debug_print(debug, message):
|
||||||
if debug:
|
if debug:
|
||||||
print(f"DEBUG: {message}")
|
print(f"DEBUG: {message}")
|
||||||
|
|
||||||
def check_gitea(server_address, repo_owner, repo_name, username, password, file_path, debug=False):
|
|
||||||
|
def extract_csrf_token(session, html, debug=False):
|
||||||
|
soup = BeautifulSoup(html, "html.parser")
|
||||||
|
|
||||||
|
csrf_input = soup.find("input", {"name": "_csrf"})
|
||||||
|
if csrf_input and csrf_input.get("value"):
|
||||||
|
debug_print(debug, "CSRF found in input name=_csrf")
|
||||||
|
return csrf_input["value"]
|
||||||
|
|
||||||
|
csrf_meta = soup.find("meta", {"name": "_csrf"})
|
||||||
|
if csrf_meta and csrf_meta.get("content"):
|
||||||
|
debug_print(debug, "CSRF found in meta name=_csrf")
|
||||||
|
return csrf_meta["content"]
|
||||||
|
|
||||||
|
csrf_meta_alt = soup.find("meta", {"name": "csrf-token"})
|
||||||
|
if csrf_meta_alt and csrf_meta_alt.get("content"):
|
||||||
|
debug_print(debug, "CSRF found in meta name=csrf-token")
|
||||||
|
return csrf_meta_alt["content"]
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
r'csrfToken\s*[:=]\s*["\']([^"\']+)["\']',
|
||||||
|
r'_csrf\s*[:=]\s*["\']([^"\']+)["\']',
|
||||||
|
r'"csrf"\s*:\s*"([^"]+)"',
|
||||||
|
r'"csrfToken"\s*:\s*"([^"]+)"',
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
match = re.search(pattern, html)
|
||||||
|
if match:
|
||||||
|
debug_print(debug, "CSRF found in JavaScript")
|
||||||
|
return match.group(1)
|
||||||
|
|
||||||
|
for cookie_name in ["csrf_token", "_csrf", "csrf", "csrf-token"]:
|
||||||
|
value = session.cookies.get(cookie_name)
|
||||||
|
if value:
|
||||||
|
debug_print(debug, f"CSRF found in cookie {cookie_name}")
|
||||||
|
return value
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def login_failed(response):
|
||||||
|
text = response.text.lower()
|
||||||
|
|
||||||
|
failed_markers = [
|
||||||
|
"invalid username or password",
|
||||||
|
"incorrect username or password",
|
||||||
|
"authentication failed",
|
||||||
|
"login failed",
|
||||||
|
"nieprawidłowa nazwa użytkownika lub hasło",
|
||||||
|
"nieprawidłowy użytkownik lub hasło",
|
||||||
|
"niepoprawna nazwa użytkownika lub hasło",
|
||||||
|
]
|
||||||
|
|
||||||
|
if any(marker in text for marker in failed_markers):
|
||||||
|
return True
|
||||||
|
|
||||||
|
if "/user/login" in response.url:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def check_gitea(server_address, repo_owner, repo_name, username, password, file_path, branch, debug=False):
|
||||||
try:
|
try:
|
||||||
file_path = file_path.lstrip('/')
|
base_url = f"https://{server_address}".rstrip("/")
|
||||||
base_url = f"https://{server_address}".rstrip('/')
|
|
||||||
session = requests.Session()
|
|
||||||
|
|
||||||
debug_print(debug, "Connecting to Gitea server...")
|
|
||||||
try:
|
|
||||||
response = session.get(base_url, timeout=10)
|
|
||||||
response.raise_for_status()
|
|
||||||
debug_print(debug, f"Gitea server available (HTTP {response.status_code})")
|
|
||||||
except requests.exceptions.RequestException as e:
|
|
||||||
print(f"CRITICAL - Could not connect to Gitea server: {str(e)}")
|
|
||||||
return 2
|
|
||||||
|
|
||||||
login_url = f"{base_url}/user/login"
|
login_url = f"{base_url}/user/login"
|
||||||
debug_print(debug, f"Attempting login for user {username}...")
|
file_path = file_path.lstrip("/")
|
||||||
|
|
||||||
|
session = requests.Session()
|
||||||
|
session.headers.update({
|
||||||
|
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) Nagios check_gitea",
|
||||||
|
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
|
||||||
|
"Accept-Language": "en-US,en;q=0.9,pl;q=0.8",
|
||||||
|
"Connection": "close",
|
||||||
|
})
|
||||||
|
|
||||||
|
debug_print(debug, f"Base URL: {base_url}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = session.get(login_url)
|
response = session.get(base_url, timeout=10, allow_redirects=True)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
debug_print(debug, f"Server reachable HTTP {response.status_code}")
|
||||||
|
debug_print(debug, f"Server final URL: {response.url}")
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print(f"CRITICAL - Could not load login page: {str(e)}")
|
print(f"CRITICAL - Could not connect to Gitea server: {e}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
from bs4 import BeautifulSoup
|
try:
|
||||||
soup = BeautifulSoup(response.text, 'html.parser')
|
response = session.get(login_url, timeout=10, allow_redirects=True)
|
||||||
csrf_token = soup.find('input', {'name': '_csrf'})
|
response.raise_for_status()
|
||||||
|
debug_print(debug, f"Login page HTTP {response.status_code}")
|
||||||
if not csrf_token:
|
debug_print(debug, f"Login page final URL: {response.url}")
|
||||||
print("CRITICAL - Could not find CSRF token on login page")
|
except requests.exceptions.RequestException as e:
|
||||||
|
print(f"CRITICAL - Could not load login page: {e}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
csrf_token = csrf_token['value']
|
csrf_token = extract_csrf_token(session, response.text, debug)
|
||||||
debug_print(debug, f"Found CSRF token: {csrf_token}")
|
|
||||||
|
|
||||||
login_data = {
|
login_data = {
|
||||||
"user_name": username,
|
"user_name": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
"_csrf": csrf_token
|
"remember": "on",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if csrf_token:
|
||||||
|
login_data["_csrf"] = csrf_token
|
||||||
|
debug_print(debug, "Using CSRF token")
|
||||||
|
else:
|
||||||
|
debug_print(debug, "No CSRF token found, trying login without _csrf")
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
"Referer": login_url,
|
||||||
|
"Origin": base_url,
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
}
|
||||||
|
|
||||||
|
debug_print(debug, f"Trying login as {username}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = session.post(login_url, data=login_data)
|
response = session.post(
|
||||||
|
login_url,
|
||||||
|
data=login_data,
|
||||||
|
headers=headers,
|
||||||
|
timeout=10,
|
||||||
|
allow_redirects=True,
|
||||||
|
)
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
debug_print(debug, f"After login HTTP {response.status_code}")
|
||||||
if "invalid username or password" in response.text.lower():
|
debug_print(debug, f"After login URL: {response.url}")
|
||||||
print("CRITICAL - Login failed: invalid username or password")
|
|
||||||
return 2
|
|
||||||
|
|
||||||
debug_print(debug, "Login successful")
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print(f"CRITICAL - Login error: {str(e)}")
|
print(f"CRITICAL - Login error: {e}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
repo_url = f"{base_url}/{repo_owner}/{repo_name}"
|
if login_failed(response):
|
||||||
debug_print(debug, f"Checking repository {repo_owner}/{repo_name}...")
|
print("CRITICAL - Login failed")
|
||||||
|
if debug:
|
||||||
|
print("DEBUG: Cookies after login:")
|
||||||
|
for c in session.cookies:
|
||||||
|
print(f"DEBUG: cookie {c.name}={c.value[:80]}")
|
||||||
|
print("DEBUG: First 3000 chars after login:")
|
||||||
|
print(response.text[:3000])
|
||||||
|
return 2
|
||||||
|
|
||||||
|
debug_print(debug, "Login successful")
|
||||||
|
|
||||||
|
repo_url = f"{base_url}/{quote(repo_owner, safe='')}/{quote(repo_name, safe='')}"
|
||||||
|
debug_print(debug, f"Checking repository: {repo_url}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = session.get(repo_url)
|
response = session.get(repo_url, timeout=10, allow_redirects=True)
|
||||||
|
debug_print(debug, f"Repository HTTP {response.status_code}")
|
||||||
|
debug_print(debug, f"Repository final URL: {response.url}")
|
||||||
|
|
||||||
if response.status_code == 404:
|
if response.status_code == 404:
|
||||||
print(f"CRITICAL - Repository {repo_owner}/{repo_name} not found")
|
print(f"CRITICAL - Repository {repo_owner}/{repo_name} not found")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
if response.status_code in [401, 403]:
|
||||||
|
print(f"CRITICAL - No permission to access repository HTTP {response.status_code}")
|
||||||
|
return 2
|
||||||
|
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
debug_print(debug, f"Repository {repo_owner}/{repo_name} is accessible")
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print(f"CRITICAL - Repository access error: {str(e)}")
|
print(f"CRITICAL - Repository access error: {e}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
file_url = f"{base_url}/{repo_owner}/{repo_name}/raw/branch/master/{quote(file_path)}"
|
encoded_file_path = quote(file_path, safe="/")
|
||||||
debug_print(debug, f"Attempting to read file: {file_path}")
|
file_url = (
|
||||||
debug_print(debug, f"Full file URL: {file_url}")
|
f"{base_url}/"
|
||||||
|
f"{quote(repo_owner, safe='')}/"
|
||||||
|
f"{quote(repo_name, safe='')}/raw/branch/"
|
||||||
|
f"{quote(branch, safe='')}/"
|
||||||
|
f"{encoded_file_path}"
|
||||||
|
)
|
||||||
|
|
||||||
|
debug_print(debug, f"Checking file: {file_url}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
response = session.get(file_url)
|
response = session.get(file_url, timeout=10, allow_redirects=True)
|
||||||
debug_print(debug, f"HTTP response: {response.status_code}")
|
debug_print(debug, f"File HTTP {response.status_code}")
|
||||||
|
debug_print(debug, f"File final URL: {response.url}")
|
||||||
|
|
||||||
if response.status_code == 200:
|
if response.status_code == 200:
|
||||||
if response.text:
|
if response.content:
|
||||||
print(f"OK - Successfully read file {file_path} from {repo_owner}/{repo_name}")
|
print(f"OK - Successfully read file {file_path} from {repo_owner}/{repo_name} on branch {branch}")
|
||||||
return 0
|
return 0
|
||||||
else:
|
|
||||||
print(f"WARNING - File {file_path} exists but is empty")
|
print(f"WARNING - File {file_path} exists but is empty")
|
||||||
return 1
|
|
||||||
elif response.status_code == 404:
|
|
||||||
print(f"WARNING - File {file_path} not found in repository")
|
|
||||||
return 1
|
return 1
|
||||||
else:
|
|
||||||
print(f"CRITICAL - File read error (HTTP {response.status_code})")
|
if response.status_code == 404:
|
||||||
|
print(f"WARNING - File {file_path} not found in repository on branch {branch}")
|
||||||
|
return 1
|
||||||
|
|
||||||
|
if response.status_code in [401, 403]:
|
||||||
|
print(f"CRITICAL - No permission to read file {file_path} HTTP {response.status_code}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
print(f"CRITICAL - File read error HTTP {response.status_code}")
|
||||||
|
return 2
|
||||||
|
|
||||||
except requests.exceptions.RequestException as e:
|
except requests.exceptions.RequestException as e:
|
||||||
print(f"CRITICAL - File access error: {str(e)}")
|
print(f"CRITICAL - File access error: {e}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"CRITICAL - Unexpected error: {str(e)}")
|
print(f"CRITICAL - Unexpected error: {e}")
|
||||||
return 2
|
return 2
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description='Verify Gitea repository and file access',
|
description="Nagios check for Gitea repository file access via HTML login"
|
||||||
epilog='Example:\n'
|
|
||||||
'./check_gitea.py --server gitea.example.com --repo-owner owner \\\n'
|
|
||||||
'--repo-name repo --username user --password pass --file path/to/file.txt',
|
|
||||||
formatter_class=argparse.RawTextHelpFormatter
|
|
||||||
)
|
)
|
||||||
parser.add_argument('--server', required=True, help='Gitea server address (e.g., gitea.example.com)')
|
|
||||||
parser.add_argument('--repo-owner', required=True, help='Repository owner username')
|
parser.add_argument("--server", required=True, help="Gitea server address, e.g. git.example.com")
|
||||||
parser.add_argument('--repo-name', required=True, help='Repository name')
|
parser.add_argument("--repo-owner", required=True, help="Repository owner or organization")
|
||||||
parser.add_argument('--username', required=True, help='Login username')
|
parser.add_argument("--repo-name", required=True, help="Repository name")
|
||||||
parser.add_argument('--password', required=True, help='Login password')
|
parser.add_argument("--username", required=True, help="Login username")
|
||||||
parser.add_argument('--file', required=True, help='File path in repository')
|
parser.add_argument("--password", required=True, help="Login password")
|
||||||
parser.add_argument('--debug', action='store_true', help='Enable debug output')
|
parser.add_argument("--file", required=True, help="File path in repository")
|
||||||
|
parser.add_argument("--branch", default="master", help="Branch name, default: master")
|
||||||
|
parser.add_argument("--debug", action="store_true", help="Enable debug output")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
sys.exit(check_gitea(
|
sys.exit(check_gitea(
|
||||||
args.server,
|
server_address=args.server,
|
||||||
args.repo_owner,
|
repo_owner=args.repo_owner,
|
||||||
args.repo_name,
|
repo_name=args.repo_name,
|
||||||
args.username,
|
username=args.username,
|
||||||
args.password,
|
password=args.password,
|
||||||
args.file,
|
file_path=args.file,
|
||||||
args.debug
|
branch=args.branch,
|
||||||
|
debug=args.debug,
|
||||||
))
|
))
|
||||||
Reference in New Issue
Block a user