> For the complete documentation index, see [llms.txt](https://www.saladpuk.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://www.saladpuk.com/cloud/cloud-playground/app-config-04.md).

# การป้องกันความลับหลุดตอนจบ

## 😘 ทวนปัญหา

จากบทความที่ 3 เราจะพบว่า **`ความลับ`** ที่ใช้ใน **Production Environment** เราเอามันไป **`ซ่อนไว้บนคลาว์`** ได้แล้วก็จริง แต่การที่เราจะให้แอพของเราไปอ่านความลับพวกนั้นมา เราก็ต้องมอบ `ความลับ` ที่ใช้ในการเชื่อมต่อเข้าสู่คลาว์ให้กับ Developer เห็นอยู่ดี ดังนั้นทุกอย่างมันเลยกลับมาที่จุดเริ่มต้นของปัญหา ดังนั้นเราไปทวนกันก่อนว่าเราต้องแก้ปัญหาเรื่องไยบ้าง

### 💔 รายการที่ต้องแก้

* การผัง Secret ไว้เป็น Hard code ทำให้เราต้อง Build & Deploy โปรเจคใหม่เท่านั้น
* ใน Source code ของมี Secret ติดเข้าไปใน commit เสมอ
* Developer บางคนอาจจะรู้ Secret ซึ่งเขาอาจจะเป็นคนร้ายในอนาคต หรือ เป็นจุดที่อันตรายถ้าเขาเก็บมันไว้ไม่ดี
* ถ้ามีคนที่มี access เข้าถึง service บนคลาว์ตัวนั้นๆได้ เขาก็สามารถเข้ามาดู secret เหล่านั้นได้เหมือนเดิม
* ~~ถ้าเราไม่ให้ access เข้าถึง service ตัวนั้นๆ เราก็จะเอาแอพขึ้นคลาว์ไม่ได้~~

ดูคร่าวๆแล้วเหมือนมันจะยาวเลยเน๊อะ แต่ในรอบนี้เรามี Infrastructure ที่ซ่อนความลับทุกอย่างไว้บนคลาว์เรียบร้อยละ เดี๋ยวมาดูกันว่าเราจะแก้ปัญหาทั้งหมดนี้ด้วยการจัดการเพียงครั้งเดียวได้ยังไงกันเรย

## ❤️ หัวใจหลัก

### 😈 ปัญหา

ปัญหาทั้งหมดทั้งมวลตั้งแต่บนแรกจนถึงบทนี้ + และหลายโปรเจคที่ส่วนใหญ่ทุกคนบนโลกทำกันแล้วมันเกิดปัญหาในการจัดการความลับคือ **เราแยกความลับออกมาแล้วเราต้องจัดการมันเอง** เช่น แยก Connection String, Access Token, Credential หรืออะไรก็ตามออกมาไว้ที่นึง แล้วสุดท้ายเวลาจะให้แอของเรา เช่น เว็บ มือถือ แอพบน PC เชื่อมต่อเข้าไป เราก็จะต้องบริหารจัดการเก็บความลับพวกนั้นเอง เช่นไปเข้ารหัสก่อนจะเอาลงในมือถือ หรืออะไรก็ตามแต่ ดังนั้นสรุปสั้นปัญหาเกิดจาก **`เราต้องจัดการเก็บความลับเอง`** นั่นเอง

### 😘 วิธีแก้ปัญหา

วิธีการแก้นั้นสุดแสนจะง่ายคือทำตรงข้ามกันโดย **`เราจะไม่จัดการเก็บความลับเอง`** นั่นเอง บร๊ะ!! ฟังดูง่ายดีนะ แล้วเราจะทำแบบนั้นได้ยังไง? ดังนั้นไปดูพระเอกของเรากันเลยดีกั่ว

## 🤠 จำกัดการเข้าถึงด้วย Azure Managed Identities

อย่างที่เกริ่นไว้ว่าเราจะไม่จัดการเรื่องการรักษาความลับเอง ซึ่งก็เข้าทางกับเจ้า **`Azure Managed Identities`** โดยเจ้าตัวนี้มีหลักการง่ายๆคือ เขาจะดูแลความลับให้เราเอง โดยจะยอมให้เฉพาะ service ที่เรากำหนดไว้เข้าถึงความลับได้เท่านั้น ส่วนใครก็ตามที่ไม่มีสิทธิ์ก็จะเข้าถึงความลับของเราไม่ได้นั่นเอง ดังนั้นเราไปดูวิธีการตั้งค่าเจ้านี้กันเลยดีกว่า

เข้าไปที่ตัว **`Web App Service`** ที่เราต้องการเข้าใช้งาน **`App Configuration`** (ที่ทำไว้ในบทความที่ 3) ขึ้นมา แล้วที่เมนูด้านซ้ายไปที่ **`Identity`** แล้วทำการเปิดใช้งานมัน แล้วก็กด **`Save`**&#xE15;ามรูปด้านล่าง

![](/files/-M1VD4N-N3nSnhDsfLK2)

ถัดมาเขาก็จะถามว่าจะเปิดใช้งานความสามารถนี้หรือเปล่าก็กด `Yes` ไปแซร๊ะ

![](/files/-LpU-ksrod8iRbVZ1hYT)

> เขาแจ้งเตือนเพราะว่าถ้าเราเปิดโหมดนี้และทำการ register เข้ากับ **AAD (Azure Active Directory)** แล้ว เจ้า service ตัวนี้มันจะเข้าถึงความลับต่างๆของเราได้นั่นเอง ซึ่งถ้าอ่านตรงนี้แล้วไม่เข้าใจก็ยังไม่ต้องกังวล เดี๋ยวเราจะมีอธิบายเรื่อง **AAD** อีกใบบนถัดๆไป

เพียงเท่านี้ก็เปิดการเปิดให้ตัว **`Web App Service`** ของเราพร้อมที่จะเข้าถึงความลับต่างๆของเราละ ตามรูปด้านล่าง

![](/files/-LnrSLzwyrBag1ksmP6y)

ถัดไปเราก็จะไปกำหนดว่าจะให้ **`Service`** ตัวไหนบ้างที่จะเข้าถึง **ความลับ** ที่อยู่ใน `App Configuration` ของเราได้บ้าง โดยการเปิด **`App Configuration Service`** ที่เราสร้างไว้ แล้วให้เข้าไปที่เมนู **`Access control (IAM)`** ที่อยู่ด้านซ้าย แล้วเราจะเจอหน้าตามรูปด้านล่าง

![](/files/-LvryGBMqlUttBtwcZKg)

คราวนี้เราก็จะมากำหนดว่าจะให้ service ไหนบ้างที่จะเข้าถึงได้ โดยการกดปุ่ม **`Add`** แล้วเลือก **`Add role assignment`** ตามรูปด้านล่าง

![](/files/-Lwn1jj9DZ6j0pOZe39E)

สุดท้ายก็ทำการตั้งค่าต่างๆ ตามตารางด้านล่าง

| ชื่อ             | ความหมาย                                                   |
| ---------------- | ---------------------------------------------------------- |
| Role             | สิทธิ์ที่เราจะกำหนดให้                                     |
| Assign access to | จะมอบ security principle รูปแบบไหนให้กับเจ้า role ที่เลือก |

และสุดท้ายก็กดเลือก Service ของเราให้เรียบร้อยแล้วกดปุ่ม **`Save`** ตามรูปด้านล่าง

![](/files/-LrKNcZcmqS8HJkxeZVA)

เพียงเท่านี้ก็เป็นอันเสร็จสิ้นว่า เราได้มอบสิทธิ์ให้กับ `Web App Service` ตัวที่เราเลือกไปตะกี้ ให้สามารถเข้าใช้งานตัว `App Configuration Service` ได้แล้วนั่นเอง

สุดท้ายเราก็จะแก้โค้ดของเรานิดหน่อยให้สามารถเข้าใช้งาน App Configuration Service ได้โดยที่ไม่ต้องระบุ Connection String ใดๆทั้งสิ้นเลย โดยที่ตัว source code ของเราก็ต้องติดตั้ง package ด้านล่างนี้เข้าไปเพิ่มด้วย

```
dotnet add package Azure.Identity --version 1.1.0
```

แล้วที่ไฟล์ `Program.cs` ก็ทำการเพิ่มโค้ดตัวนี้ลงไป

```csharp
using Azure.Identity;
using Microsoft.Azure.Services.AppAuthentication;
```

และในไฟล์เดียวกัน ตรงเมธอด **CreateHostBuilder** ก็ทำการแก้ตามด้านล่างนี้ด้วย (เลือกใช้โค้ดตาม .net version ที่เราใช้ให้ถูกตัวนะ)

{% tabs %}
{% tab title=".NET Core 2.x" %}

```csharp
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
        WebHost.CreateDefaultBuilder(args)
            .ConfigureAppConfiguration((hostingContext, config) =>
            {
                var settings = config.Build();
                config.AddAzureAppConfiguration(options =>
                    options.Connect(new Uri("Endpoint"),
                    new ManagedIdentityCredential()));
            })
            .UseStartup<Startup>();
```

{% endtab %}

{% tab title=".NET Core 3.x" %}

```csharp
public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        webBuilder.ConfigureAppConfiguration((hostingContext, config) =>
        {
            var settings = config.Build();
                config.AddAzureAppConfiguration(options =>
                    options.Connect(new Uri("Endpoint"),
                    new ManagedIdentityCredential()));
            })
            .UseStartup<Startup>());
```

{% endtab %}
{% endtabs %}

จากโค้ดด้านบนก็จะเห็นข้อความที่ชื่อว่า **`Endpoint`** อยู่ชิมิ ซึ่งเจ้าตัวนี้คือตัว URL ของ **App Configuration Service** ที่เราจะเชื่อมต่อเข้าไปนั่นเอง โดยสามารถเข้าไปดูได้ที่เมนู **`Overview`** แล้วมันจะอยู่บนขวา ให้ copy มาแล้ววางใส่ได้เรย ตามรูปด้านล่าง

![](/files/-M1lg3-6O9fXg0CEITge)

หลังจากที่ลองแก้โค้ดเสร็จเรียบร้อย ไหนเรามาลองเปิดเว็บของเราใน Localhost ดูหน่อยจิ๊ ซึ่งผลที่ได้คือ

```
Unhandled exception. Azure.Identity.CredentialUnavailableException:
No managed identity endpoint found.
...
```

เราก็จะพบ error ยาวเป็นหางว่าวตามด้านบนนั่นเอง ซึ่งใจความสำคัญของมันก็คือ ตัวเว็บของเราเชื่อมต่อไปเอาอ่านความลับที่อยู่ใน App Configuration ไม่ได้นั่นเอง ส่วนสาเหตุที่เกิดแบบนี้ขึ้นก็ถูกแล้ว เพราะเครื่องที่ใช้ Run website ตัวนี้ไม่มีสิทธิ์ที่จะเข้าถึง App Configuration Service ของเรายังไงล่ะ (ที่เราตั้งไว้ในขั้นตอนด้านบนไง)

ดังนั้นเราจะลองเอาตัวเว็บของเราไป Deploy ลงบนคลาว์แล้วลองเปิดดูกันบ้างนะ ซึ่งผลลัพท์ก็คือ

![Cloud](/files/-M0OHOo2BpQTAmgeeEvG)

บนคลาว์สามารถใช้งานได้ตามปรกติ!! เพราะเราเปิดสิทธิ์ให้ตัว Service นี้สามารถเข้าถึง App Configuration ได้ยังไงล่ะ

## 🎯 สรุปคร่าวๆก่อนไปต่อ

ในตอนนี้สิ่งที่เราทำลงไปก็จะเห็นว่า **ไม่มี Developer คนไหนสามารถเข้าถึงความลีบได้โดยตรงอีกต่อไปแล้ว** เพราะตัวที่สามารถเข้าถึงความลับได้จะต้องเป็น Service ที่ทำงานอยู่บนคลาว์ และ มันจะต้องถูก Grant สิทธิ์ในการเข้าถึงด้วยเท่านั้นนั่นเอง จากตรงนี้ทำให้เรา **ไม่ต้องแจกความลับของ Production Environment ให้ Developer อีกต่อไปแล้ว**นั่นเอง

ซึ่งสิ่งที่เราทำลงไปนั้น ไม่มีใครเห็นเคย Secret เลย ยกเว้นคนที่ทำ App Configuration (ถ้าหลุดก็เจ้านี่แหละคือคนร้าย) และ ไม่มี Secret ไปอยู่กับโค้ดด้วย ดังนั้นปัญหาทั้งหมดนั่นก็จะมะลายหายไปหมดเลยนั่นเอง

* ~~การผัง Secret ไว้เป็น Hard code ทำให้เราต้อง Build & Deploy โปรเจคใหม่เท่านั้น~~
* ~~ใน Source code ของมี Secret ติดเข้าไปใน commit เสมอ~~
* ~~Developer บางคนอาจจะรู้ Secret ซึ่งเขาอาจจะเป็นคนร้ายในอนาคต หรือ เป็นจุดที่อันตรายถ้าเขาเก็บมันไว้ไม่ดี~~
* ~~ถ้ามีคนที่มี access เข้าถึง service บนคลาว์ตัวนั้นๆได้ เขาก็สามารถเข้ามาดู secret เหล่านั้นได้เหมือนเดิม~~
* ~~ถ้าเราไม่ให้ access เข้าถึง service ตัวนั้นๆ เราก็จะเอาแอพขึ้นคลาว์ไม่ได้~~

เพราะทั้งหมดนี่เราใช้ **Azure Managed Identities** เข้ามาช่วยจำกัดสิทธิ์คนเข้าถึงความลับของเรานั่นเอง

## 😢 แล้วจะพัฒนาแอพต่อไง?

จากตัวอย่างที่ทำไปทั้งหมดจะเห็นว่า เราไม่สามารถเปิด website ของเราภายใน Localhost ของเราได้เลย เพราะมันไม่มีสิทธิ์เข้าถึงความลับ ซึ่งทวนความทรงจำกันหน่อยนึง

1. ความลับของเราจะใช้ที่ไหน???? .... **Production Environment** ไง
2. เราจะให้ Develope เข้าถึง Production Environment ทำไมไม? ... **มีแค่แก้ bug เพียงอย่างเดียว**
3. ถ้าไม่แก้ bug แล้วจะพัฒนาแอพต่อทำไง? ... ไปทำที่ **Development Environment** จิ

จากคำถามคำตอบทั้ง 3 ข้อด้านบน สรุปสั้นๆแบบไม่เมากาวก็คือ เราไม่ควรให้ Developer ทำงานที่ Production ดังนั้นถ้าเราอยากพัฒนาแอพต่อก็ไปสร้าง Development Environment แล้วใช้ Connection String แบบเดิมเชื่อมต่อเข้าไปเหมือนในบนที่ 2 ก็ได้ แล้วพอเอาขึ้น Production ก็แค่ Deploy ลงไปใน Web App Service ที่เรา Grant สิทธิ์ในการเข้าถึง App Configuration Service เท่านั้นยังไงล่ะ

จากที่พิมพ์มาอาจจะมึนๆแต่ผมขอสรุปเป็นภาพด้านล่างง่ายๆประมาณนี้ละกัน

![](/files/-LusN7LElSqF5PyIS8j4)

## 💖 สรุป

จากที่ทำไว้ทั้งหมดเราก็จะสามารถแยกความลับของ Production ออกจาก Environment อื่นๆได้แล้ว โดยเราโยนหน้าที่รับผิดชอบในการดูแลความลับไว้ให้คลาว์มันดูแลเอง และตัว Service ที่เป็นความลับนั้นก็ควรจะแยกไว้ใน Subscription พิเศษที่ไม่ให้ Developer ทั่วไปเข้าถึงได้นั่นเอง

{% hint style="danger" %}
**คำเตือน**\
ในตัวอย่างที่ทำมาทั้งหมดจนถึงบทนี้ ไม่ได้หมายความว่าแบบนี้เป็นแบบที่ดีที่สุด และมันไม่ได้มีแค่แบบเดียว เราควรเลือกใช้แต่ละแบบให้ตรงกับหน้างานที่เจอจะเหมาะสมกว่า
{% endhint %}

{% hint style="success" %}
**Cloud Practices**\
เรื่องการรักษาความลับเป็นส่วนหนึ่งของการทำ Security ของระบบ ซึ่งในการสร้างระบบที่อยู่บนคลาว์เราจะต้องใช้ความรู้ที่เป็นหลักปฎิบัติของคลาว์มาใช้ ซึ่งบางทีหลักปฎิบัติเดิมที่เป็นของ On-premise เองอาจจะไม่เหมาะสมเมื่อเทียบกับของคลาว์ ดังนั้นจะทำอะไรก็ตามให้ดู Cloud Best Practices ของตัว Service เหล่านั้นไว้เสมอ
{% endhint %}
