Secure Production Ready Setup, Security Best Practices and Setup Monitoring Logging

This commit is contained in:
Syifa 2025-09-01 17:30:33 +07:00
parent 66806e70d7
commit f391d5355a
13 changed files with 32806 additions and 78 deletions

View File

@ -1,48 +1,37 @@
FROM php:8.2-cli
# Buat non-root user
ARG USER=appuser
ARG UID=1000
RUN useradd -u $UID -m $USER
# Install dependencies & PostgreSQL driver
RUN apt-get update && apt-get install -y \
libpq-dev \
postgresql-client \
unzip \
git \
libzip-dev \
libicu-dev \
&& docker-php-ext-install pdo pdo_pgsql pgsql zip intl \
&& rm -rf /var/lib/apt/lists/*
# Install Composer
COPY --from=composer:2 /usr/bin/composer /usr/bin/composer
WORKDIR /var/www
# Install build dependencies & PHP extensions
RUN apk add --no-cache \
bash \
git \
unzip \
libpng-dev \
libjpeg-turbo-dev \
freetype-dev \
libzip-dev \
icu-dev \
zlib-dev \
postgresql-dev \
curl \
oniguruma-dev \
autoconf \
gcc \
g++ \
make \
pkgconfig \
&& docker-php-ext-configure gd --with-freetype --with-jpeg \
&& docker-php-ext-install pdo pdo_pgsql mbstring zip intl bcmath gd \
&& rm -rf /var/cache/apk/*
# Copy source code
COPY . .
# Install Composer
RUN curl -sS https://getcomposer.org/installer | php -- --install-dir=/usr/bin --filename=composer
# Change owner to non-root user
RUN chown -R $USER:$USER /var/www
# Copy project
COPY . /var/www
# Switch to non-root user
USER $USER
# Set permissions
RUN mkdir -p storage bootstrap/cache \
&& chown -R www-data:www-data storage bootstrap/cache
# Install Laravel dependencies & optimasi
RUN composer install --no-dev --optimize-autoloader
# Cache Laravel
RUN php artisan config:cache \
&& php artisan route:cache \
&& php artisan view:cache
# Install Laravel dependencies
RUN composer install --no-dev --optimize-autoloader --prefer-dist
EXPOSE 8000
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]
CMD ["php", "artisan", "serve", "--host=0.0.0.0", "--port=8000"]

View File

@ -89,7 +89,7 @@ return [
'port' => env('DB_PORT', '5432'),
'database' => env('DB_DATABASE', 'laravel'),
'username' => env('DB_USERNAME', 'root'),
'password' => env('DB_PASSWORD', ''),
'password' => env('DB_PASSWORD_FILE', ''),
'charset' => env('DB_CHARSET', 'utf8'),
'prefix' => '',
'prefix_indexes' => true,

View File

@ -1,60 +1,96 @@
version: "3.9"
secrets:
db_password:
file: ./secrets/db_password.txt
volumes:
db-data:
networks:
sijago_net:
driver: overlay
monitoring_net:
external: true # pastikan monitoring stack network sudah dibuat sebelumnya
services:
app:
build:
context: .
dockerfile: Dockerfile.prod
container_name: sijago_app
working_dir: /var/www
volumes:
- .:/var/www
ports:
- "8000:8000"
depends_on:
db:
condition: service_healthy
redis:
condition: service_healthy
environment:
APP_ENV: production
APP_DEBUG: false
APP_KEY: base64:8LpKi2PtQ4nhCQr+u495ZRvfno7PjNrVnh0kyuoLTeE=
DB_CONNECTION: pgsql
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: sijago
DB_USERNAME: sijago_user
DB_PASSWORD: secret
CACHE_DRIVER: redis
REDIS_HOST: redis
db:
image: postgres:13
container_name: sijago_db
restart: unless-stopped
environment:
POSTGRES_DB: sijago
POSTGRES_USER: sijago_user
POSTGRES_PASSWORD: secret
POSTGRES_PASSWORD_FILE: /run/secrets/db_password
volumes:
- db-data-prod:/var/lib/postgresql/data
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U sijago_user"]
interval: 5s
timeout: 5s
retries: 5
retries: 10
secrets:
- db_password
networks:
- sijago_net
deploy:
restart_policy:
condition: on-failure
resources:
limits:
cpus: '0.5'
memory: 512M
redis:
image: redis:7-alpine
container_name: sijago_redis
restart: unless-stopped
ports:
- "6379:6379" # beda port juga biar aman
- "6379:6379"
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 5s
timeout: 5s
retries: 5
networks:
- sijago_net
deploy:
restart_policy:
condition: on-failure
resources:
limits:
cpus: '0.2'
memory: 256M
volumes:
db-data
app:
image: sijago_app:prod # build dulu: docker build -t sijago_app:prod -f Dockerfile.prod .
working_dir: /var/www
volumes:
- .:/var/www:cached
ports:
- "8000:8000"
environment:
APP_ENV: production
APP_DEBUG: "false"
DB_CONNECTION: pgsql
DB_HOST: db
DB_PORT: 5432
DB_DATABASE: sijago
DB_USERNAME: sijago_user
DB_PASSWORD_FILE: /run/secrets/db_password
CACHE_DRIVER: redis
REDIS_HOST: redis
PROMETHEUS_URL: http://prometheus:9090
CADVISOR_URL: http://cadvisor:8080
GRAFANA_URL: http://grafana:3000
secrets:
- db_password
networks:
- sijago_net
- monitoring_net
deploy:
restart_policy:
condition: on-failure
resources:
limits:
cpus: '1.0'
memory: 512M
reservations:
cpus: '0.5'
memory: 256M

View File

@ -0,0 +1,81 @@
version: "3.9"
networks:
monitoring_net:
external: true
volumes:
grafana-data:
loki-data:
services:
cadvisor:
image: gcr.io/cadvisor/cadvisor:latest
ports:
- "8080:8080"
volumes:
- /:/rootfs:ro
- /var/run:/var/run:ro
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
networks:
- monitoring_net
deploy:
mode: global
privileged: true # biar cadvisor bisa akses semua metric container
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml:ro
networks:
- monitoring_net
deploy:
replicas: 1
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
volumes:
- grafana-data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
environment:
- GF_SECURITY_ADMIN_USER=syifa
- GF_SECURITY_ADMIN_PASSWORD=secret123
depends_on:
- prometheus
- loki
networks:
- monitoring_net
deploy:
replicas: 1
loki:
image: grafana/loki:2.8.2
ports:
- "3100:3100"
volumes:
- ./loki-config.yml:/etc/loki/local-config.yml:ro
- loki-data:/loki
command: -config.file=/etc/loki/local-config.yml
networks:
- monitoring_net
deploy:
replicas: 1
promtail:
image: grafana/promtail:2.8.2
volumes:
- /var/log:/var/log
- /var/lib/docker/containers:/var/lib/docker/containers:ro
- ./promtail-config.yml:/etc/promtail/config.yml:ro
command: -config.file=/etc/promtail/config.yml
depends_on:
- loki
networks:
- monitoring_net
deploy:
replicas: 1

View File

@ -0,0 +1,9 @@
apiVersion: 1
providers:
- name: 'Default'
orgId: 1
folder: ''
type: file
options:
path: /etc/grafana/provisioning/dashboards

View File

@ -0,0 +1,30 @@
{
"id": null,
"title": "Docker Container Overview",
"tags": ["docker", "cadvisor"],
"timezone": "browser",
"panels": [
{
"type": "graph",
"title": "Container CPU Usage",
"targets": [
{
"expr": "rate(container_cpu_usage_seconds_total[1m])",
"legendFormat": "{{container_label_com_docker_swarm_task_name}}"
}
]
},
{
"type": "graph",
"title": "Container Memory Usage",
"targets": [
{
"expr": "container_memory_usage_bytes",
"legendFormat": "{{container_label_com_docker_swarm_task_name}}"
}
]
}
],
"schemaVersion": 16,
"version": 1
}

View File

@ -0,0 +1,13 @@
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
- name: Loki
type: loki
access: proxy
url: http://loki:3100

View File

@ -0,0 +1,54 @@
auth_enabled: false
server:
http_listen_port: 3100
grpc_listen_port: 9096
ingester:
lifecycler:
address: 0.0.0.0
ring:
kvstore:
store: inmemory
replication_factor: 1
chunk_idle_period: 5m
max_chunk_age: 1h
chunk_target_size: 1048576
chunk_retain_period: 30s
wal:
enabled: true
dir: /loki/wal
schema_config:
configs:
- from: 2020-10-24
store: boltdb-shipper
object_store: filesystem
schema: v11
index:
prefix: index_
period: 24h
storage_config:
boltdb_shipper:
active_index_directory: /loki/index
cache_location: /loki/cache
shared_store: filesystem
filesystem:
directory: /loki/chunks
compactor:
working_directory: /loki/compactor
shared_store: filesystem
limits_config:
ingestion_rate_mb: 8
ingestion_burst_size_mb: 16
max_concurrent_tail_requests: 20
chunk_store_config:
max_look_back_period: 0s
table_manager:
retention_deletes_enabled: true
retention_period: 168h # 7 hari

25
monitoring/prometheus.yml Normal file
View File

@ -0,0 +1,25 @@
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'sijago_app'
#metrics_path: /metrics
static_configs:
- targets: ['sijago_stack_app:8000']
# Scrape semua cadvisor (global mode di swarm)
- job_name: 'cadvisor'
dns_sd_configs:
- names:
- 'tasks.monitoring_stack_cadvisor'
type: A
port: 8080
# Scrape Loki (opsional, buat healthcheck)
- job_name: 'loki'
static_configs:
- targets: ['loki:3100']

View File

@ -0,0 +1,34 @@
server:
http_listen_port: 9080
grpc_listen_port: 0
positions:
filename: /tmp/positions.yaml
clients:
- url: http://loki:3100/loki/api/v1/push
scrape_configs:
# Ambil log khusus dari container "sijago_stack_app"
- job_name: sijago
docker_sd_configs:
- host: unix:///var/run/docker.sock
relabel_configs:
# Ambil hanya container dengan nama sijago_stack_app
- source_labels: [__meta_docker_container_name]
regex: /sijago_stack_app.*
action: keep
# Path log
- source_labels: [__meta_docker_container_log_stream]
target_label: stream
- source_labels: [__meta_docker_container_name]
target_label: container
# Ambil semua log dari semua container Docker
- job_name: docker
static_configs:
- targets:
- localhost
labels:
job: docker
__path__: /var/lib/docker/containers/*/*-json.log

18
nginx/default.conf Normal file
View File

@ -0,0 +1,18 @@
server {
listen 80;
server_name localhost;
root /var/www/public;
index index.php index.html;
location / {
try_files $uri $uri/ /index.php?$query_string;
}
location ~ \.php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}

1
secrets/db_password.txt Normal file
View File

@ -0,0 +1 @@
secret

32438
trivy_laravel_report.json Normal file

File diff suppressed because it is too large Load Diff