الإعداد والملفّات الشخصية وActuator

تأمين Actuator وتخصيصه

18 دقيقة الدرس 9 من 13

تأمين Actuator وتخصيصه

يُعدّ Spring Boot Actuator أداةً بالغة النفع في بيئة الإنتاج — غير أنه يكشف افتراضيًا قدرًا كبيرًا من التفاصيل الداخلية لتطبيقك: متغيرات البيئة، وخصائص الضبط، وخريطة الـ beans، وتفريغات الخيوط، وغيرها. ترك هذه النقاط مفتوحة على مصراعيها يُشكّل خطرًا أمنيًا جسيمًا. في هذا الدرس ستتعلّم بالضبط أيّ نقاط تكشف، وكيف تحميها خلف Spring Security، وكيف تُضبط مخرجاتها بحيث لا تكشف إلا ما يحتاجه كل مستهلك فعلًا.

سطح الكشف الافتراضي

تتوفر نقاط Actuator عبر وسيطَين: HTTP وJMX. تختلف سياسات الكشف الافتراضية بينهما بشكل مقصود:

  • HTTP: لا يُكشف إلا عن health وinfo افتراضيًا.
  • JMX: تُكشف جميع النقاط افتراضيًا (وعادةً ما يكون JMX محميًا بالجدار الناري).

القاعدة الأولى في تأمين Actuator هي: اكشف الحد الأدنى الذي تحتاجه فعلًا عبر HTTP. اضبط هذا في application.properties:

# اكشف فقط ما تحتاجه — ابدأ بالتضييق ثم افتح عن قصد management.endpoints.web.exposure.include=health,info,metrics,loggers management.endpoints.web.exposure.exclude=env,beans,heapdump,threaddump,mappings
لا تكشف أبدًا عن env أو heapdump أو shutdown على الإنترنت العام. تُدرج نقطة env كل خاصية محلولة — بما فيها الأسرار التي تتسرب عبر متغيرات البيئة. يحتوي تفريغ الكومة (heap dump) على كامل كومة JVM، ويمكن تحليله لاستخراج كلمات المرور والرموز المميزة وبيانات الجلسات. أما نقطة shutdown فتوقف العملية بالكامل.

نقل Actuator إلى منفذ منفصل

من القرارات المعمارية الجيدة تشغيل Actuator على منفذ مختلف لا يمكن الوصول إليه إلا من داخل شبكتك الخاصة أو VPN. لا يُوجّه موازن التحميل أي حركة خارجية إلى ذلك المنفذ:

management.server.port=8081 management.server.address=127.0.0.1 # ربط بـ localhost فقط

بهذا الضبط يخدّم Actuator على المنفذ 8081 مقيّدًا بالـ loopback. لا يمكن للعملاء الخارجيين الوصول إليه بصرف النظر عن قواعد Spring Security. هذا هو الدفاع على مستوى الشبكة — يعمل حتى لو كان في ضبط الأمان خطأ ما.

طبّق دفاعك بطبقات. استخدم منفذ إدارة خاصًا مع Spring Security معًا. يتولى تقييد المنفذ العزل على مستوى البنية التحتية؛ بينما يتولى Spring Security المصادقة للمستهلكين الداخليين كعوامل المراقبة التي تعمل على الجهاز نفسه.

حماية Actuator بـ Spring Security

حين يشترك Actuator في المنفذ نفسه مع تطبيقك الرئيسي يجب أن تحمي نقاطه بـ Spring Security. أضف الـ starter إن لم يكن موجودًا:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>

ثم اكتب bean من نوع SecurityFilterChain يُطبّق قواعد مختلفة على مسارات Actuator مقارنةً بمسارات التطبيق. في Spring Boot 3 / Spring Security 6 يُعدّ أسلوب lambda DSL هو الطريقة المثلى:

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.core.userdetails.User; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import static org.springframework.security.config.Customizer.withDefaults; @Configuration @EnableWebSecurity public class SecurityConfig { @Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth // فحص الصحة العام — تستخدمه مسابر موازن التحميل .requestMatchers("/actuator/health").permitAll() // كل ما يندرج تحت /actuator يتطلب دور ACTUATOR_ADMIN .requestMatchers("/actuator/**").hasRole("ACTUATOR_ADMIN") // بقية نقاط التطبيق تتطلب مصادقة .anyRequest().authenticated() ) .httpBasic(withDefaults()); return http.build(); } // في الإنتاج: حمّل المستخدمين من قاعدة بيانات أو LDAP، لا من الذاكرة @Bean public UserDetailsService userDetailsService() { var admin = User.withDefaultPasswordEncoder() .username("ops") .password("changeme") .roles("ACTUATOR_ADMIN") .build(); return new InMemoryUserDetailsManager(admin); } }

نقاط جوهرية في هذا الضبط:

  • /actuator/health مسموح به لـ permitAll() كي تعمل مسابر موازن التحميل بدون بيانات اعتماد.
  • جميع مسارات /actuator/** الأخرى تتطلب دور ACTUATOR_ADMIN — الدور المخصص يفصل الوصول إلى Actuator عن صلاحيات الإدارة على مستوى التطبيق.
  • الترتيب مهم في requestMatchers: القاعدة الأكثر تحديدًا /actuator/health يجب أن تسبق الواسعة /actuator/**.

تخصيص نقطة health

يمكن لنقطة health أن تُظهر مستويات تفصيل مختلفة بحسب ما إذا كان المستدعي مُصادقًا. اضبط هذا في application.properties:

# NEVER — لا تظهر تفاصيل أبدًا، فقط UP/DOWN # WHEN_AUTHORIZED — اظهر التفاصيل الكاملة للمستخدمين المصادقين فقط # ALWAYS — اظهر التفاصيل للجميع (لا تستخدم هذا في الإنتاج) management.endpoint.health.show-details=when_authorized management.endpoint.health.show-components=when_authorized

مسبار موازن التحميل غير المصادق يرى فقط:

{ "status": "UP" }

بينما يرى عامل المراقبة المصادق التفاصيل الكاملة بما فيها قاعدة البيانات والقرص ومؤشرات الصحة المخصصة — دون كشف هذه التفاصيل للعالم الخارجي.

تصفية البيانات الحساسة من نقطة env

إن كان يجب كشف env على شبكة داخلية، يُطهّر Spring Boot تلقائيًا قيم الخصائص التي تتطابق مفاتيحها مع قائمة أنماط مدمجة مثل password وsecret وkey وtoken وcredentials، إذ تُستبدل القيمة بـ ****** في الاستجابة. يمكنك توسيع هذه القائمة بأنماطك الخاصة:

management.endpoint.env.additional-keys-to-sanitize=api-key,db-pass,stripe.*
التطهير ليس بديلًا عن التحكم في الوصول. مهاجم متمرس يصل إلى نقطة env قد يحاول إعادة بناء قيمة مُخفاة بالتجربة والخطأ إن كان يعرف صيغتها. اعتبر التطهير خط الدفاع الأخير — خطك الأول هو عدم كشف env أصلًا على شبكة عامة.

كتابة نقطة Actuator مخصصة

أحيانًا لا تغطي النقاط المدمجة ما تحتاجه. يمكنك كتابة نقطتك الخاصة باستخدام @Endpoint (HTTP وJMX معًا) أو @WebEndpoint (HTTP فقط). علّم العمليات بـ @ReadOperation أو @WriteOperation أو @DeleteOperation:

import org.springframework.boot.actuate.endpoint.annotation.Endpoint; import org.springframework.boot.actuate.endpoint.annotation.ReadOperation; import org.springframework.boot.actuate.endpoint.annotation.WriteOperation; import org.springframework.stereotype.Component; import java.time.Instant; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; @Component @Endpoint(id = "releaseinfo") // يُكشف على /actuator/releaseinfo public class ReleaseInfoEndpoint { private final AtomicReference<String> notes = new AtomicReference<>("none"); @ReadOperation public Map<String, Object> releaseInfo() { return Map.of( "version", System.getenv().getOrDefault("APP_VERSION", "unknown"), "deployedAt", Instant.now().toString(), "notes", notes.get() ); } @WriteOperation public void updateNotes(String text) { notes.set(text); } }

يصبح معرّف النقطة (releaseinfo) قسم URL. يُسجّله Spring Boot تلقائيًا ويُطبّق عليه قواعد الأمان ذاتها التي عرّفتها لـ /actuator/**. لا حاجة لأي ربط إضافي.

إعادة تسمية المسار الأساسي لـ Actuator

تغيير المسار الأساسي من /actuator إلى شيء أقل توقعًا يُضيف طبقة تمويه صغيرة لكنها حقيقية — المهاجمون الذين يُشغّلون مسوحات آلية بحثًا عن /actuator/env لن يجدوه:

management.endpoints.web.base-path=/internal/ops

أصبح مسار نقطة health الآن /internal/ops/health. حدّث أي أداة مراقبة أو ضبط مسبار موازن تحميل وفقًا لذلك.

الخلاصة

تأمين Actuator ليس إعدادًا واحدًا — إنه استراتيجية متعددة الطبقات ومدروسة:

  1. الكشف الأدنى: استخدم exposure.include لإدراج النقاط التي تحتاجها فقط.
  2. العزل على مستوى الشبكة: اربط management.server.port بعنوان خاص متى أمكن.
  3. المصادقة مع Spring Security: اجعل /actuator/health عامًا للمسابر وأحمِ كل شيء آخر خلف دور مخصص.
  4. التحكم في مستوى التفاصيل: اضبط show-details=when_authorized كي يرى المستدعون غير المصادقين ملخصًا فقط.
  5. تطهير المفاتيح الحساسة: وسّع قائمة التطهير إن كنت تكشف env داخليًا.
  6. إعادة تسمية المسار الأساسي: يُضيف تمويهًا فوق المصادقة.

بهذه الضوابط في مكانها يصبح Actuator نافذة تشغيلية قوية وآمنة على خدمتك الجارية.