Skip to content

Commit d4d13d7

Browse files
author
Dementii Priadko
committed
Merge branch 'add-password-generation' into 'main'
Add password generation See merge request postgres-ai/postgres_ai!39
2 parents 424be7b + 9088495 commit d4d13d7

File tree

2 files changed

+210
-34
lines changed

2 files changed

+210
-34
lines changed

docker-compose.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ services:
9898
image: grafana/grafana:12.0.2
9999
container_name: grafana-with-datasources
100100
environment:
101-
GF_SECURITY_ADMIN_USER: demo
101+
GF_SECURITY_ADMIN_USER: monitor
102102
GF_SECURITY_ADMIN_PASSWORD: demo
103103
GF_INSTALL_PLUGINS: yesoreyeram-infinity-datasource
104104
ports:

postgres_ai

Lines changed: 209 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ show_help() {
5353
echo "COMMANDS:"
5454
echo " quickstart [--demo] [--api-key=KEY] [--db-url=URL] [-y] Complete setup: install, configure, and start monitoring"
5555
echo " install [--demo] Clone repository and setup the monitoring project"
56-
echo " start Start all monitoring services using Docker Compose"
56+
echo " start Start all monitoring services using Docker Compose (auto-generates secure Grafana password)"
5757
echo " stop Stop all services"
5858
echo " restart Restart all services"
5959
echo " status Show status of all services"
@@ -77,6 +77,10 @@ show_help() {
7777
echo " show-key Show current API key (masked)"
7878
echo " remove-key Remove stored API key"
7979
echo ""
80+
echo "GRAFANA PASSWORD MANAGEMENT:"
81+
echo " generate-grafana-password Generate secure password for Grafana"
82+
echo " show-grafana-credentials Show current Grafana credentials"
83+
echo ""
8084
echo "OPTIONS:"
8185
echo " --demo Include demo PostgreSQL database for testing"
8286
echo " --api-key=KEY Provide API key for automated report uploads"
@@ -99,11 +103,13 @@ show_help() {
99103
echo " $0 quickstart -y --api-key=key --db-url=postgresql://user:pass@host:5432/db # Fully automated setup"
100104
echo " $0 install # Clone repository and setup for production"
101105
echo " $0 install --demo # Clone repository and setup with demo database"
102-
echo " $0 start # Start monitoring services"
106+
echo " $0 start # Start monitoring services (auto-generates secure Grafana password)"
103107
echo " $0 logs pgwatch-postgres # Show logs for postgres pgwatch instance"
104108
echo " $0 health # Check if all services are healthy"
105109
echo " $0 shell target-db # Open shell in target database (demo mode only)"
106110
echo " $0 check # Verify system prerequisites and readiness"
111+
echo " $0 generate-grafana-password # Generate secure password for Grafana"
112+
echo " $0 show-grafana-credentials # Show current Grafana login credentials"
107113
echo ""
108114
echo "INSTANCE MANAGEMENT EXAMPLES:"
109115
echo " $0 list-instances # Show all configured instances"
@@ -118,17 +124,18 @@ show_help() {
118124
echo " • Add '--api-key=your_key' to any quickstart command for automated report uploads"
119125
echo " • Add '--db-url=postgresql://user:pass@host:port/db' to automatically configure a database"
120126
echo " • Add '-y' to accept all defaults and skip interactive prompts (useful for automation)"
127+
echo " • Secure Grafana password is automatically generated for all setups"
121128
echo ""
122129
echo " MANUAL SETUP:"
123130
echo " PRODUCTION MODE:"
124131
echo " 1. Run '$0 install' to setup monitoring infrastructure"
125132
echo " 2. Add your PostgreSQL instances: '$0 add-instance postgresql://user:pass@host:port/db'"
126-
echo " 3. Start monitoring: '$0 start'"
133+
echo " 3. Start monitoring: '$0 start' (auto-generates secure Grafana password)"
127134
echo ""
128135
echo " DEMO MODE:"
129136
echo " 1. Run '$0 install --demo' to setup with demo database included"
130-
echo " 2. Start all services: '$0 start'"
131-
echo " 3. Access demo database at localhost:5432"
137+
echo " 2. Start all services: '$0 start' (auto-generates secure Grafana password)"
138+
echo " 3. Access demo database at localhost:55432"
132139
echo ""
133140
echo " The CLI auto-detects the project directory and should be run from outside it"
134141
echo
@@ -146,12 +153,12 @@ show_help() {
146153
echo " 🚀 MAIN: Grafana Dashboard: http://localhost:3000 (demouser/demopwd)"
147154
echo ""
148155
echo " Technical URLs (for advanced users):"
149-
echo " PGWatch Postgres: http://localhost:8080"
150-
echo " PGWatch Prometheus: http://localhost:8089"
151-
echo " Prometheus: http://localhost:9090"
152-
echo " Flask API: http://localhost:5000"
153-
echo " Sink DB: postgresql://postgres:postgres@localhost:5433/postgres"
154-
echo " Demo DB: postgresql://postgres:postgres@localhost:5432/target_database (--demo mode only)"
156+
echo " PGWatch Postgres: http://localhost:58080"
157+
echo " PGWatch Prometheus: http://localhost:58089"
158+
echo " Prometheus: http://localhost:59090"
159+
echo " Flask API: http://localhost:55000"
160+
echo " Sink DB: postgresql://postgres:postgres@localhost:55433/postgres"
161+
echo " Demo DB: postgresql://postgres:postgres@localhost:55432/target_database (--demo mode only)"
155162
}
156163

157164
# Check basic prerequisites (software installation)
@@ -243,7 +250,7 @@ check_system_resources() {
243250
fi
244251

245252
# Check if required ports are available
246-
local required_ports=(3000 5000 5432 5433 8080 8089 9090)
253+
local required_ports=(3000 55000 55432 55433 58080 58089 59090)
247254
local ports_in_use=()
248255

249256
for port in "${required_ports[@]}"; do
@@ -261,8 +268,8 @@ check_system_resources() {
261268
if [ ${#ports_in_use[@]} -ne 0 ]; then
262269
log_warning "The following ports are already in use: ${ports_in_use[*]}"
263270
log_warning "This may cause conflicts when starting services"
264-
echo "Required ports: 3000 (Grafana), 5000 (Flask), 5432 (Target DB), 5433 (Sink DB),"
265-
echo " 8080 (PGWatch), 8089 (PGWatch Prometheus), 9090 (Prometheus)"
271+
echo "Required ports: 3000 (Grafana), 55000 (Flask), 55432 (Target DB), 55433 (Sink DB),"
272+
echo " 58080 (PGWatch), 58089 (PGWatch Prometheus), 59090 (Prometheus)"
266273
fi
267274

268275
log_success "System resources check completed"
@@ -471,6 +478,146 @@ is_demo_mode() {
471478
return 1
472479
}
473480

481+
# Password Generator Functions
482+
483+
# Generate a secure random password
484+
generate_secure_password() {
485+
# Generate a 16-character password with mixed case, numbers, and special characters
486+
local password
487+
password=$(openssl rand -base64 12 | tr -d "=+/" | cut -c1-16)
488+
489+
# Ensure it contains at least one uppercase, one lowercase, one number, and one special character
490+
local upper=$(echo {A..Z} | tr -d ' ' | fold -w1 | sort -R | head -1)
491+
local lower=$(echo {a..z} | tr -d ' ' | fold -w1 | sort -R | head -1)
492+
local number=$(echo {0..9} | tr -d ' ' | fold -w1 | sort -R | head -1)
493+
local special=$(echo "!@#$%^&*" | fold -w1 | sort -R | head -1)
494+
495+
# Replace characters at random positions
496+
local pos1=$((RANDOM % 16))
497+
local pos2=$((RANDOM % 16))
498+
local pos3=$((RANDOM % 16))
499+
local pos4=$((RANDOM % 16))
500+
501+
# Ensure all positions are different
502+
while [ "$pos2" -eq "$pos1" ]; do pos2=$((RANDOM % 16)); done
503+
while [ "$pos3" -eq "$pos1" ] || [ "$pos3" -eq "$pos2" ]; do pos3=$((RANDOM % 16)); done
504+
while [ "$pos4" -eq "$pos1" ] || [ "$pos4" -eq "$pos2" ] || [ "$pos4" -eq "$pos3" ]; do pos4=$((RANDOM % 16)); done
505+
506+
# Build the final password
507+
password=$(echo "$password" | sed "s/./$upper/$((pos1+1))" | sed "s/./$lower/$((pos2+1))" | sed "s/./$number/$((pos3+1))" | sed "s/./$special/$((pos4+1))")
508+
509+
echo "$password"
510+
}
511+
512+
# Check if Grafana password exists
513+
check_grafana_password_exists() {
514+
if [ -f "$SCRIPT_DIR/.pgwatch-config" ]; then
515+
local password=$(grep "^grafana_password=" "$SCRIPT_DIR/.pgwatch-config" 2>/dev/null | cut -d'=' -f2)
516+
if [ -n "$password" ]; then
517+
return 0 # Password exists
518+
fi
519+
fi
520+
return 1 # Password doesn't exist
521+
}
522+
523+
# Ensure Grafana password exists (generate if needed)
524+
ensure_grafana_password() {
525+
if ! check_grafana_password_exists; then
526+
log_info "No Grafana password configured - generating secure password..."
527+
generate_grafana_password
528+
else
529+
log_info "Grafana password already configured"
530+
fi
531+
}
532+
533+
# Generate Grafana password and update configuration
534+
generate_grafana_password() {
535+
local password
536+
password=$(generate_secure_password)
537+
538+
# Create config file if it doesn't exist
539+
touch "$SCRIPT_DIR/.pgwatch-config"
540+
541+
# Remove existing grafana_password line if present
542+
if [ -f "$SCRIPT_DIR/.pgwatch-config" ]; then
543+
grep -v "^grafana_password=" "$SCRIPT_DIR/.pgwatch-config" > "$SCRIPT_DIR/.pgwatch-config.tmp" || true
544+
mv "$SCRIPT_DIR/.pgwatch-config.tmp" "$SCRIPT_DIR/.pgwatch-config"
545+
fi
546+
547+
# Add the new Grafana password
548+
echo "grafana_password=$password" >> "$SCRIPT_DIR/.pgwatch-config"
549+
550+
log_success "Grafana password generated successfully"
551+
log_info "New password: $password"
552+
log_info "Username: monitor"
553+
log_info "Access Grafana at: http://localhost:3000"
554+
555+
# Change password via existing Grafana container
556+
change_grafana_password_via_container "$password"
557+
558+
return 0
559+
}
560+
561+
# Change Grafana password via existing container
562+
change_grafana_password_via_container() {
563+
local password="$1"
564+
local compose_cmd=$(get_compose_cmd)
565+
566+
cd "$SCRIPT_DIR"
567+
568+
# Check if Grafana container is running
569+
if ! $compose_cmd -f "$COMPOSE_FILE" ps --services --filter "status=running" | grep -q "grafana"; then
570+
log_warning "Grafana container is not running. Starting it first..."
571+
$compose_cmd -f "$COMPOSE_FILE" up -d grafana
572+
573+
# Wait for Grafana to be ready
574+
log_info "Waiting for Grafana to be ready..."
575+
sleep 10
576+
fi
577+
578+
# Change password using Grafana CLI inside the container
579+
log_info "Changing Grafana password via container..."
580+
581+
# Use grafana-cli to change the admin password
582+
if $compose_cmd -f "$COMPOSE_FILE" exec -T grafana grafana-cli admin reset-admin-password "$password" > /dev/null 2>&1; then
583+
log_success "Grafana password changed successfully via container"
584+
else
585+
log_warning "Failed to change password via grafana-cli, trying alternative method..."
586+
587+
# Alternative: Use SQL to update password directly in database
588+
if $compose_cmd -f "$COMPOSE_FILE" exec -T grafana sqlite3 /var/lib/grafana/grafana.db "UPDATE user SET password = '$password' WHERE login = 'monitor';" > /dev/null 2>&1; then
589+
log_success "Grafana password changed successfully via database update"
590+
else
591+
log_error "Failed to change Grafana password via container"
592+
log_info "Password has been saved to .pgwatch-config but may need manual update in Grafana"
593+
return 1
594+
fi
595+
fi
596+
597+
log_info "Password saved to .pgwatch-config file"
598+
}
599+
600+
# Show current Grafana credentials
601+
show_grafana_credentials() {
602+
if [ -f "$SCRIPT_DIR/.pgwatch-config" ]; then
603+
local password=$(grep "^grafana_password=" "$SCRIPT_DIR/.pgwatch-config" 2>/dev/null | cut -d'=' -f2)
604+
if [ -n "$password" ]; then
605+
log_info "Current Grafana credentials:"
606+
echo " Username: monitor"
607+
echo " Password: $password"
608+
echo " URL: http://localhost:3000"
609+
else
610+
log_warning "No custom Grafana password configured"
611+
log_info "Using default credentials: monitor/demo"
612+
log_info "Use '$0 generate-grafana-password' to set a secure password"
613+
fi
614+
else
615+
log_warning "No Grafana password configured"
616+
log_info "Using default credentials: monitor/demo"
617+
log_info "Use '$0 generate-grafana-password' to set a secure password"
618+
fi
619+
}
620+
474621
# API Key Management Functions
475622

476623
# Add API key to configuration
@@ -653,7 +800,7 @@ EOF
653800

654801
if [ "$demo_mode" = true ]; then
655802
log_info "You can now start all services (including demo database) with: $0 start"
656-
log_info "The demo database will be available at: postgresql://postgres:postgres@localhost:5432/target_database"
803+
log_info "The demo database will be available at: postgresql://postgres:postgres@localhost:55432/target_database"
657804
else
658805
log_info "You can now:"
659806
echo " 1. Add PostgreSQL instances to monitor: $0 add-instance 'postgresql://user:pass@host:port/db'"
@@ -872,16 +1019,25 @@ quickstart_setup() {
8721019
fi
8731020
update_config
8741021

875-
# Step 5/4: Start services
1022+
# Step 5/4: Ensure Grafana password is configured
8761023
echo
8771024
if [ "$demo_mode" = true ]; then
878-
log_info "Step 4: Starting monitoring services..."
1025+
log_info "Step 4: Configuring Grafana security..."
8791026
else
1027+
log_info "Step 5: Configuring Grafana security..."
1028+
fi
1029+
ensure_grafana_password
1030+
1031+
# Step 6/5: Start services
1032+
echo
1033+
if [ "$demo_mode" = true ]; then
8801034
log_info "Step 5: Starting monitoring services..."
1035+
else
1036+
log_info "Step 6: Starting monitoring services..."
8811037
fi
8821038
start_services
8831039

884-
# Step 6/5: Final summary
1040+
# Step 7/6: Final summary
8851041
echo
8861042
log_success "🎉 Quickstart setup completed successfully!"
8871043
echo
@@ -890,7 +1046,7 @@ quickstart_setup() {
8901046
echo " ✅ Demo PostgreSQL database (monitoring target)"
8911047
fi
8921048
echo " ✅ PostgreSQL monitoring infrastructure"
893-
echo " ✅ Grafana dashboards"
1049+
echo " ✅ Grafana dashboards (with secure password)"
8941050
echo " ✅ Prometheus metrics storage"
8951051
echo " ✅ Flask API backend"
8961052
echo " ✅ Automated report generation (every 24h)"
@@ -904,7 +1060,7 @@ quickstart_setup() {
9041060
else
9051061
log_info "Demo mode next steps:"
9061062
echo " • Explore Grafana dashboards at http://localhost:3000"
907-
echo " • Connect to demo database: postgresql://postgres:postgres@localhost:5432/target_database"
1063+
echo " • Connect to demo database: postgresql://postgres:postgres@localhost:55432/target_database"
9081064
echo " • Generate some load on the demo database to see metrics"
9091065
fi
9101066

@@ -925,6 +1081,9 @@ get_compose_cmd() {
9251081
start_services() {
9261082
precheck_for_services
9271083

1084+
# Ensure Grafana password is configured before starting services
1085+
ensure_grafana_password
1086+
9281087
local compose_cmd=$(get_compose_cmd)
9291088

9301089
cd "$SCRIPT_DIR"
@@ -1002,17 +1161,17 @@ health_check() {
10021161
log_info "Performing health checks..."
10031162

10041163
local services=(
1005-
"sink-postgres:5433:PostgreSQL Sink"
1006-
"pgwatch-postgres:8080:PGWatch PostgreSQL"
1007-
"pgwatch-prometheus:8089:PGWatch Prometheus"
1008-
"sink-prometheus:9090:Prometheus"
1164+
"sink-postgres:55433:PostgreSQL Sink"
1165+
"pgwatch-postgres:58080:PGWatch PostgreSQL"
1166+
"pgwatch-prometheus:58089:PGWatch Prometheus"
1167+
"sink-prometheus:59090:Prometheus"
10091168
"grafana:3000:Grafana"
1010-
"flask-pgss-api:5000:Flask API"
1169+
"flask-pgss-api:55000:Flask API"
10111170
)
10121171

10131172
# Add target-db only in demo mode
10141173
if is_demo_mode; then
1015-
services=("target-db:5432:PostgreSQL Target Database" "${services[@]}")
1174+
services=("target-db:55432:PostgreSQL Target Database" "${services[@]}")
10161175
fi
10171176

10181177
local all_healthy=true
@@ -1514,17 +1673,17 @@ open_shell() {
15141673
show_access_info() {
15151674
echo
15161675
log_info "Technical service URLs (for advanced users):"
1517-
echo " PGWatch Postgres: http://localhost:8080"
1518-
echo " PGWatch Prometheus: http://localhost:8089"
1519-
echo " Prometheus: http://localhost:9090"
1520-
echo " Flask API: http://localhost:5000"
1676+
echo " PGWatch Postgres: http://localhost:58080"
1677+
echo " PGWatch Prometheus: http://localhost:58089"
1678+
echo " Prometheus: http://localhost:59090"
1679+
echo " Flask API: http://localhost:55000"
15211680

15221681
# Show target database only in demo mode
15231682
if is_demo_mode; then
1524-
echo " Target Database: postgresql://postgres:postgres@localhost:5432/target_database"
1683+
echo " Target Database: postgresql://postgres:postgres@localhost:55432/target_database"
15251684
fi
15261685

1527-
echo " Sink Database: postgresql://postgres:postgres@localhost:5433/postgres"
1686+
echo " Sink Database: postgresql://postgres:postgres@localhost:55433/postgres"
15281687
echo
15291688

15301689
# Show reports information
@@ -1549,7 +1708,18 @@ show_access_info() {
15491708
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
15501709
echo -e "${GREEN}🚀 MAIN ACCESS POINT - Start here:${NC}"
15511710
echo -e "${GREEN} Grafana Dashboard: http://localhost:3000${NC}"
1552-
echo -e "${GREEN} Login: demo / demo${NC}"
1711+
1712+
# Show custom credentials if available
1713+
if [ -f "$SCRIPT_DIR/.pgwatch-config" ]; then
1714+
local grafana_password=$(grep "^grafana_password=" "$SCRIPT_DIR/.pgwatch-config" 2>/dev/null | cut -d'=' -f2)
1715+
if [ -n "$grafana_password" ]; then
1716+
echo -e "${GREEN} Login: monitor / $grafana_password${NC}"
1717+
else
1718+
echo -e "${GREEN} Login: monitor / demo${NC}"
1719+
fi
1720+
else
1721+
echo -e "${GREEN} Login: monitor / demo${NC}"
1722+
fi
15531723
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
15541724
}
15551725

@@ -1620,6 +1790,12 @@ main() {
16201790
"remove-key")
16211791
remove_api_key
16221792
;;
1793+
"generate-grafana-password")
1794+
generate_grafana_password
1795+
;;
1796+
"show-grafana-credentials")
1797+
show_grafana_credentials
1798+
;;
16231799
"help"|"--help"|"-h")
16241800
show_help
16251801
;;

0 commit comments

Comments
 (0)