Secure Grafana Loki

Secure Grafana Loki

secure-grafana-loki.png

จากบทความก่อนที่เราได้ลองสร้าง Simple Centralized Logs Monitoring with Grafana Loki กันไปในบทความที่แล้ว แต่ยังมีเรื่องสำคัญที่ยังไม่ได้จัดการก็คือเรื่อง Security นั่นเอง ในบทความนี้เรามาทำการ improve Security ให้กับ Centralized Logs Monitoring ของเรากันครับ

Theory

ในบทความนี้เราจะใช้เทคนิคในการทำ Security ให้กับ Centralized Logs Monitoring ดังต่อไปนี้

TLS/HTTPS: Encrypt Communication

https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview#https
https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Overview#https

HTTP

เป็น protocol ที่ใช้ในการ fetching resource จาก server ปลายทาง เช่น HTML documents ซึ่งเป็น protocol หลักที่ใช้ใน Web browser หรืออาจเรียกได้ว่าเป็น client-server protocol ที่ใช้กันอย่างแพร่หลายในปัจจุบัน

โดยการติดต่อสื่อสารระหว่าง client-server ที่ใช้ protocol นี้ จะยังไม่ถูกเข้ารหัส ซึ่งมีความเสี่ยงในการถูกดักจับข้อมูล (man-in-the-middle attacks) จึงได้มีการพัฒนาเป็น HTTPS protocol ขึ้นมาเพื่อช่วยในการเข้ารหัสข้อความในการติดต่อสื่อสารระหว่าง client-server

TLS (Transport Layer Security)

TLS เป็น protocol ที่ช่วยในการเข้ารหัส โดยส่วนใหญ่จะรู้จักกันในชื่อ SSL (Secure Sockets Layer) ซึ่ง HTTP protocol นี้จะใช้ TLS มาช่วยในการเข้ารหัสจึงกลายเป็น HTTPS protocol หรือที่เรียกว่า HTTP over TLS ที่ใช้งานกันอย่างแพร่หลายในทุกวันนี้

HTTPS ทำงานยังไง?

การทำงานของ HTTPS จะทำการเข้ารหัสการติดต่อสื่อสารในรูปแบบของ asymmetric public key infrastructure ซึ่งจะประกอบไปด้วย keys ทั้งหมด 2 ประเภท ได้แก่

  1. Private key - key นี้จะอยู่กับเจ้าของ server เพื่อใช้ในการถอดข้อมูลที่ทำการเข้ารหัส ซึ่งตามบทความนี้จะเก็บอยู่ใน Grafana Loki Gateway
  2. Public key - key นี้จะใช้สำหรับใครก็ตามที่ต้องการติดต่อสื่อสารกับ server ซึ่งตามบทความนี้ตัว Promtail agent จะใช้ Public key ของ Grafana Loki Gateway ในการเข้ารหัสข้อมูล แล้วส่งข้อมูล Logs ให้ต่อไป

Reverse Proxy (NGINX) Gateway: Access Control

ในบทความที่แล้วเราได้ implement Grafana Loki ในรูปแบบของ Simple Scalable ซึ่งจะมีการ deploy ตัว Grafana Loki Gateway (Nginx) เป็น reverse proxy คั่นกลางก่อนที่จะเข้าถึง Grafana Loki โดยตัว Gateway นี้จะทำการ enforce ในการทำ authentication requests ก่อนที่จะเข้าถึง Grafana Loki

Basic Authentication: Access Control

เป็นการยืนยันตัวตนในการเข้าถึง Grafana Loki Server โดยจะใช้หลักการยืนยันตัวตนในรูปแบบของ username และ password ในการเข้าถึง resource ซึ่งจะทำผ่าน Grafana Loki Gateway

image.png

Practice

ใน Lab นี้เราจะทำการ configure ให้ Promtail และ Grafana ทำการติดต่อสื่อสารกับ Grafana Loki ด้วย Protocol HTTPS และมีการทำ Authentication ก่อนเข้าถึง Grafana Loki

⚠️

Lab นี้จะใช้ Self-signed certificate ในการทำ HTTPS ซึ่งเป็นการใช้สำหรับ Network Private ระหว่าง Promtail agent, Grafana และ Grafana Loki เท่านั้น จึงต้องมีการทำ Trust certificate เพื่อใช้ในการติดต่อสื่อสารกันระหว่าง Servers

แต่ไม่ควรนำ Self-signed certificate ที่สร้างนี้ไปใช้ในการติดต่อสื่อสารแบบ client server ที่ให้ user ใช้ในการเข้าถึง เช่น การเข้าผ่าน Web Browser เนื่องจากอาจเป็นความเสี่ยงในกรณีถูกปลอมแปลง certificate ได้

Grafana Loki

  1. ทำการสร้าง Self-Signed TLS Certificate เพื่อใช้ในการติดต่อสื่อสารระหว่าง Promtail กับ Grafana Loki ผ่าน Protocol HTTPS ซึ่งตามตัวอย่างเราจะกำหนดอายุไว้ที่ 1 ปี โดยเราจะ reuse code จาก repository Simple Centralized Logs Monitoring with Grafana Loki

    git clone [email protected]:nestpractice/observability-simpleloki.git
    
    cd observability-simpleloki
    
    mkdir -p certs auth
    
    cat > certs/cert.conf <<EOF
    [req]
    default_bits = 2048
    prompt = no
    default_md = sha256
    distinguished_name = dn
    req_extensions = req_ext
    
    [dn]
    CN = loki-gateway
    
    [req_ext]
    subjectAltName = @alt_names
    
    [alt_names]
    DNS.1 = loki-gateway
    DNS.2 = localhost
    IP.1 = <GRAFANA_LOKI_IP_ADDRESS>
    EOF
    
    openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
      -keyout certs/loki.key -out certs/loki.crt -config certs/cert.conf -extensions req_ext
  2. จากนั้นทำการสร้าง Basic Auth Username Password เพื่อใช้ในการ Authentication ในการเข้าถึง Grafana Loki

    docker run --rm -it httpd:alpine htpasswd -Bbn username password > auth/htpasswd
  3. ตั้งค่า nginx.conf ให้รับ request HTTPS ผ่าน port 443 (Container Port) ใน path ของการ read และ write ใน Grafana Loki

    config/nginx.conf
    error_log  /dev/stderr;
    pid        /tmp/nginx.pid;
    worker_rlimit_nofile 8192;
    
    events {
        worker_connections  4096;
    }
    
    http {
      default_type application/octet-stream;
      log_format   main '$remote_addr - $remote_user [$time_local]  $status '
        '"$request" $body_bytes_sent "$http_referer" '
        '"$http_user_agent" "$http_x_forwarded_for"';
      access_log   /dev/stderr  main;
      sendfile     on;
      tcp_nopush   on;
    
      upstream read {
        server loki-read:3100;
      }
    
      upstream write {
        server loki-write:3100;
      }
    
      upstream cluster {
        server loki-read:3100;
        server loki-write:3100;
      }
    
      server {
        listen 80;
        listen 3100;
        listen 443 ssl;
    
        ssl_certificate /etc/nginx/certs/loki.crt;
        ssl_certificate_key /etc/nginx/certs/loki.key;
    
        location = /ring {
            proxy_pass       http://cluster$request_uri;
        }
    
        location = /memberlist {
            proxy_pass       http://cluster$request_uri;
        }
    
        location = /config {
            proxy_pass       http://cluster$request_uri;
        }
    
        location = /metrics {
            proxy_pass       http://cluster$request_uri;
        }
    
        location = /ready {
            proxy_pass       http://cluster$request_uri;
        }
    
        location = /loki/api/v1/push {
            auth_basic "Restricted";
            auth_basic_user_file /etc/nginx/auth/htpasswd;
            proxy_pass       http://write$request_uri;
        }
    
        location = /loki/api/v1/tail {
            proxy_pass       http://read$request_uri;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection "upgrade";
        }
    
        location ~ /loki/api/.* {
            auth_basic "Restricted";
            auth_basic_user_file /etc/nginx/auth/htpasswd;
            proxy_pass       http://read$request_uri;
        }
      }
    }
  4. เปลี่ยนแปลง configuration loki-gateway service ใน docker-compose.yaml file

    docker-compose.yaml
    services:
      ...
      loki-gateway:
        image: nginx:latest
        volumes:
          - ./config/nginx.conf:/etc/nginx/nginx.conf
          - ./certs:/etc/nginx/certs
          - ./auth:/etc/nginx/auth
        ports:
          - "8443:443"
          - "8080:80"
          - "3100"
        networks:
          - loki
        ...
  5. ทำการ run Grafana Loki ขึ้นมาใหม่

    docker compose -f simple-loki/docker-compose.yaml up -d
  6. ทำการแก้ไข Datasource ใน Grafana ให้ใช้ HTTPS, BasicAuth ในการเข้าถึง Grafana Loki และทำการ Add self-signed certificate จากไฟล์ loki.crt ที่เราได้ทำการสร้างตามขั้นตอนที่ 1

    image.png

Promtail

  1. นำไฟล์ loki.crt ที่ได้ทำการสร้างไว้บน Grafana Loki server ส่งไปที่ Server ที่เราได้ลง Promtail ไว้ ตาม Lab นี้ก็คือ web01 และ db01 แล้วทำการ restart service

    sudo cp /tmp/loki.crt /usr/local/share/ca-certificates/loki.crt
    
    sudo update-ca-certificates
  2. แก้ไข Promtail configuration ทั้ง 2 เครื่อง และ restart service

    /etc/promtail/promtail-config.yaml
    ...
    clients:
      - url: https://<GRAFANA_LOKI_IP_ADDRESS>:8443/loki/api/v1/push
        tenant_id: servers
        basic_auth:
          username: admin
          password: demoLoki
        tls_config:
          ca_file: /usr/local/share/ca-certificates/loki.crt
          insecure_skip_verify: false
    ...
    sudo systemctl restart promtail

Verification

กลับไปที่ Grafana Loki server แล้วทำการเช็ค Log ว่าสามารถเชื่อมต่อไปยังรับ Logs จาก Promtail ได้หรือไม่ หาก return 2xx ทั้งขา read (GET) และ write (POST) เป็นอันใช้งานได้

image.png

Summary

จากที่ได้ทำ Lab กันมาแล้วนั้น จะเห็นได้ว่าเราสามารถทำให้ระบบ Centralized Logs Monitoring ปลอดภัยมากขึ้น แต่ไม่ได้หมายความว่าระบบจะมั่นคงปลอดภัยได้ 100% แต่สิ่งที่เราได้ทำกันใน Lab นี้นั้น มันคือการลดความเสี่ยงที่อาจจะเกิดขึ้นจากผู้ไม่หวังดีที่เข้ามาโจมตีระบบเราได้ครับ

ทั้งนี้ การทำเทคนิคเหล่านี้อาจจะทำได้ยากขึ้น ถ้าหากเรามี Servers ที่ต้องการเก็บ Logs จำนวนมาก เราอาจจะต้องมีการใช้เครื่องมือการช่วยเหลือในการทำ Automation อย่างเช่น Ansible เข้ามาช่วยในการทำ implement ให้ง่ายยิ่งขึ้น (ไว้มาติดตามกันในบทความหน้านะครับ)

References

Last updated on