<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>융융이'Blog</title>
    <link>https://gmldbd94.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 19 Aug 2026 15:02:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>바로퇴장</managingEditor>
    <image>
      <title>융융이'Blog</title>
      <url>https://tistory1.daumcdn.net/tistory/3519630/attach/609581e189164c90a40dab68c0f3d799</url>
      <link>https://gmldbd94.tistory.com</link>
    </image>
    <item>
      <title>매번 같은 설명을 반복하지 않기: AGENTS.md로 AI 프로젝트 규칙 관리하기</title>
      <link>https://gmldbd94.tistory.com/187</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;한눈에 보는 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI와 여러 작업을 이어 가다 보면 매번 같은 설명을 반복하게 된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업을 시작할 때 어떤 문서를 읽어야 하는가?&lt;/li&gt;
&lt;li&gt;비밀정보는 어디에 저장하면 안 되는가?&lt;/li&gt;
&lt;li&gt;설정을 바꾸기 전에 무엇을 확인해야 하는가?&lt;/li&gt;
&lt;li&gt;작업이 끝나면 어떤 기록을 남겨야 하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 문제를 해결하기 위해 프로젝트 루트에 &lt;code&gt;AGENTS.md&lt;/code&gt;를 두고, Codex가 작업 전에 따라야 할 공통 규칙을 기록했다. 환경 정보, 학습 목표와 작업 이력은 각각 별도 Markdown 파일로 분리하고 &lt;code&gt;AGENTS.md&lt;/code&gt;가 작업 시작 시 해당 파일들을 읽도록 연결했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 가장 중요한 점은 다음과 같다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;Codex가 기본 규칙 파일로 자동 탐색하는 것은 &lt;code&gt;AGENTS.md&lt;/code&gt;다. &lt;code&gt;PROJECT_CONTEXT.md&lt;/code&gt; 같은 다른 문서는 이름만 만들어 두면 자동으로 읽는 것이 아니라, &lt;code&gt;AGENTS.md&lt;/code&gt;에서 읽도록 명시해야 한다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 문서에 따르면 Codex는 작업 전에 &lt;code&gt;AGENTS.md&lt;/code&gt;를 읽고, 프로젝트 루트에서 현재 작업 디렉터리까지 지침 파일을 탐색해 결합한다. 현재 디렉터리에 가까운 지침이 앞선 지침보다 우선한다. &lt;a href=&quot;https://learn.chatgpt.com/docs/agent-configuration/agents-md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI Docs: AGENTS.md로 Codex 구성하기&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트 지침 구조&lt;/h2&gt;
&lt;figure&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dna/42hXu/dJMcagT992U/AAAAAAAAAAAAAAAAAAAAAEpztLkxYLC5q3KH8uWeag3Th2deY_mkUs8EWttoWxvf/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1788188399&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=9tLqAPfK6g7Ey7%2BuTHYvfQ%2BCfJA%3D&quot; alt=&quot;AGENTS.md가 프로젝트 컨텍스트와 학습 문서를 연결하고 작업&amp;middot;검증&amp;middot;기록으로 이어지는 흐름도&quot; /&gt;
&lt;figcaption&gt;AGENTS.md를 중심으로 프로젝트 문서가 연결되고 작업, 검증, 기록 갱신으로 이어지는 구조다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 구조에서 &lt;code&gt;AGENTS.md&lt;/code&gt;는 모든 내용을 담는 거대한 설명서가 아니다. 어떤 원칙으로 일하고, 어떤 문서를 언제 참조할지를 정하는 &lt;b&gt;프로젝트 작업의 입구&lt;/b&gt;에 가깝다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;일반 프롬프트와 AGENTS.md의 역할 차이&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI에게 전달하는 정보는 수명이 서로 다르다. 한곳에 모두 섞기보다 다음처럼 나누는 편이 관리하기 쉽다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;구분&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;담을 내용&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;예시&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;현재 요청&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;이번 작업에만 필요한 목표&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&amp;ldquo;로그인 오류의 원인만 진단해 줘&amp;rdquo;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;반복해서 지켜야 할 행동 규칙&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;변경 전 현재 상태 확인, 비밀정보 기록 금지&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;컨텍스트 문서&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;여러 작업이 공유할 비민감 사실&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;사용 중인 시스템, 확정된 설계, 미해결 항목&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;학습 문서&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;목표와 실제 작업 증거&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;로드맵, 실패 원인, 검증 결과, 다음 실험&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;비밀 저장소&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;공개되면 안 되는 인증정보&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;API 키, 비밀번호, 액세스 토큰, 개인 키&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI의 프롬프트 가이드는 요청을 작성할 때 &lt;b&gt;목표(Goal), 맥락(Context), 결과물(Output), 경계(Boundaries)&lt;/b&gt;를 구분하도록 권장한다. &lt;code&gt;AGENTS.md&lt;/code&gt;에도 같은 관점을 적용할 수 있다. 무엇을 해야 하는지뿐 아니라 어떤 변경을 승인받아야 하는지, 어떤 데이터를 기록하면 안 되는지, 완료를 무엇으로 검증할지 함께 적는 방식이다. &lt;a href=&quot;https://learn.chatgpt.com/docs/prompting&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI Docs: Prompting&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Codex가 AGENTS.md를 찾는 방식&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에 설명된 탐색 순서는 크게 두 단계다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 사용자 전체에 적용되는 전역 지침&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex는 사용자 설정 디렉터리에서 먼저 &lt;code&gt;AGENTS.override.md&lt;/code&gt;를 찾고, 없으면 &lt;code&gt;AGENTS.md&lt;/code&gt;를 찾는다. 여기에는 여러 프로젝트에서 공통으로 지킬 개인 작업 방식을 둘 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 현재 프로젝트에 적용되는 지침&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그다음 프로젝트 루트에서 현재 작업 디렉터리까지 내려가며 각 디렉터리의 지침 파일을 찾는다. 기본적으로 같은 디렉터리에서는 &lt;code&gt;AGENTS.override.md&lt;/code&gt;가 &lt;code&gt;AGENTS.md&lt;/code&gt;보다 우선한다. 루트에서 가까운 지침부터 결합되고, 더 안쪽 디렉터리의 지침이 충돌하는 앞선 규칙을 덮어쓴다.&lt;/p&gt;
&lt;figure&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dna/dDlMvj/dJMcagT992T/AAAAAAAAAAAAAAAAAAAAALhefU3kSomE8W56hRNVvAJemYn81azfO7ai4nX_7hdO/img.png?credential=yqXZFxpELC7KVnFOS48ylbz2pIh7yKj8&amp;amp;expires=1788188399&amp;amp;allow_ip=&amp;amp;allow_referer=&amp;amp;signature=CqSsWFd5Ya7EI8SHB6mHcV0cH9k%3D&quot; alt=&quot;사용자 전역부터 프로젝트 루트, 하위 폴더, override 파일까지 Codex 지침 탐색 우선순위&quot; /&gt;
&lt;figcaption&gt;Codex는 전역 지침에서 현재 작업 폴더 방향으로 규칙을 결합하며, 가까운 지침이 우선한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 프로젝트 전체는 Python을 사용하지만 &lt;code&gt;frontend/&lt;/code&gt; 폴더만 TypeScript 규칙을 적용해야 한다면, 루트 파일을 길게 만들기보다 &lt;code&gt;frontend/AGENTS.md&lt;/code&gt;에 해당 규칙을 둘 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 문서에는 결합된 프로젝트 지침의 기본 최대 크기가 32KiB이며 &lt;code&gt;project_doc_max_bytes&lt;/code&gt;로 조정할 수 있다고 안내되어 있다. 크기를 늘릴 수 있더라도 같은 내용을 반복하기보다 핵심 규칙을 간결하게 유지하는 편이 좋다. &lt;a href=&quot;https://learn.chatgpt.com/docs/agent-configuration/agents-md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI Docs: AGENTS.md로 Codex 구성하기&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실제 프로젝트에 사용한 문서 구성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 프로젝트에서는 다음과 같이 역할을 나눴다.&lt;/p&gt;
&lt;pre class=&quot;reasonml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;project/
├── AGENTS.md
├── PROJECT_CONTEXT.md
├── AI_SKILL_ROADMAP.md
├── AI_SKILL_LOG.md
└── CS_STUDY_GUIDE.md&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;: 행동과 판단 규칙&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;작업을 시작할 때 읽어야 할 문서&lt;/li&gt;
&lt;li&gt;설명 수준과 결과 보고 방식&lt;/li&gt;
&lt;li&gt;공식 문서를 확인해야 하는 조건&lt;/li&gt;
&lt;li&gt;변경 전 점검, 변경 후 검증과 승인 기준&lt;/li&gt;
&lt;li&gt;비밀번호&amp;middot;API 키&amp;middot;토큰을 다루는 원칙&lt;/li&gt;
&lt;li&gt;공통 정보와 학습 로그를 갱신하는 조건&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;code&gt;PROJECT_CONTEXT.md&lt;/code&gt;: 여러 작업이 공유할 사실&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;확인된 실행 환경&lt;/li&gt;
&lt;li&gt;이미 결정한 아키텍처와 보안 원칙&lt;/li&gt;
&lt;li&gt;검증을 마친 결과&lt;/li&gt;
&lt;li&gt;아직 확인하지 못한 항목&lt;/li&gt;
&lt;li&gt;변경 이력&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자주 바뀌는 상태를 &lt;code&gt;AGENTS.md&lt;/code&gt;에 계속 추가하면 행동 규칙과 환경 정보가 뒤섞인다. 그래서 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt;, 상태와 결정은 컨텍스트 문서로 분리했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;학습 문서: 목표와 증거&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AI_SKILL_ROADMAP.md&lt;/code&gt;에는 무엇을 어떤 순서로 익힐지 기록하고, &lt;code&gt;AI_SKILL_LOG.md&lt;/code&gt;에는 실제 작업에서 AI가 맡은 역할, 실패와 수정, 검증 결과, 보안&amp;middot;비용과 다음 실험을 기록했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 하면 &amp;ldquo;공부했다&amp;rdquo;는 추상적인 기록 대신 어떤 문제에 어떤 도구와 판단을 사용했고, 무엇으로 결과를 검증했는지가 남는다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;AGENTS.md 예시&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음은 특정 프로젝트의 비밀정보 없이 재사용할 수 있도록 줄인 예시다.&lt;/p&gt;
&lt;pre class=&quot;markdown&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# 프로젝트 작업 지침

## 작업 시작

- 작업 전에 `PROJECT_CONTEXT.md`를 끝까지 읽는다.
- 기존 결정과 실제 환경이 다르면 임의로 덮어쓰지 말고 차이를 설명한다.

## 설명 방식

- 결과를 먼저 설명한다.
- 전문용어는 처음 등장할 때 쉬운 말로 풀이한다.
- 명령에는 목적, 실행 위치, 예상 결과와 되돌리는 방법을 함께 적는다.

## 변경과 검증

- 설정을 바꾸기 전에 현재 상태를 읽기 전용으로 확인한다.
- 변경은 작고 되돌릴 수 있는 단위로 진행한다.
- 삭제&amp;middot;덮어쓰기&amp;middot;권한 변경은 실행 직전에 사용자 확인을 받는다.
- 변경 후 정상 동작과 실패해야 하는 동작을 모두 검증한다.

## 비밀정보

- 비밀번호, API 키, 액세스 토큰과 개인 키를 Markdown이나 소스에 저장하지 않는다.
- 예제에는 `&amp;lt;YOUR_API_KEY&amp;gt;` 같은 자리표시자를 사용한다.

## 기록

- 확정된 비민감 정보는 `PROJECT_CONTEXT.md`에 반영한다.
- 의미 있는 작업은 `AI_SKILL_LOG.md`에 목표, 실패, 검증과 다음 실험을 기록한다.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 예시의 핵심은 &amp;ldquo;잘해 줘&amp;rdquo;처럼 모호한 표현이 아니라, 작업 시작 조건&amp;middot;변경 경계&amp;middot;검증 기준&amp;middot;기록 위치를 관찰 가능한 행동으로 적은 것이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프롬프트 규칙을 설계한 순서&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 반복되는 요구사항을 모았다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;대화할 때마다 다시 설명하게 되는 내용부터 찾았다. 보안 원칙, 설명 수준, 작업 후 문서화처럼 대부분의 작업에 반복해서 적용되는 항목이 대상이었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 규칙과 사실을 분리했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;변경 전에 상태를 확인한다&amp;rdquo;는 규칙이지만 &amp;ldquo;현재 서비스가 어느 버전이다&amp;rdquo;는 사실이다. 전자는 &lt;code&gt;AGENTS.md&lt;/code&gt;, 후자는 &lt;code&gt;PROJECT_CONTEXT.md&lt;/code&gt;에 두었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 권한 경계를 명확히 했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기와 진단은 바로 수행할 수 있지만 삭제, 덮어쓰기, 계정&amp;middot;권한 변경처럼 영향이 큰 작업은 정확한 대상을 먼저 보여 주고 승인받도록 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 완료 조건을 기록했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 만들었다고 끝내지 않고 실제 동작, 실패 경로, 권한, 로그와 결과물을 확인하도록 했다. 중요한 작업에는 사람이 최종 검토하는 단계도 남겼다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 자동 기록의 범위를 제한했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 작은 작업을 문서로 남기면 기록이 빠르게 불어난다. 코드, 설계, 배포, 장애 해결과 보안 설정처럼 재사용 가치가 있는 작업만 학습 로그로 남겼다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;비밀정보는 프롬프트 파일에 넣지 않는다&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;와 프로젝트 Markdown은 AI가 읽기 쉽게 만든 문서다. 따라서 비밀번호, OTP, API 키, 액세스 토큰, 쿠키, 인증서 개인 키를 저장하면 안 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀정보가 필요한 경우에는 다음처럼 분리한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;운영체제 키체인 또는 승인된 비밀 저장소에 실제 값을 저장한다.&lt;/li&gt;
&lt;li&gt;애플리케이션 설정에는 환경변수 이름만 기록한다.&lt;/li&gt;
&lt;li&gt;문서와 예제에는 &lt;code&gt;&amp;lt;YOUR_API_KEY&amp;gt;&lt;/code&gt; 같은 자리표시자를 사용한다.&lt;/li&gt;
&lt;li&gt;명령 출력, 오류 로그와 스크린샷에도 값이 노출되지 않았는지 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;사설 프로젝트이므로 괜찮다&amp;rdquo;는 판단도 피해야 한다. 프로젝트가 Git 저장소에 들어가거나, 로그가 공유되거나, AI 컨텍스트에 포함되면 예상하지 못한 경로로 값이 복사될 수 있기 때문이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;적용 여부를 확인하는 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;파일을 만들었으면 Codex가 지침을 실제로 발견했는지 확인해야 한다. OpenAI 공식 문서는 프로젝트 루트에서 다음과 같이 현재 지침을 요약하게 하는 검증 방법을 안내한다.&lt;/p&gt;
&lt;pre class=&quot;ada&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;codex --ask-for-approval never &quot;Summarize the current instructions.&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;명령의 목적은 실제 작업을 변경하는 것이 아니라 현재 실행에서 어떤 지침을 읽었는지 요약하게 하는 것이다. 결과에는 루트와 하위 폴더의 규칙이 기대한 우선순서로 반영되어야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;지침 파일을 바꿨는데 기존 실행이 이전 내용을 따르는 것처럼 보이면 새 Codex 실행을 시작해 다시 확인한다. 공식 문서에 따르면 지침 체인은 실행 시작 시 구성되므로, 실행 중 변경한 내용을 즉시 다시 읽는다고 가정하면 안 된다. &lt;a href=&quot;https://learn.chatgpt.com/docs/agent-configuration/agents-md&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI Docs: AGENTS.md로 Codex 구성하기&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;검증할 때는 요약만 보지 말고 대표 작업도 작게 시험하는 편이 좋다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비밀 값 대신 자리표시자를 쓰는가?&lt;/li&gt;
&lt;li&gt;변경 전에 현재 상태를 먼저 확인하는가?&lt;/li&gt;
&lt;li&gt;영향이 큰 작업에서 승인을 요청하는가?&lt;/li&gt;
&lt;li&gt;완료 후 지정된 문서만 갱신하는가?&lt;/li&gt;
&lt;li&gt;하위 폴더의 별도 규칙이 필요한 범위에서만 우선하는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;사용하면서 알게 된 실수와 개선점&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;모든 정보를 AGENTS.md 한 파일에 넣기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;규칙, 시스템 상태, 작업 일지와 긴 참고자료를 모두 넣으면 중요한 지침을 찾기 어렵다. &lt;code&gt;AGENTS.md&lt;/code&gt;는 짧은 작업 규칙과 문서 연결에 집중하고, 세부 정보는 목적별 문서로 분리한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;다른 Markdown도 자동으로 읽을 것이라 생각하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex의 기본 탐색 규칙은 &lt;code&gt;AGENTS.md&lt;/code&gt; 계열을 대상으로 한다. 임의로 만든 컨텍스트 파일은 &lt;code&gt;AGENTS.md&lt;/code&gt;에서 읽도록 지시하거나 작업 요청에서 명시해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;실제 인증정보를 편의상 기록하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AI가 자동으로 사용할 수 있다는 장점보다 유출 위험이 훨씬 크다. 문서에는 비밀 값이 아니라 저장 위치와 환경변수 이름만 기록한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;같은 규칙을 여러 위치에서 반복하기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서로 다른 파일에 비슷하지만 미묘하게 다른 규칙이 있으면 어느 지침을 따라야 하는지 불명확해진다. 공통 규칙은 상위 파일에 한 번만 쓰고, 하위 파일에는 차이만 둔다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;행동이 아니라 희망 사항만 적기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;ldquo;안전하게 작업한다&amp;rdquo;보다 &amp;ldquo;설정 변경 전에 현재 상태를 읽기 전용으로 확인하고, 변경 후 권한과 실패 경로를 검증한다&amp;rdquo;처럼 확인 가능한 행동으로 적는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;자동화와 최종 승인을 구분하지 않기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;상태 점검과 변경안 작성은 자동화할 수 있지만 외부 전송, 삭제, 권한 확대처럼 되돌리기 어렵거나 영향 범위가 큰 동작은 사람의 확인 단계를 둔다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배운 점과 다음 단계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;좋은 프로젝트 프롬프트는 길고 화려한 문장이 아니라 &lt;b&gt;반복되는 판단을 일관된 행동으로 바꾸는 운영 규칙&lt;/b&gt;에 가까웠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 구성으로 다음 흐름을 만들었다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;Codex가 &lt;code&gt;AGENTS.md&lt;/code&gt;를 자동으로 찾는다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;AGENTS.md&lt;/code&gt;의 지시에 따라 컨텍스트와 학습 문서를 읽는다.&lt;/li&gt;
&lt;li&gt;현재 요청의 목표와 경계를 확인한 뒤 작업한다.&lt;/li&gt;
&lt;li&gt;변경 후 동작과 보안 경계를 검증한다.&lt;/li&gt;
&lt;li&gt;확정된 공통 정보와 학습 증거를 갱신한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계에서는 하위 프로젝트마다 실제로 다른 규칙이 필요한지 관찰하고, 필요한 경우에만 하위 &lt;code&gt;AGENTS.md&lt;/code&gt;를 추가할 예정이다. 또한 대표 작업을 이용한 지침 준수 점검표를 만들어 규칙을 고친 전후의 행동 차이를 비교해 볼 계획이다.&lt;/p&gt;</description>
      <category>AI 프로젝트 글/실생활 실습</category>
      <category>AGENTS.md</category>
      <category>Codex</category>
      <category>문서화</category>
      <category>자동화</category>
      <category>컨텍스트</category>
      <category>프롬프트</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/187</guid>
      <comments>https://gmldbd94.tistory.com/187#entry187comment</comments>
      <pubDate>Tue, 18 Aug 2026 01:29:26 +0900</pubDate>
    </item>
    <item>
      <title>Synology NAS 사진을 안전하게 AI에 연결하기: 읽기 전용 MCP 구축기</title>
      <link>https://gmldbd94.tistory.com/186</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;한눈에 보는 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Synology NAS의 지정된 테스트 폴더를 Codex 같은 AI 클라이언트가 MCP(Model Context Protocol)로 조회할 수 있도록 읽기 전용 서버를 구축했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서 중요하게 본 것은 단순히 사진을 읽는 기능이 아니었다. 관리자 계정을 사용하지 않고, 허용된 폴더만 읽게 하고, 원본을 수정할 수 없게 만든 뒤 실제 실패 테스트로 이를 검증하는 것이 목표였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종적으로 다음 항목을 확인했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사진 목록 조회&lt;/li&gt;
&lt;li&gt;사진 형식&amp;middot;크기&amp;middot;해상도 조회&lt;/li&gt;
&lt;li&gt;최대 크기가 제한된 JPEG 미리보기 생성&lt;/li&gt;
&lt;li&gt;허용된 폴더 밖으로 나가는 경로 차단&lt;/li&gt;
&lt;li&gt;사진 폴더에 대한 파일 생성 차단&lt;/li&gt;
&lt;li&gt;인증 토큰이 없는 MCP 요청 차단&lt;/li&gt;
&lt;li&gt;새 Codex 프로세스에서 실제 MCP 도구 호출 성공&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;서비스 구성&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;데이터 흐름&lt;/h3&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2026-08-18-photo-mcp-data-flow.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;760&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bwHFEP/dJMcafAWvt1/SfNO5DHvQIoFkxIy3NBrOk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bwHFEP/dJMcafAWvt1/SfNO5DHvQIoFkxIy3NBrOk/img.png&quot; data-alt=&quot;Photo MCP는 허용된 사진 폴더를 읽기 전용으로 조회하며, 사용자가 승인한 결과만 클라우드 모델로 전달한다.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bwHFEP/dJMcafAWvt1/SfNO5DHvQIoFkxIy3NBrOk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbwHFEP%2FdJMcafAWvt1%2FSfNO5DHvQIoFkxIy3NBrOk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;760&quot; data-filename=&quot;2026-08-18-photo-mcp-data-flow.png&quot; data-origin-width=&quot;1600&quot; data-origin-height=&quot;760&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;Photo MCP는 허용된 사진 폴더를 읽기 전용으로 조회하며, 사용자가 승인한 결과만 클라우드 모델로 전달한다.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;사용자가 Codex 같은 AI 클라이언트에서 사진 조회를 요청한다.&lt;/li&gt;
&lt;li&gt;클라이언트는 사설 네트워크의 Photo MCP 컨테이너에 Bearer 토큰과 MCP 요청을 전달한다.&lt;/li&gt;
&lt;li&gt;MCP 서버는 읽기 전용으로 연결된 허용 사진 폴더에서 목록&amp;middot;메타데이터&amp;middot;축소 미리보기만 반환한다.&lt;/li&gt;
&lt;li&gt;사용자가 승인한 결과만 클라우드 모델로 전달될 수 있다.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;MCP 서버가 사설망에 있다는 사실과 사진 내용이 외부로 전송되지 않는다는 것은 같은 의미가 아니다.&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 서버와 사진 폴더는 사설 네트워크 안에 있다. 하지만 사진 미리보기나 추출 결과를 GPT 또는 Claude가 분석하려면 그 데이터는 클라우드 모델로 전송될 수 있다. 따라서 &lt;b&gt;MCP가 사설망에 있다&lt;/b&gt;는 것과 &lt;b&gt;사진 내용이 외부로 전송되지 않는다&lt;/b&gt;는 것은 같은 의미가 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI 공식 문서도 MCP 서버를 모델의 외부 도구와 데이터에 연결하는 수단으로 설명하며, 민감한 도구는 승인 절차로 제한할 수 있다고 안내한다. 사설 서버를 지원되는 OpenAI 제품에 연결할 때는 서버를 인터넷에 직접 공개하지 않고 Secure MCP Tunnel을 사용하는 방법도 제공한다. &lt;a href=&quot;https://developers.openai.com/api/docs/guides/tools-connectors-mcp&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI MCP and Connectors&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표와 요구사항&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 최소 기능 제품의 요구사항은 다음과 같았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;DSM 관리자 또는 마스터 계정을 사용하지 않는다.&lt;/li&gt;
&lt;li&gt;별도의 테스트 사진 폴더만 허용한다.&lt;/li&gt;
&lt;li&gt;목록, 메타데이터, 축소 미리보기 기능만 제공한다.&lt;/li&gt;
&lt;li&gt;삭제, 이동, 업로드, 이름 변경과 원본 덮어쓰기는 제공하지 않는다.&lt;/li&gt;
&lt;li&gt;컨테이너와 데이터 볼륨을 모두 읽기 전용 방향으로 제한한다.&lt;/li&gt;
&lt;li&gt;토큰 인증을 통과한 사설망 클라이언트만 MCP를 호출한다.&lt;/li&gt;
&lt;li&gt;정상 동작뿐 아니라 인증 실패와 쓰기 실패도 검증한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;테스트 환경은 Synology DS224+와 Container Manager였다. 기존 서비스와 충돌하지 않도록 별도의 프로젝트와 디렉터리를 사용했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;설계 결정과 이유&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 관리자 대신 전용 서비스 계정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP 컨테이너에는 NAS 전체에 접근할 수 있는 관리자 권한이 필요하지 않다. 테스트 사진을 읽는 기능만 필요하므로, 관리자 그룹에 속하지 않은 별도 계정을 만들고 해당 공유 폴더에만 읽기 권한을 부여했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이것이 최소 권한 원칙이다. 계정이나 컨테이너가 침해되더라도 접근 가능한 범위를 필요한 만큼으로 줄인다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 세 겹의 읽기 전용 제어&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용은 한 곳에만 설정하지 않았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DSM 공유 폴더 권한: 전용 계정에 읽기만 허용&lt;/li&gt;
&lt;li&gt;Docker bind mount: 사진 폴더를 &lt;code&gt;:ro&lt;/code&gt;로 연결&lt;/li&gt;
&lt;li&gt;MCP 도구 설계: 쓰기&amp;middot;이동&amp;middot;삭제 도구 자체를 제공하지 않음&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한 방어선의 설정이 잘못되더라도 다른 방어선이 원본 변경을 막을 가능성을 높이는 방어 심층화 방식이다.&lt;/p&gt;
&lt;pre class=&quot;yaml&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  photo-mcp:
    read_only: true
    user: &quot;${MCP_RUN_UID}:${MCP_RUN_GID}&quot;
    security_opt:
      - no-new-privileges:true
    cap_drop:
      - ALL
    volumes:
      - &quot;${PHOTO_HOST_PATH}:/data/photos:ro&quot;
    mem_limit: 512m&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 예시는 공개용으로 주소, 실제 경로와 숫자 ID를 일반화한 것이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 작고 명확한 MCP 도구&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 다음 세 도구만 제공한다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;도구&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;반환 내용&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;사진 원본 내용 전송 여부&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&lt;code&gt;list_photos&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;상대 경로, 파일 크기, 수정 시각&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;전송하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&lt;code&gt;get_photo_metadata&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;형식, 해상도, 파일 크기&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;이미지 픽셀은 전송하지 않음&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&lt;code&gt;get_photo_preview&lt;/code&gt;&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;크기를 제한한 JPEG 미리보기&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;선택한 사진의 축소본 전송 가능&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목록 도구에는 최대 반환 개수 제한을 두고, 메타데이터 도구는 위치 정보 같은 EXIF 개인정보를 기본 결과에 포함하지 않았다. 미리보기는 긴 변 기준 최대 1600px로 제한해 원본 전체를 그대로 전달하지 않도록 했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 경로 검증&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 전달한 경로를 그대로 파일시스템에 연결하면 &lt;code&gt;../&lt;/code&gt;을 이용한 경로 순회 공격이나 외부 파일을 가리키는 심볼릭 링크 문제가 생길 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 서버는 다음을 검사한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;절대 경로 거부&lt;/li&gt;
&lt;li&gt;정규화한 최종 경로가 허용된 사진 루트 내부인지 확인&lt;/li&gt;
&lt;li&gt;지원하는 이미지 확장자인지 확인&lt;/li&gt;
&lt;li&gt;심볼릭 링크 파일은 목록에서 제외&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. 비밀정보 분리&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MCP Bearer 토큰은 소스 코드나 Markdown에 저장하지 않았다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;NAS 서버: 프로젝트의 &lt;code&gt;.env&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;로컬 클라이언트: 운영체제 키체인&lt;/li&gt;
&lt;li&gt;Codex 설정: 토큰 값이 아니라 환경변수 이름만 기록&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경변수가 화면에 평문으로 표시되는 관리 UI도 있으므로, 해당 화면은 스크린샷이나 블로그에 포함하지 않는 것이 좋다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;구축 과정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 테스트 데이터 경계 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 가족 사진 전체를 연결하지 않고 별도의 테스트 공유 폴더를 만들었다. 초기 검증 데이터도 JPEG 두 장으로 제한했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 읽기 전용 MCP 서버 구현&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Python 3.12 환경에서 MCP Python SDK와 Pillow를 사용했다. 서버 전송 방식은 Streamable HTTP이며, 토큰 검증에는 일정 시간 비교 함수를 사용했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너는 다음 조건으로 실행했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;비루트 사용자&lt;/li&gt;
&lt;li&gt;읽기 전용 루트 파일시스템&lt;/li&gt;
&lt;li&gt;Linux capability 전체 제거&lt;/li&gt;
&lt;li&gt;&lt;code&gt;no-new-privileges&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;512MB 메모리 제한&lt;/li&gt;
&lt;li&gt;임시 파일은 크기가 제한된 &lt;code&gt;/tmp&lt;/code&gt; tmpfs 사용&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAS 커널에서 Compose의 특정 CPU 제한 방식이 지원되지 않아 CPU 제한은 제거하고 메모리 제한은 유지했다. 기능 지원 여부는 Docker가 설치됐다는 사실만으로 판단하지 않고 실제 NAS 커널과 런타임에서 검증해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. DSM 전용 계정과 컨테이너 UID/GID 연결&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;처음에는 이미지에 정의한 임의의 비루트 UID로 실행했지만, DSM 공유 폴더 ACL과 연결되지 않아 사진을 읽지 못했다. 이후 DSM 전용 서비스 계정의 실제 UID/GID를 확인해 컨테이너 실행 사용자에 매핑했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 과정은 별도의 문제 해결 글로 정리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용: &lt;a href=&quot;https://gmldbd94.tistory.com/185&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker 컨테이너는 실행되는데 Synology 공유 폴더를 읽지 못한 이유&lt;/a&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. Codex에 MCP 등록&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Codex에는 다음 정보만 등록했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사설망의 MCP URL&lt;/li&gt;
&lt;li&gt;Streamable HTTP 방식&lt;/li&gt;
&lt;li&gt;Bearer 토큰이 들어 있는 환경변수 이름&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실제 URL과 토큰은 공개 문서에 포함하지 않는다. 실행 중이던 데스크톱 앱이 새 설정과 환경변수를 읽지 못하면 앱을 재시작해야 할 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;보안과 데이터 경계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;데이터 흐름은 도구별로 다르다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사진 목록 요청: 파일명과 크기 등 목록 정보만 AI 컨텍스트에 포함될 수 있다.&lt;/li&gt;
&lt;li&gt;메타데이터 요청: 형식과 해상도 등이 포함될 수 있다.&lt;/li&gt;
&lt;li&gt;미리보기 요청: 선택한 사진의 축소 이미지가 클라우드 모델에 전달될 수 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 &lt;b&gt;폴더를 MCP에 연결했다는 이유만으로 모든 사진이 한꺼번에 AI로 전송되는 것은 아니다.&lt;/b&gt; 반대로 MCP 도구가 전체 파일을 반환하도록 구현하면 전체 내용이 전송될 수 있으므로, 데이터 최소화는 MCP 프로토콜이 자동으로 해결해 주는 기능이 아니라 서버 설계자의 책임이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;OpenAI API를 직접 사용하는 경우 입력&amp;middot;출력 데이터의 학습 사용 여부, 오남용 모니터링 보관과 애플리케이션 상태 저장 조건은 공식 데이터 제어 문서를 확인해야 한다. ChatGPT&amp;middot;Codex 구독 환경과 Anthropic 제품은 각각 적용되는 계정 및 제품 정책을 별도로 확인해야 한다. &lt;a href=&quot;https://developers.openai.com/api/docs/guides/your-data&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;OpenAI API 데이터 제어&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검증 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기능이 동작하는지만 확인하지 않고 각 보안 경계를 따로 검증했다.&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;검증 항목&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;기대 결과&lt;/th&gt;
&lt;th style=&quot;border: 1px solid #ddd; padding: 8px; background: #f5f5f5;&quot;&gt;실제 결과&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;컨테이너 상태 확인&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;서비스 정상&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;HTTP 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;토큰 없는 MCP 요청&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;인증 거부&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;HTTP 401&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;테스트 사진 목록&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;허용된 파일만 반환&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;JPEG 2장 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;메타데이터&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;형식과 해상도 반환&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;두 파일 모두 4000&amp;times;3000 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;640px 미리보기&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;유효한 축소 JPEG 생성&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;JPEG 시작&amp;middot;종료 바이트 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;사진 폴더 파일 생성&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;실패&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;읽기 전용 파일시스템으로 차단&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&lt;code&gt;../&lt;/code&gt; 외부 파일 접근&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;실패&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;단위 테스트 통과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;외부 대상 심볼릭 링크&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;목록에서 제외&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;단위 테스트 통과&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;새 Codex 프로세스의 도구 호출&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;실제 MCP 결과 수신&lt;/td&gt;
&lt;td style=&quot;border: 1px solid #ddd; padding: 8px;&quot;&gt;&lt;code&gt;count: 2&lt;/code&gt;, &lt;code&gt;read_only: true&lt;/code&gt; 확인&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;단위 테스트, 서비스 통합 테스트, 실제 AI 클라이언트를 통한 종단 간 테스트를 나눠 실행했다. 서버 코드의 함수가 성공하는 것과 AI 클라이언트가 인증&amp;middot;프로토콜&amp;middot;도구 호출 전 과정을 통과하는 것은 서로 다른 검증이기 때문이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;연결되는 CS 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습에서 가장 중요했던 CS 개념은 UID/GID와 ACL이었다. 컨테이너가 비루트로 실행된다는 사실만으로 NAS 파일을 읽을 권한이 생기지는 않는다. 호스트 파일시스템이 확인하는 숫자 사용자 신분과 DSM의 권한 규칙이 일치해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용: &lt;a href=&quot;https://gmldbd94.tistory.com/184&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker와 Synology 권한을 이해하는 UID, GID, ACL&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 밖에도 다음 개념이 연결된다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증과 인가&lt;/li&gt;
&lt;li&gt;bind mount와 읽기 전용 파일시스템&lt;/li&gt;
&lt;li&gt;컨테이너 격리와 capability&lt;/li&gt;
&lt;li&gt;HTTP 상태 코드&lt;/li&gt;
&lt;li&gt;JSON-RPC와 MCP&lt;/li&gt;
&lt;li&gt;단위&amp;middot;통합&amp;middot;종단 간 테스트&lt;/li&gt;
&lt;li&gt;비밀정보 저장과 토큰 회전&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;주요 문제와 해결&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;가장 큰 문제는 컨테이너 자체는 정상 실행되지만 사진 디렉터리 목록 읽기가 거부된 것이었다. 원인은 애플리케이션 코드가 아니라 임의 UID와 DSM ACL의 불일치였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서비스의 상태 확인이 성공한다는 것은 프로세스와 HTTP 서버가 살아 있다는 뜻일 뿐, 마운트한 데이터까지 읽을 수 있다는 뜻은 아니었다. 그래서 상태, 인증, 읽기, 쓰기 차단을 서로 다른 테스트로 분리했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;자세한 내용: &lt;a href=&quot;https://gmldbd94.tistory.com/185&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Docker 컨테이너는 실행되는데 Synology 공유 폴더를 읽지 못한 이유&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;배운 점과 다음 단계&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 실습을 통해 &amp;ldquo;AI에 NAS를 연결했다&amp;rdquo;보다 다음 내용을 설명할 수 있게 됐다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;왜 관리자 계정 대신 최소 권한 서비스 계정을 사용했는가?&lt;/li&gt;
&lt;li&gt;왜 애플리케이션&amp;middot;컨테이너&amp;middot;DSM 세 계층에서 쓰기를 막았는가?&lt;/li&gt;
&lt;li&gt;어떤 데이터가 사설망을 벗어나는가?&lt;/li&gt;
&lt;li&gt;정상 기능뿐 아니라 실패 경로를 어떻게 검증했는가?&lt;/li&gt;
&lt;li&gt;컨테이너와 NAS의 사용자 권한을 어떻게 연결했는가?&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다음 단계는 Word와 Excel 문서를 대상으로 같은 원칙을 적용하는 것이다. 원본 전체를 무조건 보내기보다 NAS 또는 사설망 내부에서 검색&amp;middot;필터&amp;middot;집계를 수행하고, 질문에 필요한 문단이나 셀 범위만 AI에 제공하는 문서 MCP를 설계할 예정이다.&lt;/p&gt;</description>
      <category>AI 프로젝트 글/실생활 실습</category>
      <category>Codex</category>
      <category>docker</category>
      <category>MCP</category>
      <category>synology</category>
      <category>보안</category>
      <category>최소권한</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/186</guid>
      <comments>https://gmldbd94.tistory.com/186#entry186comment</comments>
      <pubDate>Tue, 18 Aug 2026 00:41:25 +0900</pubDate>
    </item>
    <item>
      <title>Docker 컨테이너는 실행되는데 Synology 공유 폴더를 읽지 못한 이유</title>
      <link>https://gmldbd94.tistory.com/185</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;결론&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 정상 실행됐지만 사진 공유 폴더를 읽지 못한 근본 원인은 &lt;b&gt;컨테이너의 임의 UID/GID와 Synology DSM에서 읽기 권한을 받은 서비스 계정의 UID/GID가 일치하지 않았기 때문&lt;/b&gt;이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSM 마스터 계정이나 root로 실행하지 않고, 다음 방식으로 해결했다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;관리자 그룹에 속하지 않은 DSM 전용 서비스 계정을 만든다.&lt;/li&gt;
&lt;li&gt;테스트 사진 공유 폴더에만 읽기 권한을 준다.&lt;/li&gt;
&lt;li&gt;전용 계정의 실제 UID/GID를 확인한다.&lt;/li&gt;
&lt;li&gt;그 값을 Compose의 컨테이너 실행 사용자에 매핑한다.&lt;/li&gt;
&lt;li&gt;읽기 성공과 쓰기 실패를 각각 재검증한다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;핵심은 &amp;ldquo;Permission denied를 없애는 것&amp;rdquo;이 아니라 &amp;ldquo;필요한 사용자만 읽고 누구도 이 경로를 쓰지 못하는 상태&amp;rdquo;를 만드는 것이었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;현상과 영향&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Photo MCP 컨테이너는 시작됐고 HTTP 상태 확인도 성공했다. 하지만 MCP가 마운트된 사진 디렉터리의 목록을 읽으려고 하면 권한 오류가 발생했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 상태에서는 다음과 같은 착시가 생긴다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너 상태: 정상&lt;/li&gt;
&lt;li&gt;HTTP 서버: 정상&lt;/li&gt;
&lt;li&gt;사진 볼륨 마운트: 설정상 존재&lt;/li&gt;
&lt;li&gt;실제 서비스 기능: 사진을 읽지 못해 실패&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;즉, 상태 확인만 보면 서비스가 정상처럼 보이지만 핵심 데이터 접근 기능은 사용할 수 없었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경과 재현 조건&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Synology NAS와 DSM Container Manager&lt;/li&gt;
&lt;li&gt;Python 기반 MCP 서버&lt;/li&gt;
&lt;li&gt;호스트 사진 폴더를 /data/photos:ro로 bind mount&lt;/li&gt;
&lt;li&gt;컨테이너는 비루트 UID로 실행&lt;/li&gt;
&lt;li&gt;DSM 공유 폴더는 별도 서비스 계정에 읽기 권한 부여&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초 이미지에는 일반적인 비루트 실행을 위해 임의 UID가 정의돼 있었다. 그러나 그 UID는 DSM에서 사진 폴더 읽기 권한을 받은 계정의 숫자 ID가 아니었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관찰한 증거&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;문제를 애플리케이션 오류로 단정하지 않고 계층별로 나눠 확인했다.&lt;/p&gt;
&lt;p&gt;확인 대상관찰 결과의미&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;컨테이너 프로세스&lt;/td&gt;
&lt;td&gt;실행 중&lt;/td&gt;
&lt;td&gt;이미지와 시작 명령은 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/health&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;td&gt;HTTP 서버와 포트는 동작&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사진 마운트 설정&lt;/td&gt;
&lt;td&gt;존재&lt;/td&gt;
&lt;td&gt;Compose가 경로를 연결함&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;컨테이너 실행 신분&lt;/td&gt;
&lt;td&gt;임의 비루트 UID/GID&lt;/td&gt;
&lt;td&gt;DSM 전용 계정과 다른 신분&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사진 목록 읽기&lt;/td&gt;
&lt;td&gt;권한 거부&lt;/td&gt;
&lt;td&gt;파일 인가 계층 문제 가능성 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;/health가 성공했다는 사실은 애플리케이션 프로세스가 살아 있다는 증거다. 마운트된 데이터의 읽기 권한까지 보장하는 증거는 아니다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;세운 가설&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설 1. 호스트 경로가 잘못됐다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사진 폴더가 다른 경로에 마운트됐다면 파일이 없거나 디렉터리를 찾지 못할 수 있다. Compose 설정과 컨테이너의 마운트 지점을 확인했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과: 마운트 자체는 존재했으므로 주원인이 아니었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설 2. :ro가 읽기까지 막았다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 전용 마운트라는 이름 때문에 읽기도 막는다고 오해할 수 있다. 하지만 :ro는 쓰기를 차단하는 옵션이며 기존 읽기 권한을 새로 부여하거나 제거하는 기능이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과: 읽기 권한 거부의 직접 원인이 아니었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설 3. MCP 서버의 경로 검증 코드가 차단했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버는 허용된 루트 밖으로 나가는 경로를 거부한다. 잘못된 상대 경로나 확장자를 전달했다면 애플리케이션 수준 오류가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과: 사진 루트 자체의 목록 읽기 단계에서 OS 권한 오류가 발생했기 때문에 주원인이 아니었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;가설 4. 컨테이너 UID/GID와 DSM ACL이 일치하지 않았다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부 프로세스의 숫자 신분과 DSM에서 권한을 받은 계정의 숫자 신분을 비교했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과: 두 값이 달랐고, 이 가설이 실제 원인과 일치했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;진단 과정&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 서비스 상태와 데이터 접근을 분리했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 HTTP 상태 확인으로 애플리케이션 시작 문제를 제외했다. 그다음 사진 디렉터리 목록 읽기를 별도 테스트해 데이터 접근 문제임을 좁혔다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 컨테이너의 실행 신분을 확인했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부에서 id로 UID/GID를 확인했다. 사용자 이름보다 숫자 값이 중요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1786981363290&quot; class=&quot;applescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;id&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. DSM 서비스 계정의 숫자 ID와 비교했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;DSM에서 테스트 폴더 읽기 권한을 받은 전용 계정의 실제 UID/GID를 확인했다. 진단 단계에서 필요한 계정 정보를 읽기 전용으로 확인했으며, 확인을 위해 잠시 사용한 진단용 시스템 파일 마운트는 최종 구성에서 제거했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 권한 문제와 애플리케이션 문제를 구분했다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 경로에 대해 운영체제 수준의 목록 읽기가 실패하면 MCP 도구 코드보다 파일 권한을 먼저 봐야 한다. 반대로 OS 수준 읽기는 성공하는데 도구만 실패하면 경로 검증과 이미지 처리 코드를 확인해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 순서 덕분에 코드 수정을 반복하지 않고 권한 계층으로 원인을 좁힐 수 있었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;근본 원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 이미지에는 보안을 위해 임의의 비루트 사용자가 정의돼 있었다. 비루트라는 방향은 맞았지만, 그 숫자 UID/GID는 Synology DSM의 공유 폴더 ACL에서 읽기 권한을 받은 서비스 계정과 연결되지 않았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;bind mount는 컨테이너에 경로를 보여줄 뿐, 호스트 파일시스템 권한을 무시하지 않는다. 따라서 컨테이너 프로세스는 DSM 입장에서 권한을 받지 않은 숫자 신분으로 사진 폴더에 접근했고, 읽기가 거부됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정리하면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1786981363290&quot; class=&quot;&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;컨테이너 실행 성공 &amp;ne; 마운트 성공 &amp;ne; 파일 읽기 권한 성공&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세 상태는 각각 검증해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;수정 내용&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. DSM 전용 서비스 계정 생성&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마스터 계정을 재사용하지 않고 사진 MCP 전용 계정을 만들었다. 관리자 그룹과 DSM 응용 프로그램 접근은 제외하고, 테스트 사진 공유 폴더에만 읽기 권한을 부여했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 실제 UID/GID를 Compose 변수로 분리&lt;/h3&gt;
&lt;pre id=&quot;code_1786981363290&quot; class=&quot;less&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  photo-mcp:
    user: &quot;${MCP_RUN_UID}:${MCP_RUN_GID}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;NAS마다 UID/GID가 다를 수 있으므로 값을 소스 코드에 고정하지 않고 배포 환경의 .env에서 주입하도록 했다. 공개 문서에는 실제 숫자를 기록하지 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 읽기 전용 방어 유지&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 매핑을 수정했지만 다음 보안 설정은 그대로 유지했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사진 볼륨 :ro&lt;/li&gt;
&lt;li&gt;컨테이너 루트 파일시스템 read_only: true&lt;/li&gt;
&lt;li&gt;Linux capability 전체 제거&lt;/li&gt;
&lt;li&gt;no-new-privileges&lt;/li&gt;
&lt;li&gt;쓰기 MCP 도구 미제공&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 권한 문제를 해결한다는 이유로 root 실행이나 전체 권한 부여로 돌아가지 않았다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;재검증 결과&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;수정 후 다음 순서로 검증했다.&lt;/p&gt;
&lt;p&gt;검증결과&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;컨테이너 실행 UID/GID&lt;/td&gt;
&lt;td&gt;DSM 전용 계정과 대응&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사진 디렉터리 목록 읽기&lt;/td&gt;
&lt;td&gt;성공&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사진 파일 생성 시도&lt;/td&gt;
&lt;td&gt;읽기 전용 파일시스템으로 실패&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;/health&lt;/td&gt;
&lt;td&gt;HTTP 200&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;토큰 없는 /mcp&lt;/td&gt;
&lt;td&gt;HTTP 401&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;사진 목록 MCP 도구&lt;/td&gt;
&lt;td&gt;테스트 사진 2장 반환&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;임시 진단 마운트&lt;/td&gt;
&lt;td&gt;최종 구성에서 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;중요한 점은 읽기 성공만 확인하지 않은 것이다. 권한 변경 과정에서 쓰기까지 가능해지지 않았는지 파일 생성 실패를 별도로 확인했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;실패했던 접근과 이유&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;임의 비루트 UID만 사용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보안 관점에서 root를 피했다는 점은 맞았지만, DSM ACL과 연결되지 않은 UID였기 때문에 실제 데이터 읽기는 불가능했다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;root 실행&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 오류를 빠르게 우회할 수 있지만 최종 해결책으로 채택하지 않았다. 컨테이너가 침해되면 NAS 데이터와 호스트에 미치는 영향이 커질 수 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;공유 폴더 전체 권한 부여&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;chmod 777처럼 광범위한 권한 부여도 사용하지 않았다. 문제를 가리는 대신 공격 표면을 넓히며, 어떤 계정이 실제로 필요한 권한을 가졌는지 설명하기 어려워진다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;읽기 성공만 테스트&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;읽기 성공만으로 완료하면 UID/GID 수정 과정에서 쓰기 권한까지 열렸는지 놓칠 수 있다. 그래서 READ_OK와 WRITE_BLOCKED를 별도의 성공 기준으로 정했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;재발 방지&lt;/h2&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;NAS 배포 전에 전용 서비스 계정과 허용 폴더를 먼저 정의한다.&lt;/li&gt;
&lt;li&gt;다른 NAS로 이동할 때 UID/GID를 그대로 복사하지 않고 다시 확인한다.&lt;/li&gt;
&lt;li&gt;Compose에는 숫자를 직접 고정하지 않고 환경변수로 주입한다.&lt;/li&gt;
&lt;li&gt;배포 검증표에 실행 신분, 읽기 성공, 쓰기 실패를 모두 포함한다.&lt;/li&gt;
&lt;li&gt;상태 확인은 프로세스 상태와 데이터 접근 상태를 구분한다.&lt;/li&gt;
&lt;li&gt;진단을 위해 추가한 마운트나 권한은 원인 확인 후 제거한다.&lt;/li&gt;
&lt;li&gt;root 또는 전체 권한은 최종 해결책으로 남기지 않는다.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 CS 개념&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제의 핵심인 UID, GID, ACL과 bind mount 권한의 관계는 별도 글에서 자세히 설명한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://gmldbd94.tistory.com/184&quot;&gt;Docker와 Synology 권한을 이해하는 UID, GID, ACL&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;관련 프로젝트&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://gmldbd94.tistory.com/186&quot;&gt;Synology NAS 사진을 안전하게 AI에 연결하기: 읽기 전용 MCP 구축기&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI 프로젝트 글/디버깅</category>
      <category>ACL</category>
      <category>docker</category>
      <category>gid</category>
      <category>permissiondenied</category>
      <category>synology</category>
      <category>uid</category>
      <category>트러블슈팅</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/185</guid>
      <comments>https://gmldbd94.tistory.com/185#entry185comment</comments>
      <pubDate>Tue, 18 Aug 2026 00:40:59 +0900</pubDate>
    </item>
    <item>
      <title>Docker와 Synology 권한을 이해하는 UID, GID, ACL</title>
      <link>https://gmldbd94.tistory.com/184</link>
      <description>&lt;h2 data-ke-size=&quot;size26&quot;&gt;한 문장 정의&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;UID와 GID는 Linux가 사용자와 그룹을 구분하는 숫자 신분증이고, ACL은 특정 사용자와 그룹이 파일이나 디렉터리에 어떤 작업을 할 수 있는지 표현하는 권한 목록이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;이 개념이 중요한 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 컨테이너가 정상적으로 실행되고 NAS 폴더가 컨테이너에 마운트됐는데도 Permission denied가 발생할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이때 흔히 다음과 같이 생각하기 쉽다.&lt;/p&gt;
&lt;blockquote data-ke-style=&quot;style2&quot;&gt;&amp;ldquo;컨테이너 안에 mcp라는 사용자가 있고 비루트로 실행했으니 권한 설정이 끝난 것 아닌가?&amp;rdquo;&lt;/blockquote&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;하지만 Linux 파일시스템은 대부분 사용자 이름보다 숫자 UID와 GID를 기준으로 접근 권한을 판단한다. 컨테이너 안의 사용자 이름과 Synology DSM의 사용자 이름이 같아 보여도 숫자 ID가 다르면 같은 신분으로 취급되지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 개념은 NAS뿐 아니라 Kubernetes의 securityContext, NFS, CI 실행 계정, 공유 개발 볼륨과 운영 서버 권한 문제에서도 반복해서 등장한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;동작 원리&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 사용자 이름은 사람이 보기 위한 이름이다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Linux에서 다음 명령을 실행하면 현재 프로세스의 숫자 신분을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1786981342792&quot; class=&quot;applescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 출력의 형태는 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1786981342792&quot; class=&quot;routeros&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;uid=&amp;lt;UID&amp;gt;(service-user) gid=&amp;lt;GID&amp;gt;(service-group) groups=...&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;프로그램이 파일을 열려고 하면 커널은 이 숫자 신분과 파일의 소유권&amp;middot;권한&amp;middot;ACL을 비교한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. Docker의 user 설정은 숫자 신분을 지정한다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Compose에서 다음과 같이 설정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1786981342792&quot; class=&quot;dts&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;services:
  app:
    user: &quot;${RUN_UID}:${RUN_GID}&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 설정은 컨테이너 프로세스가 지정한 UID와 GID로 실행되게 한다. 하지만 이 숫자와 일치하는 DSM 계정을 자동으로 생성하거나 공유 폴더 권한을 자동으로 부여하지는 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. bind mount는 호스트 권한을 없애지 않는다&lt;/h3&gt;
&lt;pre id=&quot;code_1786981342792&quot; class=&quot;avrasm&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;volumes:
  - &quot;${HOST_PATH}:/data/photos:ro&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트의 폴더를 컨테이너에 연결해도 호스트 파일시스템의 소유권과 접근 제어는 그대로 적용된다. 컨테이너 안에서 /data/photos로 보이더라도 실제 접근 허용 여부는 Synology의 파일 권한과 ACL이 결정한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. :ro와 읽기 권한은 다른 문제다&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ro는 마운트된 데이터를 수정하지 못하게 하는 옵션이다. 읽기 권한을 새로 부여하는 옵션이 아니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;따라서 다음 두 조건이 모두 필요하다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;DSM 권한: 서비스 계정이 폴더를 읽을 수 있어야 한다.&lt;/li&gt;
&lt;li&gt;Docker 마운트: 읽을 수는 있지만 쓸 수 없도록 :ro로 연결한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 표로 구분하면 다음과 같다.&lt;/p&gt;
&lt;p&gt;DSM 읽기 권한Docker :ro결과&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;있음&lt;/td&gt;
&lt;td&gt;읽기 실패, 쓰기도 실패&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;있음&lt;/td&gt;
&lt;td&gt;있음&lt;/td&gt;
&lt;td&gt;읽기 성공, 쓰기 실패 &amp;mdash; 이번 프로젝트의 목표&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;있음&lt;/td&gt;
&lt;td&gt;없음&lt;/td&gt;
&lt;td&gt;읽기와 쓰기가 가능할 수 있어 위험 증가&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. POSIX 권한과 ACL&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기본 Linux 권한은 소유자&amp;middot;그룹&amp;middot;기타 사용자에 대해 rwx를 표현한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;r: 파일 내용 또는 디렉터리 목록 읽기&lt;/li&gt;
&lt;li&gt;w: 파일 수정, 생성 또는 삭제&lt;/li&gt;
&lt;li&gt;x: 실행 또는 디렉터리 진입&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ACL은 이 기본 구조보다 세밀하게 특정 사용자나 그룹별 권한을 추가할 수 있다. Synology DSM의 공유 폴더 권한은 이런 접근 제어와 연결되므로, 단순히 컨테이너 안의 ls -l 결과만 보고 모든 권한을 판단하면 놓치는 부분이 생길 수 있다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;프로젝트에서 만난 사례&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Synology 사진 MCP 컨테이너는 처음에 이미지에 정의된 임의의 비루트 UID로 실행됐다. 프로세스와 HTTP 상태 확인은 정상이었지만, 사진 공유 폴더의 목록 읽기는 거부됐다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원인은 다음과 같았다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;컨테이너는 임의 UID로 실행됐다.&lt;/li&gt;
&lt;li&gt;DSM 공유 폴더는 별도의 전용 계정에 읽기 권한을 부여했다.&lt;/li&gt;
&lt;li&gt;두 숫자 신분이 일치하지 않았다.&lt;/li&gt;
&lt;li&gt;마운트는 존재했지만 DSM ACL 관점에서 읽을 수 없는 사용자가 접근했다.&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해결 방법은 컨테이너를 root로 실행하는 것이 아니라, DSM 전용 계정의 실제 UID/GID를 확인해 Compose의 실행 사용자에 매핑하는 것이었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련 실습: &lt;a href=&quot;https://gmldbd94.tistory.com/186&quot;&gt;Synology NAS 사진을 안전하게 AI에 연결하기&lt;/a&gt;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;인증, 인가, 격리를 구분하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이번 문제를 이해하려면 세 개념을 분리해야 한다.&lt;/p&gt;
&lt;p&gt;구분질문프로젝트 예시&lt;/p&gt;
&lt;table style=&quot;border-collapse: collapse; width: 100%;&quot; border=&quot;1&quot; data-ke-align=&quot;alignLeft&quot;&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;인증&lt;/td&gt;
&lt;td&gt;누구인가?&lt;/td&gt;
&lt;td&gt;MCP Bearer 토큰이 올바른가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;인가&lt;/td&gt;
&lt;td&gt;무엇을 할 수 있는가?&lt;/td&gt;
&lt;td&gt;DSM 서비스 계정이 사진 폴더를 읽을 수 있는가?&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;격리&lt;/td&gt;
&lt;td&gt;침해돼도 어디까지 영향을 주는가?&lt;/td&gt;
&lt;td&gt;비루트, :ro, capability 제거&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;올바른 토큰으로 MCP 인증을 통과해도 컨테이너 프로세스의 파일 인가가 실패할 수 있다. 반대로 파일을 읽을 수 있는 프로세스라도 MCP 인증이 없으면 네트워크 요청은 거부해야 한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;자주 혼동하는 개념&lt;/h2&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;ldquo;비루트면 자동으로 안전하다&amp;rdquo;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비루트 실행은 중요하지만 그것만으로 충분하지 않다. 비루트 계정에 지나치게 넓은 NAS 권한을 주면 최소 권한이 아니다. 반대로 권한을 전혀 주지 않으면 서비스가 동작하지 않는다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;ldquo;볼륨이 보이면 읽을 수 있다&amp;rdquo;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;마운트 지점이 존재하는 것과 내부 파일을 읽을 권한이 있는 것은 다르다. mount 또는 컨테이너 설정 확인과 실제 ls&amp;middot;파일 열기 테스트를 분리해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;ldquo;chmod 777이면 해결이다&amp;rdquo;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 오류를 빠르게 없앨 수는 있지만 모든 사용자에게 넓은 권한을 주어 문제의 원인을 숨기고 공격 범위를 키울 수 있다. 운영 환경에서는 어떤 신분에 어떤 권한이 필요한지 확인해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&amp;ldquo;root로 실행하면 해결이다&amp;rdquo;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동작 여부를 잠깐 진단하는 데 사용할 수는 있어도 최종 해결책으로 삼으면 컨테이너 침해 시 피해 범위가 커진다. 이번 프로젝트는 전용 계정의 숫자 ID를 매핑하는 방식으로 해결했다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;간단한 실습&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래 명령은 컨테이너 안에서 실행한다. 실제 서비스 이름과 경로는 자신의 환경에 맞게 바꾼다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. 실행 신분 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1786981342794&quot; class=&quot;applescript&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;id&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적: 컨테이너 프로세스가 예상한 UID/GID로 실행되는지 확인한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. 숫자 소유권과 권한 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1786981342794&quot; class=&quot;haskell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;ls -ldn /data/photos&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;목적: 사용자 이름 변환 없이 숫자 UID/GID와 기본 권한을 확인한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 읽기 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1786981342794&quot; class=&quot;routeros&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;find /data/photos -maxdepth 1 -type f -print -quit&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과: 읽기 권한이 있으면 첫 번째 파일 경로가 출력된다. 아무 파일도 없을 수 있으므로, 테스트 파일이 존재하는 환경에서 실행해야 한다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 쓰기 차단 확인&lt;/h3&gt;
&lt;pre id=&quot;code_1786981342794&quot; class=&quot;irpf90&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;touch /data/photos/write-test.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예상 결과: 읽기 전용 마운트라면 Read-only file system 또는 권한 거부로 실패해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;주의: 쓰기 가능한 운영 폴더에서 실행하면 실제 파일이 생성될 수 있다. 반드시 별도 테스트 폴더와 읽기 전용 마운트에서만 수행한다. 실수로 생성됐다면 대상 경로를 확인한 뒤 테스트 파일만 삭제한다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;운영과 보안 관점&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권한 설계는 다음 순서로 점검하면 이해하기 쉽다.&lt;/p&gt;
&lt;ol style=&quot;list-style-type: decimal;&quot; data-ke-list-type=&quot;decimal&quot;&gt;
&lt;li&gt;자산: 어떤 데이터를 보호하는가?&lt;/li&gt;
&lt;li&gt;신분: 어떤 프로세스가 어떤 UID/GID로 접근하는가?&lt;/li&gt;
&lt;li&gt;인가: DSM에서 어느 폴더에 어떤 권한을 부여했는가?&lt;/li&gt;
&lt;li&gt;격리: 컨테이너가 침해되면 다른 폴더나 호스트 기능에 접근할 수 있는가?&lt;/li&gt;
&lt;li&gt;검증: 읽기는 성공하고 쓰기는 실제로 실패하는가?&lt;/li&gt;
&lt;li&gt;회수: 계정이나 토큰이 유출되면 어떻게 비활성화하고 교체하는가?&lt;/li&gt;
&lt;/ol&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;권장 패턴은 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서비스마다 별도 계정 사용&lt;/li&gt;
&lt;li&gt;관리자 그룹 제외&lt;/li&gt;
&lt;li&gt;필요한 공유 폴더만 허용&lt;/li&gt;
&lt;li&gt;컨테이너 user에 실제 서비스 계정의 UID/GID 매핑&lt;/li&gt;
&lt;li&gt;데이터는 가능한 한 :ro&lt;/li&gt;
&lt;li&gt;root와 chmod 777을 최종 해결책으로 사용하지 않음&lt;/li&gt;
&lt;li&gt;설정 후 성공 경로와 실패 경로를 모두 테스트&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;참고 자료&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;a href=&quot;https://gmldbd94.tistory.com/186&quot;&gt;관련 프로젝트 실습 글&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://gmldbd94.tistory.com/185&quot;&gt;관련 문제 해결 글&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>AI 프로젝트 글/CS 공부</category>
      <category>ACL</category>
      <category>docker</category>
      <category>gid</category>
      <category>Linux</category>
      <category>synology</category>
      <category>uid</category>
      <category>파일권한</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/184</guid>
      <comments>https://gmldbd94.tistory.com/184#entry184comment</comments>
      <pubDate>Tue, 18 Aug 2026 00:40:16 +0900</pubDate>
    </item>
    <item>
      <title>mysql 무중단으로 replication 설정하기</title>
      <link>https://gmldbd94.tistory.com/181</link>
      <description>&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;#[master] 설정
vi /etc/my.cnf
​```
server_id=1 
log-bin=mysql-bin
​```

#값 확인하기
mysql -u root -p

SHOW VARIABLES LIKE 'server_id';

#replication 계정 확인
grant replication slave, replication client on *.* to repl@'%' identified by '패스워드';
Query OK, 0 rows affected (0.000 sec)

select user, host from user;
+------+-----------+
| user | host      |
+------+-----------+
| repl | %         |
| root | 127.0.0.1 |
| root | ::1       |
| root | localhost |
+------+-----------+
4 rows in set (0.000 sec)

flush privileges;
Query OK, 0 rows affected (0.000 sec)


# Master 데이터 덤프 가져오기
mysqldupm -h [masterIP] -u [아이디] -p --single-transaction --master-data=2 --all-databases &amp;gt; masterDB.sql

#[Slave] 설정
vi /etc/my.cnf
​```
[mysqld]
log_bin=mysql.bin                # 추가
server-id=2                      # 추가 (Slave 서버가 여러 대일 경우 각각 다른 번호 할당)
relay_log=mysql-relay-bin        # 추가
log_slave_updates=1              # 추가
read_only=1                      # 추가
​```
mysql -u root -p &amp;lt; masterDB.sql

mysql -u root -p

show databases;
SHOW VARIABLES LIKE 'server_id';

# slave 설정에 필요한 값들 확인
head -30 masterDB.sql
ex&amp;gt;
.... 중략
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql.000001', MASTER_LOG_POS=661;
.... 중략


CHANGE MASTER TO MASTER_HOST='IP', MASTER_USER='repl', MASTER_PASSWORD='패스워드', MASTER_PORT=3306, MASTER_LOG_FILE='mysql.000001', MASTER_LOG_POS=661, MASTER_CONNECT_RETRY=10;

start slave;

show slave status\G;
&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--single-transaction&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;별도의 트랜잭션을 이용하여 DB Lock 없이 Dump를 진행합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--master-data=2&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;생성된 Dump 파일 헤더에 Slave 서버에서 Master를 바라보게 할 때 사용할 값을 남깁니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;--all-databases&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;전체 데이터베이스를 모두 Dump 합니다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 : &lt;a href=&quot;https://blog.naver.com/ncloud24/222384450653&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://blog.naver.com/ncloud24/222384450653&lt;/a&gt;&lt;/p&gt;</description>
      <category>2022이전/Linux</category>
      <category>mysql</category>
      <category>REPLICATION</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/181</guid>
      <comments>https://gmldbd94.tistory.com/181#entry181comment</comments>
      <pubDate>Wed, 15 Sep 2021 18:58:15 +0900</pubDate>
    </item>
    <item>
      <title>mysql 5.6.31 소스설치</title>
      <link>https://gmldbd94.tistory.com/180</link>
      <description>&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;pre class=&quot;jboss-cli&quot;&gt;&lt;code&gt;yum install -y gcc* ncurses-devel cmake

# 소스 설치
cd /usr/local/src/
wget http://dev.mysql.com/get/Downloads/MySQL-5.6/mysql-5.6.31.tar.gz
tar zxvpf mysql-5.6.31.tar.gz
cd mysql-5.6.31

#설치 위치 /usr/local/mysql
#데이터 위치 /data
cmake -DCMAKE_INSTALL_PREFIX=/usr/local/mysql -DMYSQL_DATADIR=/usr/local/mysql/data -DENABLED_LOCAL_INFILE=1 -DWITH_INNOBASE_STORAGE_ENGINE=1 -DMYSQL_UNIX_ADDR=/tmp/mysql.sock -DSYSCONFDIR=/etc -DDEFAULT_CHARSET=utf8 -DMYSQL_TCP_PORT=3306 -DWITH_EXTRA_CHARSETS=all -DDEFAULT_COLLATION=utf8_general_ci

make &amp;amp;&amp;amp; make install

#다른 서버의 설정값 복사
vi /etc/my.cnf 

#계정 생성
groupadd -g 400 mysql
useradd -u400 -g400 -d /usr/local/mysql -s /bin/false mysql

#만약 없다면
cd /usr/local/mysql
cp -arp support-files/my-default.cnf /etc/my.cnf

# 실행 설정
cp -arp support-files/mysql.server /etc/init.d/mysqld

# 초기 설정
/usr/local/mysql/scripts/mysql_install_db --user=mysql --datadir=/data/ --basedir=/usr/local/mysql

chown -R mysql.mysql /usr/local/mysql
chmod 711 /usr/local/mysql
chmod 700 /usr/local/mysql/data
chmod 751 /usr/local/mysql/bin
chmod 750 /usr/local/mysql/bin/*
chmod 755 /usr/local/mysql/bin/mysql
chmod 755 /usr/local/mysql/bin/mysqldump
chmod 700 /etc/init.d/mysqld
chkconfig --add mysqld

#시작하기
systemctl start mysql

#에러시
cat /data/[서버명].err&lt;/code&gt;&lt;/pre&gt;
&lt;h1&gt;&amp;nbsp;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;출처 &lt;a href=&quot;https://yumserv.tistory.com/115&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://yumserv.tistory.com/115&lt;/a&gt;&lt;/p&gt;</description>
      <category>2022이전/Linux</category>
      <category>mysql</category>
      <category>소스설치</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/180</guid>
      <comments>https://gmldbd94.tistory.com/180#entry180comment</comments>
      <pubDate>Wed, 15 Sep 2021 18:56:36 +0900</pubDate>
    </item>
    <item>
      <title>RHCSA 자격증 : YUM 저장소 관리(2일차)</title>
      <link>https://gmldbd94.tistory.com/179</link>
      <description>&lt;h1&gt;&lt;span&gt;YUM 저장소 관리&lt;/span&gt;&lt;/h1&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;명령어 정리&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;yum repolist&lt;span&gt; : 현재 repo 저장소 확인&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;yum clean all&lt;span&gt; : 저장소 설정 적용하기&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;yum verify httpd&lt;span&gt; : httpd 패키지 검증하기&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;yum install yum-downloadonly&lt;span&gt; : rpm 파일만 다운로드(설치 x)&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;=&amp;gt; &lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;repo 저장소 추가하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;span&gt;# vim /etc/yum.repos.d/[아무이름].repo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;============================&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;[myrepo]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;name=myrepo&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;baseurl=http://[주어진 URL]&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;enabled=1&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;gpgcheck=0&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;============================&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;# yum clean all&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&amp;nbsp;&lt;span&gt;&lt;span&gt;​&lt;/span&gt;&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;[저장소 이름]&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;name=저장소 표시이름&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;baseurl=저장소 주소 (Ex] &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;http://url.org/packages/centos/&quot;&gt;http://url.org/packages/centos/&lt;/a&gt;&lt;/span&gt;&lt;span&gt; )&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;enabled=활성화 여부(0 또는 1)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;gpgcheck=gpg 서명키 사용여부(0 또는 1)&lt;/span&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;gpgkey= (서명키를 사용한다면)서명키 주소 입력&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;Repository 인증키 불러오기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;rpm --import &lt;a href=&quot;http://centos.mirror.cdnetworks.com/6/os/x86_64/RPM-GPG-KEY-CentOS-6&quot;&gt;http://centos.mirror.cdnetworks.com/6/os/x86_64/RPM-GPG-KEY-CentOS-6&lt;/a&gt;&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;EPEL 저장소 추가하기&lt;/span&gt;&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;span&gt;yum install epel-release&lt;/span&gt;&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;&lt;span&gt;EMI 저장소&lt;/span&gt;&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;OS 버전별 패키지 파일 URL&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-mark=&quot;-&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;CentOS 5: &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;http://rpms.famillecollet.com/enterprise/remi-release-5.rpm&quot;&gt;http://rpms.famillecollet.com/enterprise/remi-release-5.rpm&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CentOS 6: &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;http://rpms.famillecollet.com/enterprise/remi-release-6.rpm&quot;&gt;http://rpms.famillecollet.com/enterprise/remi-release-6.rpm&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span&gt;CentOS 7: &lt;/span&gt;&lt;span&gt;&lt;a href=&quot;http://rpms.famillecollet.com/enterprise/remi-release-7.rpm&quot;&gt;http://rpms.famillecollet.com/enterprise/remi-release-7.rpm&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&amp;nbsp;&lt;span&gt;rpm -ivh &lt;a href=&quot;http://rpms.famillecollet.com/enterprise/remi-release-7.rpm&quot;&gt;http://rpms.famillecollet.com/enterprise/remi-release-7.rpm&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;</description>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/179</guid>
      <comments>https://gmldbd94.tistory.com/179#entry179comment</comments>
      <pubDate>Sat, 4 Sep 2021 17:36:24 +0900</pubDate>
    </item>
    <item>
      <title>RHCSA 자격증 : root 비밀번호 변경하기(1일차)</title>
      <link>https://gmldbd94.tistory.com/178</link>
      <description>&lt;h1&gt;RHCSA 자격증 : root 비밀번호 변경하기(1일차)&lt;/h1&gt;
&lt;h2&gt;root 비밀번호 분실 했을때&lt;/h2&gt;
&lt;h2&gt;&amp;lt;방법 1&amp;gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;재부팅&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ctrl + alt + delete&lt;/code&gt; &amp;gt; &lt;code&gt;end&lt;/code&gt; 키&amp;gt; &lt;code&gt;e&lt;/code&gt; 키 편집모드 진입&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;설정값 변경 &amp;gt; &lt;code&gt;ctrl + x&lt;/code&gt; 저장 후 재부팅&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#linux16 으로 시작하는 줄 마지막
rd.break&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;비밀번호 변경&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;mount -o rw,remount /sysroot
chroot /sysroot
passwd
touch /.autorelabel
exit
exit&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;&amp;lt;방법 2&amp;gt;&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;재부팅&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ctrl + alt + delete&lt;/code&gt; &amp;gt; &lt;code&gt;end&lt;/code&gt; 키&amp;gt; &lt;code&gt;e&lt;/code&gt; 키 편집모드 진입&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;설정값 변경 &amp;gt; &lt;code&gt;ctrl + x&lt;/code&gt; 저장 후 재부팅&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;#linux16 으로 시작하는 줄 마지막
ro =&amp;gt; rw
rhgb quiet =&amp;gt; init=bin/bash
#변경&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;비밀번호 변경&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;passwd
touch /.autorelabel
exec /bin/init&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;보너스(호스트 명 변경)&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hostnamectl set-hostname [호스트명]&lt;/code&gt;&lt;/pre&gt;</description>
      <category>2022이전/Linux</category>
      <category>hostname</category>
      <category>root</category>
      <category>패스워드 초기화</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/178</guid>
      <comments>https://gmldbd94.tistory.com/178#entry178comment</comments>
      <pubDate>Wed, 25 Aug 2021 19:49:29 +0900</pubDate>
    </item>
    <item>
      <title>rsync 활용하기</title>
      <link>https://gmldbd94.tistory.com/177</link>
      <description>&lt;h3&gt;rsync 사용 시 873포트와 22포트&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;873/TCP를 사용 하겠다는 의미&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-. 데이터를 받은 쪽에서 rsync 서버 (rsyncd / 873/TCP) 서버를 열고 rsync를 받겠다는 의미&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;22/TCP ss를 사용하겠다는 의미&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;-. 22번 포트를 통해 데이터를 전송하겠다는 의미 입니다. 즉 발송하는 쪽과 수신하는 쪽 모두 22/TCP를 사용 합니다.&lt;/p&gt;
&lt;p&gt;-. SSH를 통할 경우 이점은 전송하는 데이터가 ssh의 암호/압축 등의 장점을 사용할 수 있듭니다&lt;/p&gt;
&lt;p&gt;-. 다만 SSH를 사용할 경우 암호화/복호화 과정을 거쳐야 하기 때문에 상대적으로 하드웨어 사양을 조금 더 요구 합니다.&lt;/p&gt;
&lt;h2&gt;rsync 명령의 구문:&lt;/h2&gt;
&lt;p&gt;로컬 동기화 :&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;# 로컬 동기화 : 
rsync {options} {Source} {Destination}

# 원격 동기화 Pull:
rsync {options} &amp;lt;User_Name&amp;gt;@&amp;lt;Remote-Host&amp;gt;:&amp;lt;Source-File-Dir&amp;gt; &amp;lt;Destination&amp;gt;

# 원격 동기화 Push :
rsync &amp;lt;옵션&amp;gt; &amp;lt;Source-Files-Dir&amp;gt; &amp;lt;User_Name&amp;gt;@&amp;lt;Remote-Host&amp;gt;:&amp;lt;Destination&amp;gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;옵션&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;v&lt;/code&gt;: verbosity를 높이는 옵션으로 복사하는 과정을 더 자세하게 보여줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;z&lt;/code&gt;: compress를 주는 옵션으로 파일을 복사할 때 압축해서 복사합니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;h&lt;/code&gt;: 사람이 읽기 쉬운 형태로 복사 결과들을 출력해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;a (same as -rlptgoD)&lt;/code&gt;: archive 모드로 &lt;code&gt;rlptgoD&lt;/code&gt; 옵션을 적용한것과 같습니다. 해당 옵션들은 아래서 설명하며 symlink, 권한 그리고 timestamp와 같은 속성들을 그대로 복사합는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;r&lt;/code&gt;: 디렉토리를 복사할 때 사용하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;l&lt;/code&gt;: symlink는 symlink 형태로 복사하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;p&lt;/code&gt;: 파일과 디렉토리들의 권한을 유지하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;t&lt;/code&gt;: 수정시간을 유지하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;g&lt;/code&gt;: 그룹 속성을 유지하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;o&lt;/code&gt;: 소유자 속성을 유지하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;D (same as --devices --specials)&lt;/code&gt;: &lt;code&gt;-devices --specials&lt;/code&gt;의 옵션과 같습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-devices&lt;/code&gt;: root 권한이 필요하며 Device 관련된 파일들을 복사해서 생성해줍니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-specials&lt;/code&gt;: named socket이나 fifo와 같은 특수한 파일들도 복사하는 옵션입니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;P (same as --partial --progress)&lt;/code&gt;: &lt;code&gt;-partial --progress&lt;/code&gt;의 옵션과 같습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-partial&lt;/code&gt;: rsync는 전송중에 인터럽트가 발생하면 전송하던 파일을 삭제하는게 기본값입니다. 이 옵션을 사용하면 전송된 부분파일을 남기고 다음부분부터 재전송 할 수 있게하여 속도를 빠르게 할 수 있습니다.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-progress&lt;/code&gt;: 전송시 진행상황을 보여줍니다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;예제&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;로컬로 파일 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;`# rsync [File Name] [Target Path]&lt;/p&gt;
&lt;p&gt;rsync -avzhP test.txt /tmp&lt;/p&gt;
&lt;h1&gt;전송시 파일명 변경도 가능&lt;/h1&gt;
&lt;p&gt;rsync -avzhP test.txt /tmp/test-renamed.txt`&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;로컬로 디렉토리 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;`# rsync [Directory Name] [Target Path]&lt;/p&gt;
&lt;h1&gt;디렉토리 자체가 복사된다.&lt;/h1&gt;
&lt;p&gt;rsync -avzhP test-directory /tmp&lt;/p&gt;
&lt;h1&gt;디렉토리 내 파일들과 하위 디렉토리들 복사&lt;/h1&gt;
&lt;h1&gt;rsync [Directory Name]/ [Target Path]&lt;/h1&gt;
&lt;p&gt;rsync -avzhP test-directory/ /tmp`&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;원격에 파일 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;`# rsync [File Name] [User]@[IP Address]:[Path]&lt;/p&gt;
&lt;p&gt;rsync -avzhP test.txt twpower-private-server:~&lt;br&gt;rsync -avzhP test.txt &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:~&lt;/p&gt;
&lt;h1&gt;전송시 파일명 변경도 가능&lt;/h1&gt;
&lt;p&gt;rsync -avzhP test.txt twpower-private-server:&lt;del&gt;/test-renamed.txt&lt;br&gt;rsync -avzhP test.txt &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:&lt;/del&gt;/test-renamed.txt`&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;원격에 디렉토리 복사&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;`# rsync [Directory Name] [User]@[IP Address]:[Path]&lt;/p&gt;
&lt;h1&gt;디렉토리 자체가 복사된다.&lt;/h1&gt;
&lt;p&gt;rsync -avzhP test-directory twpower-private-server:~&lt;br&gt;rsync -avzhP test-directory &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:~&lt;/p&gt;
&lt;h1&gt;디렉토리 내 파일들과 하위 디렉토리들 복사&lt;/h1&gt;
&lt;h1&gt;rsync [Directory Name]/ [User]@[IP Address]:[Path]&lt;/h1&gt;
&lt;p&gt;rsync -avzhP test-directory/ twpower-private-server:~&lt;br&gt;rsync -avzhP test-directory/ &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:~`&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;원격에 있는 파일 로컬로 가져오기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;`# rsync [User]@[IP Address]:[File Name] [Path]&lt;/p&gt;
&lt;p&gt;rsync -avzhP twpower-private-server:&lt;del&gt;/test.txt .&lt;br&gt;rsync -avzhP &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:&lt;/del&gt;/test.txt .&lt;/p&gt;
&lt;h1&gt;전송시 파일명 변경도 가능&lt;/h1&gt;
&lt;p&gt;rsync -avzhP twpower-private-server:&lt;del&gt;/test.txt ./test-renamed.txt&lt;br&gt;rsync -avzhP &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:&lt;/del&gt;test.txt ./test-renamed.txt`&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;원격에 있는 디렉토리 로컬로 가져오기&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;`# rsync [User]@[IP Address]:[Directory Name] [Path]&lt;/p&gt;
&lt;h1&gt;디렉토리 자체가 복사된다.&lt;/h1&gt;
&lt;p&gt;rsync -avzhP twpower-private-server:&lt;del&gt;/test-directory .&lt;br&gt;rsync -avzhP &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:&lt;/del&gt;/test-directory .&lt;/p&gt;
&lt;h1&gt;디렉토리 내 파일들과 하위 디렉토리들 복사&lt;/h1&gt;
&lt;h1&gt;rsync [User]@[IP Address]:[Directory Name]/ [Path]&lt;/h1&gt;
&lt;p&gt;rsync -avzhP twpower-private-server:&lt;del&gt;/test-directory/ .&lt;br&gt;rsync -avzhP &lt;a href=&quot;mailto:twpower@192.168.1.2&quot;&gt;twpower@192.168.1.2&lt;/a&gt;:&lt;/del&gt;/test-directory/ .`&lt;/p&gt;
&lt;h2&gt;원격 rsync 자세히 보기&lt;/h2&gt;
&lt;p&gt;rsync는 &lt;strong&gt;873번 포트&lt;/strong&gt;를 사용하며, 두 서버간의 데이터 백업을 위해서 주로 사용된다.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;master서버에서 rsync데몬을 띄우고, backup 서버에서 master서버에 접근하여 허용된 디렉토리의 데이터를 백업하는 방식&lt;/strong&gt;과, &lt;strong&gt;ssh를 이용하여 데이터를 미러링 하는 방법&lt;/strong&gt; 두가지가 있다.&lt;/p&gt;
&lt;p&gt;테스트 환경&lt;/p&gt;
&lt;p&gt;master서버 : 10.30.100.60&lt;/p&gt;
&lt;p&gt;backup서버 : 10.30.100.61&lt;/p&gt;
&lt;h2&gt;rsync데몬(873포트)을 이용하는 방법&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1./etc/rsyncd.conf 파일 생성&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;/etc/rsyncd.conf 파일은 기본적으로 생성되어 있지 않기 때문에 아래와 같은 형식으로 생성 한다.&lt;/p&gt;
&lt;p&gt;==============================================&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@master ~]# cat /etc/rsyncd.conf
[add4s]
comment =add4s home directoryapplication/views/common/chatbot.php
path = /home/add4s/public_html
uid = root
gid = root
use chroot = yes
read only = yes
hosts allow = 10.30.100.61
max connection = 2
timeout = 300&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;[add4s] --&amp;gt; 서비스명&lt;/p&gt;
&lt;p&gt;&lt;code&gt;comment = add4s home directory&lt;/code&gt; --&amp;gt; 서비스에 대한 코멘트&lt;/p&gt;
&lt;p&gt;&lt;code&gt;path = /home/add4s/public_html&lt;/code&gt; --&amp;gt; 서비스 디렉토리(백업 디렉토리)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uid = root&lt;/code&gt; --&amp;gt; 접속 user 권한&lt;/p&gt;
&lt;p&gt;&lt;code&gt;gid = root&lt;/code&gt; --&amp;gt; 접속 group 권한&lt;/p&gt;
&lt;p&gt;&lt;code&gt;use chroot = yes&lt;/code&gt;  --&amp;gt; 서비스 디렉토리를 최상위 디렉토리로 사용&lt;/p&gt;
&lt;p&gt;&lt;code&gt;read only = yes&lt;/code&gt; --&amp;gt; 읽기 전용으로 설정&lt;/p&gt;
&lt;p&gt;&lt;code&gt;hosts allow = 10.30.100.61&lt;/code&gt;  --&amp;gt; 접속을 허용할 호스트(백업서버의 ip)&lt;/p&gt;
&lt;p&gt;&lt;code&gt;max connection = 2&lt;/code&gt;  --&amp;gt; 최대 접속 가능 수&lt;/p&gt;
&lt;p&gt;&lt;code&gt;timeout = 300&lt;/code&gt;  --&amp;gt; 클라이언트에서 접근시 타임아웃시간&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.2. master서버에서 rsync데몬 실행&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;/etc/xinetd.d/rsync 파일에서 disable = no 로 변경하고 xinetd 데몬을 실행한다. 데몬이 실행되면, 873포트가 open된 것을 확인 할 수 있다.&lt;/p&gt;
&lt;p&gt;ubunt계열의 Linux에서는 /etc/default/rsync 파일에서 RSYNC_ENABLE=true 로 변경하고 rsync 데몬을 실행한다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@master ~]# /etc/init.d/xinetd start

Starting xinetd: [  OK  ]

[root@master ~]# netstat -nltp

Active Internet connections (only servers)

Proto Recv-Q Send-Q Local Address               Foreign Address             State       PID/Program name

tcp        0      0 0.0.0.0:22                  0.0.0.0:*                   LISTEN      1242/sshd

**tcp        0      0 :::873                      :::*                        LISTEN      7022/xinetd**

tcp        0      0 :::22                       :::*                        LISTEN      1242/sshd

[root@master ~]#&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1.3. &lt;strong&gt;iptables 873 포트 접속 허용&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;iptables 방화벽에 873 포트 접속이 가능하도록 포트를 허용한다.&lt;/p&gt;
&lt;p&gt;================================================================================&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@master ~]# iptables -L

Chain INPUT (policy ACCEPT)

target     prot opt source               destination

ACCEPT     all  --  anywhere             anywhere            state RELATED,ESTABLISHED

ACCEPT     icmp --  anywhere             anywhere

ACCEPT     all  --  anywhere             anywhere

ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:ssh

ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:http

ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:https

**ACCEPT     tcp  --  anywhere             anywhere            state NEW tcp dpt:rsync**

REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT)

target     prot opt source               destination

REJECT     all  --  anywhere             anywhere            reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT)

target     prot opt source               destination&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;1.4. backup서버에서 master서버의 rsync 서비스와 comment 확인하기&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;backup서버에서 아래와 같은 방법으로 master서버에서 허용된 서비스와 comment를 확인할 수 있다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@backup ~]# rsync 10.30.100.60::

add4s            add4s home directory&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;1.5. 백업 테스트&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;master서버에서 파일 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@master ~]# cd /home/add4s/public_html

[root@master public_html]# touch testfile1 testfile2 testfile3

[root@master public_html]# ls -l

total 0

- rw-r--r-- 1 root root 0 Apr 24 01:14 testfile1
- rw-r--r-- 1 root root 0 Apr 24 01:14 testfile2
- rw-r--r-- 1 root root 0 Apr 24 01:14 testfile3

[root@master public_html]#

==============================================================================

- backup서버에서 rsync명령으로 업데이트(master서버에서 생성된 파일이 백업된 것을 확인할 수 있다.)

아래는 master서버에서 허용된 서비스의 디렉토리와 backup서버의 /backup/add4s 디렉토리를 동기화 시키는 작업이다.

==============================================================================

[root@backup ~]# rsync -avzr --delete 10.30.100.60::add4s /backup/add4s/

receiving incremental file list

./

testfile1

testfile2

testfile3

sent 86 bytes  received 200 bytes  52.00 bytes/sec

total size is 0  speedup is 0.00

[root@backup ~]# cd /backup/add4s

[root@backup add4s]# ls -l

total 0

- rw-r--r-- 1 root root 0 Apr 24 2009 testfile1
- rw-r--r-- 1 root root 0 Apr 24 2009 testfile2
- rw-r--r-- 1 root root 0 Apr 24 2009 testfile3

[root@backup add4s]#

==============================================================================

- master서버에서 file 삭제

==============================================================================

[root@master public_html]# rm -rf testfile2 testfile3

[root@master public_html]# ls -l

total 0

- rw-r--r-- 1 root root 0 Apr 24 01:14 testfile1

[root@master public_html]#

==============================================================================

- backup서버에서 rsync명령으로 업데이트(master서버에서 삭제한 파일이 같이 삭제 된것을 확인할 수 있다.)

==============================================================================

[root@backup add4s]# rsync -avzr --delete 10.30.100.60::add4s /backup/add4s/

receiving incremental file list

deleting testfile3

deleting testfile2

./

sent 29 bytes  received 82 bytes  20.18 bytes/sec

total size is 0  speedup is 0.00

[root@backup add4s]# ls -l

total 0

- rw-r--r-- 1 root root 0 Apr 24 2009 testfile1
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;1.6. cron에 등록하여 주기적으로 백업&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;아래와 같은 방법으로 backup서버의 cron에 등록을 하면 원하는 시간에 주기적으로 데이터를 미러링 하게 된다.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@backup add4s]# crontab -l

00 02 * * * /usr/bin/rsync -avzr --delete 10.30.100.60::add4s /backup/add4s/&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;ssh를 이용하여 rsync사용하기&lt;/h2&gt;
&lt;p&gt;ssh를 이용하는 경우는 아래와 같이 -e ssh 옵션(생략가능)을 추가해서 rsync 명령을 사용하면 된다. 그리고, master서버와 backup서버 둘다 명령을 수행 할 수 있다.&lt;/p&gt;
&lt;p&gt;마찬가지로 아래의 명령을 스크립트로 만들어서 사용하거나, cron에 등록해서 주기적인 데이터 미러링 작업이 가능하다.&lt;/p&gt;
&lt;p&gt;2.1. master서버에서 rsync 명령으로 백업&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@master ~]# rsync -avzr --delete -e ssh /home/add4s/public_html/ root@10.30.100.61:/backup/add4s/

The authenticity of host &amp;#39;10.30.100.61 (10.30.100.61)&amp;#39; can&amp;#39;t be established.

RSA key fingerprint is 4a:42:46:c9:55:48:63:08:a5:84:0a:69:d8:a6:47:0e.

Are you sure you want to continue connecting (yes/no)? yes

Warning: Permanently added &amp;#39;10.30.100.61&amp;#39; (RSA) to the list of known hosts.

root@10.30.100.61&amp;#39;s password:

sending incremental file list

sent 67 bytes  received 12 bytes  10.53 bytes/sec

total size is 0  speedup is 0.00&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;2.2. slave서버에서 rsync 명령으로 백업&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;[root@backup ~]# rsync -avzr --delete -e ssh root@10.30.100.60:/home/add4s/public_html/ /backup/add4s/

root@10.30.100.60&amp;#39;s password:

receiving incremental file list

sent 11 bytes  received 68 bytes  8.32 bytes/sec

total size is 0  speedup is 0.00
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;참조&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://blueyikim.tistory.com/562&quot;&gt;[명령어] rsync 이해&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.sharedit.co.kr/posts/1579&quot;&gt;rsync를 사용하여 원격으로 데이터 백업 하기&lt;/a&gt;&lt;/p&gt;</description>
      <category>2022이전/Linux</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/177</guid>
      <comments>https://gmldbd94.tistory.com/177#entry177comment</comments>
      <pubDate>Wed, 11 Aug 2021 11:08:35 +0900</pubDate>
    </item>
    <item>
      <title>SSL 기초 개념</title>
      <link>https://gmldbd94.tistory.com/176</link>
      <description>&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; width=&quot;100%&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/TkcJx/btrbKFkjJYU/4245pWIKdcWrMiCHruhXw0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/TkcJx/btrbKFkjJYU/4245pWIKdcWrMiCHruhXw0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/TkcJx/btrbKFkjJYU/4245pWIKdcWrMiCHruhXw0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FTkcJx%2FbtrbKFkjJYU%2F4245pWIKdcWrMiCHruhXw0%2Fimg.png&quot; width=&quot;100%&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2&gt;용어 이해하기&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;암호를 만드는 행위 암호화&lt;/li&gt;
&lt;li&gt;암호화에 사용되는 비밀번호 = 키(key)&lt;/li&gt;
&lt;li&gt;암호를 푸는 행위 복호화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;대칭키&lt;/h2&gt;
&lt;p&gt;동일한 키로 암호화 복호화를 같이 할 수 있는 방식&lt;/p&gt;
&lt;p&gt;ex) &lt;/p&gt;
&lt;p&gt;암호화 : A ⇒ &lt;code&gt;1234&lt;/code&gt; ⇒ A`&lt;/p&gt;
&lt;p&gt;복호화 : A&lt;code&gt;⇒&lt;/code&gt;1234` ⇒ A&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;실습코드&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;echo &amp;#39;this is the plain text&amp;#39; &amp;gt; plaintext.txt;
openssl enc -e -des3 -salt -in plaintext.txt -out ciphertext.bin;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;enc -e -des3 : des3 방식으로 암호화 함&lt;/li&gt;
&lt;li&gt;in plaintext.txt -out ciphertext.bin : plaintext.txt 파일을 암호화 한 결과를 ciphertext.bin 파일에 저장함&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;openssl enc -d -des3 -in ciphertext.bin -out plaintext2.txt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;enc -d 옵션으로 인해서 ciphertext.bin 파일을 plaintext2.txt 파일로 복호화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;공개키&lt;/h2&gt;
&lt;p&gt;공개키(public key) : 공개키를 통해 암호화 가능 / 서버로 부터 받음&lt;/p&gt;
&lt;p&gt;비공개키(private key) : 비공개키를 통해 복호화 가능 / 서버만 소유&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;실습코드&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;openssl genrsa -out private.pem 1024;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;1024bit 길이의 비공개키생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;openssl rsa -in private.pem -out public.pem -outform PEM -pubout;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;private.pem 기반의 public.pem 공개키 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;echo &amp;#39;coding everybody&amp;#39; &amp;gt; file.txt
openssl rsautl -encrypt -inkey public.pem -pubin -in file.txt -out file.ssl;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;file.txt를 공개키로 암호화 &amp;amp; file.ssl 암호화된 파일 생성&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-powershell&quot;&gt;openssl rsautl -decrypt -inkey private.pem -in file.ssl -out decrypted.txt&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;file.ssl 암호화된 파일 private.pem을 이용하여 복호화&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;SSL 인증서&lt;/h2&gt;
&lt;h3&gt;CA (certificate authority)&lt;/h3&gt;
&lt;p&gt;공인된 기업이 인증서 증명하는 기관&lt;/p&gt;
&lt;h3&gt;사설 인증기관&lt;/h3&gt;
&lt;p&gt;사설에서 인증서 증명 / 인터넷에서 확인 안됨&lt;/p&gt;
&lt;p&gt;SSL 인증서 내용&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;서버시의 정보 (인증서를 발급한 CA, 서비스의 도메인 등등)&lt;/li&gt;
&lt;li&gt;서버 측 공개키 (공개키의 내용, 공개키의 암호화 방법)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;실제 대이터 : 대칭키&lt;/p&gt;
&lt;p&gt;대칭키의 키 : 공개키&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;악수 ⇒ 전송 ⇒ 세션종료&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;1. 악수 (handshake)&lt;/h3&gt;
&lt;p&gt;인사 할때마다 공개키 주고 &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;SSL 인증서를 주고 받는다.&lt;/li&gt;
&lt;li&gt;비공개키를 사용하기에는 부하가 커서 사용 x&lt;/li&gt;
&lt;li&gt;공개키와 대칭기 혼합한 방법&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;클라이언트 ⇒ 서버 &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;랜덤 데이터 주기(암호화 방식 확인)&lt;/li&gt;
&lt;li&gt;세션 아이디 (기존 연결 이용)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;서버 ⇒ 클라이언트&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;랜덤 데이터 주기(암호화 방식 확인)&lt;/li&gt;
&lt;li&gt;인증서 전달(클라이언트 내장된 CA의 공개키 용하여 인증서 복호화 및 확인)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;랜덤 데이터 : &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;조합하여 pre master secret 라는 키 생성(대칭키 이용)&lt;/li&gt;
&lt;li&gt;클라이언트 ⇒ 서버 : pre master secret 서버의 공개키(인증서에 포함)로 암호화 전송&lt;/li&gt;
&lt;li&gt;서버 : 암호화된 pre master secret 을 자신의 비공개키로 복호화&lt;/li&gt;
&lt;li&gt;일련의 과정을 통해 pre master secret ⇒ master secret 값으로 만든다.&lt;/li&gt;
&lt;li&gt;master secret는 session key를 생성하고 이를 대칭키 방식으로 이용&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;2. 세션(session key)&lt;/h3&gt;
&lt;p&gt;앞서 말한 방식으로 생긴 session key를 대칭키로 이용하여 데이터를 주고 받는다.&lt;/p&gt;
&lt;h3&gt;3. 세션 종료&lt;/h3&gt;
&lt;p&gt;session key를 폐기한다.&lt;/p&gt;
&lt;h2&gt;인증서의 활용&lt;/h2&gt;
&lt;p&gt;CA 관리 기관을 통해 발급&lt;/p&gt;
&lt;p&gt;ssl.key : 서버쪽 비공개키&lt;/p&gt;
&lt;p&gt;ssl.crt : 디지털 인증서&lt;/p&gt;
&lt;p&gt;ca.pem : ROOT CA 인증서&lt;/p&gt;
&lt;p&gt;sub.class1.server.ca.pem : 중계자 인증서&lt;/p&gt;
&lt;p&gt;실습코드&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;powershell
sudo apt-get install apache2;
sudo a2enmod ssl
sudo service apache2 restart
sudo mkdir /etc/apache2/ssl

/etc/apache2/ssl/ca.pem
/etc/apache2/ssl/ssl.crt
/etc/apache2/ssl/ssl.key
/etc/apache2/ssl/sub.class1.server.ca.pem

sudo chown -R root:root /etc/apache2/ssl;
sudo chmod 600 /etc/apache2/ssl/*.*
sudo chmod 700 /etc/apache2/ssl;

sudo nano /etc/apache2/sites-available/default-ssl

SSLCertificateFile    /etc/apache2/ssl/ssl.crt
SSLCertificateKeyFile /etc/apache2/ssl/ssl.key
SSLCertificateChainFile /etc/apache2/ssl/sub.class1.server.ca.pem
SSLCACertificateFile /etc/apache2/ssl/ca.pem


sudo a2ensite default-ssl&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;참조 :&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://opentutorials.org/course/228/4894&quot;&gt;https://opentutorials.org/course/228/4894&lt;/a&gt;&lt;/p&gt;</description>
      <category>2022이전/Linux</category>
      <author>바로퇴장</author>
      <guid isPermaLink="true">https://gmldbd94.tistory.com/176</guid>
      <comments>https://gmldbd94.tistory.com/176#entry176comment</comments>
      <pubDate>Wed, 11 Aug 2021 09:22:36 +0900</pubDate>
    </item>
  </channel>
</rss>