os diag
This commit is contained in:
parent
8ae543ac69
commit
acdfd3c8ca
1 changed files with 206 additions and 0 deletions
|
|
@ -63,6 +63,7 @@ class DiagnosticsScreen(Screen):
|
|||
yield Button("Refresh", variant="primary", id="refresh-btn")
|
||||
yield Button("Check Podman", variant="default", id="check-podman-btn")
|
||||
yield Button("Check Docker", variant="default", id="check-docker-btn")
|
||||
yield Button("Check OpenSearch Security", variant="default", id="check-opensearch-security-btn")
|
||||
yield Button("Copy to Clipboard", variant="default", id="copy-btn")
|
||||
yield Button("Save to File", variant="default", id="save-btn")
|
||||
yield Button("Back", variant="default", id="back-btn")
|
||||
|
|
@ -92,6 +93,8 @@ class DiagnosticsScreen(Screen):
|
|||
asyncio.create_task(self.check_podman())
|
||||
elif event.button.id == "check-docker-btn":
|
||||
asyncio.create_task(self.check_docker())
|
||||
elif event.button.id == "check-opensearch-security-btn":
|
||||
asyncio.create_task(self.check_opensearch_security())
|
||||
elif event.button.id == "copy-btn":
|
||||
self.copy_to_clipboard()
|
||||
elif event.button.id == "save-btn":
|
||||
|
|
@ -415,5 +418,208 @@ class DiagnosticsScreen(Screen):
|
|||
|
||||
log.write("")
|
||||
|
||||
async def check_opensearch_security(self) -> None:
|
||||
"""Run OpenSearch security configuration diagnostics."""
|
||||
log = self.query_one("#diagnostics-log", Log)
|
||||
log.write("[bold green]OpenSearch Security Diagnostics[/bold green]")
|
||||
|
||||
# Get OpenSearch password from environment or prompt user that it's needed
|
||||
opensearch_password = os.getenv("OPENSEARCH_PASSWORD")
|
||||
if not opensearch_password:
|
||||
log.write("[red]OPENSEARCH_PASSWORD environment variable not set[/red]")
|
||||
log.write("[yellow]Set OPENSEARCH_PASSWORD to test security configuration[/yellow]")
|
||||
log.write("")
|
||||
return
|
||||
|
||||
# Test basic authentication
|
||||
log.write("Testing basic authentication...")
|
||||
cmd = [
|
||||
"curl", "-s", "-k", "-w", "%{http_code}",
|
||||
"-u", f"admin:{opensearch_password}",
|
||||
"https://localhost:9200"
|
||||
]
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
response = stdout.decode().strip()
|
||||
# Extract HTTP status code (last 3 characters)
|
||||
if len(response) >= 3:
|
||||
status_code = response[-3:]
|
||||
response_body = response[:-3]
|
||||
if status_code == "200":
|
||||
log.write("[green]✓ Basic authentication successful[/green]")
|
||||
try:
|
||||
import json
|
||||
info = json.loads(response_body)
|
||||
if "version" in info and "distribution" in info["version"]:
|
||||
log.write(f" OpenSearch version: {info['version']['number']}")
|
||||
except:
|
||||
pass
|
||||
else:
|
||||
log.write(f"[red]✗ Basic authentication failed with status {status_code}[/red]")
|
||||
else:
|
||||
log.write("[red]✗ Unexpected response from OpenSearch[/red]")
|
||||
else:
|
||||
log.write(f"[red]✗ Failed to connect to OpenSearch: {stderr.decode().strip()}[/red]")
|
||||
|
||||
# Test security plugin account info
|
||||
log.write("Testing security plugin account info...")
|
||||
cmd = [
|
||||
"curl", "-s", "-k", "-w", "%{http_code}",
|
||||
"-u", f"admin:{opensearch_password}",
|
||||
"https://localhost:9200/_plugins/_security/api/account"
|
||||
]
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
response = stdout.decode().strip()
|
||||
if len(response) >= 3:
|
||||
status_code = response[-3:]
|
||||
response_body = response[:-3]
|
||||
if status_code == "200":
|
||||
log.write("[green]✓ Security plugin accessible[/green]")
|
||||
try:
|
||||
import json
|
||||
user_info = json.loads(response_body)
|
||||
if "user_name" in user_info:
|
||||
log.write(f" Current user: {user_info['user_name']}")
|
||||
if "roles" in user_info:
|
||||
log.write(f" Roles: {', '.join(user_info['roles'])}")
|
||||
if "tenants" in user_info:
|
||||
tenants = list(user_info['tenants'].keys())
|
||||
log.write(f" Tenants: {', '.join(tenants)}")
|
||||
except:
|
||||
log.write(" Account info retrieved but couldn't parse JSON")
|
||||
else:
|
||||
log.write(f"[red]✗ Security plugin returned status {status_code}[/red]")
|
||||
else:
|
||||
log.write(f"[red]✗ Failed to access security plugin: {stderr.decode().strip()}[/red]")
|
||||
|
||||
# Test internal users
|
||||
log.write("Testing internal users configuration...")
|
||||
cmd = [
|
||||
"curl", "-s", "-k", "-w", "%{http_code}",
|
||||
"-u", f"admin:{opensearch_password}",
|
||||
"https://localhost:9200/_plugins/_security/api/internalusers"
|
||||
]
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
response = stdout.decode().strip()
|
||||
if len(response) >= 3:
|
||||
status_code = response[-3:]
|
||||
response_body = response[:-3]
|
||||
if status_code == "200":
|
||||
try:
|
||||
import json
|
||||
users = json.loads(response_body)
|
||||
if "admin" in users:
|
||||
log.write("[green]✓ Admin user configured[/green]")
|
||||
admin_user = users["admin"]
|
||||
if admin_user.get("reserved"):
|
||||
log.write(" Admin user is reserved (protected)")
|
||||
log.write(f" Total internal users: {len(users)}")
|
||||
except:
|
||||
log.write("[green]✓ Internal users endpoint accessible[/green]")
|
||||
else:
|
||||
log.write(f"[red]✗ Internal users returned status {status_code}[/red]")
|
||||
else:
|
||||
log.write(f"[red]✗ Failed to access internal users: {stderr.decode().strip()}[/red]")
|
||||
|
||||
# Test authentication domains configuration
|
||||
log.write("Testing authentication configuration...")
|
||||
cmd = [
|
||||
"curl", "-s", "-k", "-w", "%{http_code}",
|
||||
"-u", f"admin:{opensearch_password}",
|
||||
"https://localhost:9200/_plugins/_security/api/securityconfig"
|
||||
]
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
response = stdout.decode().strip()
|
||||
if len(response) >= 3:
|
||||
status_code = response[-3:]
|
||||
response_body = response[:-3]
|
||||
if status_code == "200":
|
||||
try:
|
||||
import json
|
||||
config = json.loads(response_body)
|
||||
if "config" in config and "dynamic" in config["config"] and "authc" in config["config"]["dynamic"]:
|
||||
authc = config["config"]["dynamic"]["authc"]
|
||||
if "openid_auth_domain" in authc:
|
||||
log.write("[green]✓ OpenID Connect authentication domain configured[/green]")
|
||||
oidc_config = authc["openid_auth_domain"].get("http_authenticator", {}).get("config", {})
|
||||
if "openid_connect_url" in oidc_config:
|
||||
log.write(f" OIDC URL: {oidc_config['openid_connect_url']}")
|
||||
if "subject_key" in oidc_config:
|
||||
log.write(f" Subject key: {oidc_config['subject_key']}")
|
||||
if "basic_internal_auth_domain" in authc:
|
||||
log.write("[green]✓ Basic internal authentication domain configured[/green]")
|
||||
|
||||
# Check for multi-tenancy
|
||||
if "kibana" in config["config"]["dynamic"]:
|
||||
kibana_config = config["config"]["dynamic"]["kibana"]
|
||||
if kibana_config.get("multitenancy_enabled"):
|
||||
log.write("[green]✓ Multi-tenancy enabled[/green]")
|
||||
else:
|
||||
log.write("[yellow]⚠ Authentication configuration not found in expected format[/yellow]")
|
||||
except Exception as e:
|
||||
log.write("[green]✓ Security config endpoint accessible[/green]")
|
||||
log.write(f" (Could not parse JSON: {str(e)[:50]}...)")
|
||||
else:
|
||||
log.write(f"[red]✗ Security config returned status {status_code}[/red]")
|
||||
else:
|
||||
log.write(f"[red]✗ Failed to access security config: {stderr.decode().strip()}[/red]")
|
||||
|
||||
# Test indices with potential security filtering
|
||||
log.write("Testing index access...")
|
||||
cmd = [
|
||||
"curl", "-s", "-k", "-w", "%{http_code}",
|
||||
"-u", f"admin:{opensearch_password}",
|
||||
"https://localhost:9200/_cat/indices?v"
|
||||
]
|
||||
process = await asyncio.create_subprocess_exec(
|
||||
*cmd, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE
|
||||
)
|
||||
stdout, stderr = await process.communicate()
|
||||
|
||||
if process.returncode == 0:
|
||||
response = stdout.decode().strip()
|
||||
if len(response) >= 3:
|
||||
status_code = response[-3:]
|
||||
response_body = response[:-3]
|
||||
if status_code == "200":
|
||||
log.write("[green]✓ Index listing accessible[/green]")
|
||||
lines = response_body.strip().split('\n')
|
||||
if len(lines) > 1: # Skip header
|
||||
indices_found = []
|
||||
for line in lines[1:]:
|
||||
if 'documents' in line:
|
||||
indices_found.append('documents')
|
||||
elif 'knowledge_filters' in line:
|
||||
indices_found.append('knowledge_filters')
|
||||
elif '.opendistro_security' in line:
|
||||
indices_found.append('.opendistro_security')
|
||||
if indices_found:
|
||||
log.write(f" Key indices found: {', '.join(indices_found)}")
|
||||
else:
|
||||
log.write(f"[red]✗ Index listing returned status {status_code}[/red]")
|
||||
else:
|
||||
log.write(f"[red]✗ Failed to list indices: {stderr.decode().strip()}[/red]")
|
||||
|
||||
log.write("")
|
||||
|
||||
|
||||
# Made with Bob
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue