SimpleIsBest.NET

유경상의 닷넷 블로그

ASP.NET에서 공유 폴더 액세스 (II)

by 블로그쥔장 | 작성일자: 12/6/2005 10:41:00 AM
이 글은 오래된 전에 작성된 글입니다. 따라서 최신 버전의 기술에 알맞지 않거나 오류를 유발할 수 있습니다. 저자는 이 글에 대한 질문을 받지 않을 것입니다. 하지만 이 글이 리뉴얼 되면 이 글에 대한 질문을 하거나 토론을 할 수도 있습니다.
이 포스트 씨리즈는 ASP.NET에서 파일 서버에 액세스할 때 발생하는 다양한 문제들을 해결하기 위한 글입니다. 지난 포스트에서는 공유 폴더에 대한 기초를 다루었고 이번 포스트에서는 실제적으로 코드 상에서 파일 서버의 공유 폴더를 액세스하는 코드를 다루어 볼까 합니다. 사실 공유 폴더를 액세스하는 것은 일반 로컬 디스크의 파일을 액세스하는 것 만큼이나 간단합니다. 그렇지만 이러한 '간단함'은 일단 파일 서버에 로그온이 성공적으로 이루어지고 공유 폴더 및 해당 공유된 파일에 대한 권한을 획득해야만 간단해 지는 것이지요.

이번 글에서는 성공적인 파일 서버에 로그온을 하기 위한 몇몇 준비 사항들을 살펴보고, 설정으로 해결할 수 있는 방법을 살펴보는 것으로 이번 포스트는 마무리 짓고 구체적인 코드를 사용하여 공유 폴더를 액세스하는 최종 예제는 다음 포스트에서 살펴보도록 하겠습니다. 글이 영 재미있게 써지지 않네요. 딴지일보는 몇 문장만 읽어도 실소를 금치 못하는데... 역시 저는 글빨이  많이 딸리나 봅니다... 흑흑

시리즈 목차

Accessing Shared Folder in ASP.NET (II)

공유 폴더를 액세스하기 위해서는 이 씨리즈의 첫 번째 글에서 밝혔듯이, 폴더를 공유하는 파일 서버에 성공적으로 로그온 해야 하며, 해당 공유 폴더 자체에 대한 읽기 혹은 쓰기의 권한이 있어야 한다. 그리고, 구체적인 파일 시스템상의 폴더 혹은 파일에 대해서도 읽기/쓰기 권한이 있어야 한다. 이는 순전히 파일 서버가 자신의 자원(폴더, 파일)을 네트워크를 통해 공유할 때 어떤 계정에 어떤 권한을 주는가에 달려있다고 할 수 있다. 만약 파일 서버가 얼토 당토 않게 자신의 로컬 컴퓨터에만 존재하는 계정에 대해 권한을 주고 암호를 알려주지 않는다면 다른 컴퓨터가 공유 폴더에 접근할 방법은 없게 된다.

Granting Access Right

따라서 파일 서버가 적절한 계정에 적절한 권한을 주는 것이 성공적인 폴더/파일 공유에 있어서 선결과제가 되는 것이다. 이것이 해결되지 않은 상태에서 클라이언트가 아무리 설정을 바꾸거나 프로그램을 잘 짠다 할지라도 말짱 도루묵인 것이다.

Do not use special account !

공유 폴더에 권한을 줄 때는, 특수한 계정을 사용해서는 안 된다. SYSTEM, NETWORK SERVICE, LOCAL SERVICE 계정이 그 대표적인 것인데, 이들에게 공유 폴더의 권한을 주는 것은 폴더를 공유하지 않겠다는 말과도 같다. 이들 계정은 로컬 컴퓨터에서만 유효한 계정이다. 물론 다른 모든 XP 이상의 컴퓨터는 이들 계정을 갖고 있지만 지난 포스트에서 왜 이들 계정이 공유에 사용될 수 없는가를 언급한 바 있다. 다시 한번 구라를 풀어 보면... 파일 서버가 공유 폴더에 SYSTEM에 대해 읽기 쓰기 권한을 주었다고 가정해 보자. 이제 클라이언트도 SYSTEM 계정이 있으니 이 계정 하에서 작동하는 ASP.NET 작업 프로세스, Windows 서비스는 파일 서버에 로그온 할 수 있을 것이고 공유 폴더에도 권한이 있으니 성공할 것이다. 그런가?

결코 아니다. 클라이언트의 SYSTEM 계정이 윈도우 네트워크를 액세스할 때는 NETWORK SERVICE 계정과 마찬가지로 항상 컴퓨터 계정이란 것을 사용한다. 따라서 클라이언트는 서버의 SYSTEM 계정으로 로그온하는 것이 아니라 클라이언트 컴퓨터의 계정으로 서버에 로그온을 시도하는 것이다. 서버와 클라이언트가 액티브 디렉터리로 묶여 있다면 로그온은 성공할지 몰라도, 공유 폴더는 클라이언트 컴퓨터 계정에 대해 권한이 없으므로 공유 폴더에 접근은 오류가 날 것이다.

또 다른 사용해서는 안되는 계정은 ASPNET, IUSR_XXX, IWAM_XXX 등의 계정이다. 이들 계정은 ASP.NET 작업 프로세스의 계정이나, IIS의 익명 사용자를 위한 계정, 그리고 COM+ 서버 타입 프로세스를 위한 계정이다. 이들 계정은 컴퓨터 마다 이름이 다르거나, ASPNET 계정 처럼 계정이 같더라도 각기 암호가 다르다. 따라서 파일 서버에서 이들 계정에 대해 권한을 주는 것 역시 매우 불합리하다는 것이다.

Use Local Group

공유 폴더에 권한을 줄 때 권장되는 방법은 파일 서버의 로컬 그룹을 사용하는 것이다. 먼저 파일 서버에 로컬 그룹을 만들고 이 로컬 그룹에 대해 공유 폴더 그리고 공유 되는 파일 시스템 폴더, 파일들에게 권한을 주는 것이다. 그리고 나서 구체적으로 권한을 허용할 계정을 이 그룹 내에 포함시키는 것이다. 이렇게 로컬 그룹을 사용하면 특정 계정에게 권한을 주거나 회수하는데 용이할 뿐 더러, 액티브 디렉터리를 사용할 때와 그렇지 않을 때에도 동일한 방법으로 권한을 관리할 수 있다는 것이다.

먼저 액티브 디렉터리가 사용되지 않는 경우를 생각해 보자. 로컬 그룹을 먼저 만든다. 그냥 ShareGroup 이라고 가정해 보자. 공유 폴더에 대해 이 그룹이 읽기 혹은 읽기/쓰기 권한을 준다(공유 폴더의 성격에 맞추어 주면 된다). 공유 폴더에 권한을 줄 때, Everyone 그룹에 대해 권한을 주는 경우도 있는데, 이거 무지 않좋은 습관이다. 보안적인 구멍일 뿐 더러, 바이러스를 불러들이는 향긋한 냄새를 풍기는 것이기 때문이다.

물론 파일 시스템의 권한도 이 그룹에 대해 적절한 읽기, 쓰기 권한을 준다. 그룹으로는 로그온이 불가능하므로 구체적으로 공유에 사용할 계정을 만든다. TestUser 라고 가정해 보자. TestUser에는 공유에 꼭 필요한 권한만을 주는 것이 보안상 좋다. 쓸데 없이 Administrators 그룹에 포함시키는 우매한 짓은 하지 말기 바란다. 그리고 암호를 설정한다.


화면1. Windows XP에서 폴더 별로 다른 권한을 주고자 할 때 수행할 설정

혹시나 해서... Windows XP를 파일 서버로 쓰는 경우, 공유 폴더 및 파일 시스템에 권한을 일괄적으로 할당하도록 되어 있는 기본 설정을 바꾸어야 한다. 화면1은 이것을 설정하는 방법을 보여주고 있다.

이제 클라이언트가 공유 폴더에 액세스할 때는 반드시 TestUser 계정을 사용해야만 공유 폴더에 액세스할 수 있게 될 것이다. 물론 사용자가 x탐색기 등을 통해 직접 공유 폴더를 액세스할 때는 TestUser 계정을 사용해야 한다는 것을 알기 때문에 별다른 문제가 없을 것이다. 하지만 ASP.NET, 혹은 기타 '프로그램'이 이 공유 폴더를 액세스할 때에는 반드시 TestUser 계정하에서 작동하는 프로세스 혹은 쓰레드가 필요하다. 이것에 대해서는 나중에 좀더 논하기로 한다.

액티브 디렉토리가 사용될 때에도 동일하게 로컬 그룹(ShareGroup)을 만들고 이 그룹에 대해 권한을 준다. 그리고 공유에 사용할 계정은 도메인 계정을 만든다. 그리고 이 도메인 계정을 파일 서버의 로컬 그룹에 포함시키면 된다.

Accessing Shared Folder in Program

이렇게 공유된 폴더를 액세스하는 것을 생각해 보자. 사람이 탐색기를 통해 공유 폴더에 액세스하는 것은 그다지 어렵지 않다. 왜냐하면 탐색기가 친절하게 현재 사용자 계정으로 파일 서버에 로그온을 시도하고 로그온이 실패할 때에는 로그온 할 사용자 아이디, 패스워드를 물어보기 때문이다. 하지만 프로그램이 공유 폴더를 액세스하는 경우에는 약간 복잡해 진다. 사실 이 글을 쓴 이유도 바로 이 경우를 생각해 보기 위해서 이다.

사용자가 공유 폴더를 액세스하면 현재 사용자의 계정으로 파일 서버에 로그온을 시도한다고 했다. 그렇다면 프로그램이 공유 폴더를 액세스하는 경우에는 어떨까?

Process Account & Thread Account

사용자가 Windows 운영체제 컴퓨터에 로그온 할 때는 사용자 아이디와 패스워드를 입력하도록 되어 있다. 이렇게 로그온 하면 Windows 서비스 등 일부 시스템 프로세스를 제외하고는 대부분의 프로그램들은 이 로그온 된 사용자 계정 하에서 작동하게 된다. 이 말이 무슨 말이냐면, 어떤 사용자(계정)가 갖는 권한을 그 프로세스도 동일하게 갖는다는 말이다. 이렇게 프로세스가 기본적으로 사용하는 사용자 계정을 프로세스 계정이라고 한다(좀 더 정확한 용어로는 프로세스 토큰이지만, 토큰을 설명하자면 또 빡세지므로 걍 넘어가자... -_-;). 프로세스가 어떤 계정 하에서 작동 중인지 가장 쉽게 알아보는 방법은 작업 관리자를 사용하는 것이다.


화면2. 작업 관리자에 나타난 프로세스의 계정 상황

화면2 에서 "사용자 이름" 칼럼이 바로 프로세스가 사용하고 있는 계정을 나타낸다. 이 경우, 탐색기 프로세스(explorer.exe)는 ksyu33 계정 하에서, 그리고 시스템 프로세스들, Windows 서비스 들인 csrss.exe, services.exe, spoolsv.exe 프로세스는 로컬 시스템 계정(SYSTEM)으로 작동 중임을 알 수 있다.

프로세스 내의 쓰레드들은 특별한 작업(나중에 설명할 것이다)을 수행하지 않는 한 프로세스 계정을 그대로 사용하게 된다. 예를 들어, 탐색기 프로세스 내의 쓰레드들은 모두 ksyu33 계정 하에서 작동한다는 말이다. 이것이 오늘 가장 중요한 사항이 되겠다. 어떤 프로그램(코드)가 파일에 접근할 때는 해당 쓰레드가 어떤 계정 하에서 작동하는가에 의해 권한이 있는지 없는지 결정 난다. 해당 계정이 권한이 있다면 코드는 정상적으로 수행될 것이고, 권한이 없다면 쓰레드는 "권한 없음" 오류를 받게 되는 것이다. 이는 쓰레드가 공유 폴더를 액세스할 때도 동일하게 적용된다. 파일이 로컬 디스크에 있는지 원격 공유 폴더에 있는지의 차이가 있을 뿐 파일 액세스는 동일하기 때문이다.

Test

간단한 테스트를 수행해 보자.

using System;

using System.IO;

 

class TestApp

{

    public static void Main()

    {

        FileStream stream = new FileStream(@"\\192.168.2.4\Shared\data.dat", FileMode.Open);

        stream.Close();

    }

};

리스트1. 간단한 파일 액세스 코드. (편의상 FileAccess 라고 하자)

리스트1은 졸라 간단한 코드이다. 단순히 파일서버(IP가 192.168.2.4 이다)의 공유 폴더에서 data.dat 파일을 열었다가 닫는 코드이다. 이 코드가 작동하도록 하기 위해 임의의 파일 서버를 정하고  data.dat 파일을 만들자. 파일 서버는 옆 사람 컴퓨터를 쓰던, 노는 서버를 쓰던 Virtual PC를 사용하던 니 맘대로 하시고... 내용은 암꺼나 집어 넣어도 좋고 0 바이트짜리 허당 파일이여도 상관없다. 그리고 파일 서버에 공유를 위한 ShareGroup 그룹을 만들고 또한 공유에 사용할 TestUser 계정도 만든다(암호는 1234로 한다). TestUser 계정은 파일 서버가 도메인에 포함되어 있다면 도메인 계정으로 만들어야 하고, 그렇지 않다면 로컬 계정으로 만든다. 그리고 나서 TestUser 계정을 ShareGroup 그룹의 멤버로 설정하고, 공유 폴더에 ShareGroup 그룹에 대해 읽기/쓰기 권한을 준다. 그리고 테스트를 위해 반드시 Everyone 계정을 공유 권한에서 제거하도록 하자. 마지막으로 공유된 폴더의 보안 탭에서 ShareGroup이 폴더 및 그 하위 파일/폴더에 읽기 쓰기 권한이 있는지 확인한다.

클라이언트 컴퓨터에서 FileAccess 프로그램(리스트1)을 수행하여 공유 폴더를 액세스해 보자. 테스트 하기 전에 파일 서버에서 클라이언트 컴퓨터가 공유 폴더를 현재 액세스하고 있지 않음을 확인하자. 확인하는 방법은 지난 포스트에서 보였던 파일 공유 세션 모니터링 방법을 사용하면 될 것이다. 세션이 이미 존재한다면 강제로 끊는다. FileAccess 프로그램을 수행시키면 로그온이 실패하거나(IOException 발생) UnauthorizedAccessException 예외를 발생하고 액세스가 거부되었음을 알리는 메시지가 나타날 것이다.

로그온 실패? 무슨 말인지 X 잡고 짱구를 심각하게 돌려보자. 공유 폴더를 액세스하고자 하면 먼저 파일 서버에 로그온을 해야 한다. 파일 서버 로그온에 사용되는 계정은 공유 폴더를 접근하는 코드를 수행하는 쓰레드의 쓰레드 계정이 사용되어 진다. FileAccess.exe 를 수행시키면 현재 로그온 된 사용자(필자의 경우 ksyu33 계정) 계정을 상속받아 FileAccess.exe 프로세스의 프로세스 계정은 ksyu33 가 된다. 그리고 Main 메쏘드를 수행하는 쓰레드 역시 프로세스 계정을 이어받아 쓰레드 계정이 ksyu33이 될 것이다. 이 상황에서 공유 폴더를 액세스하게 되면 쓰레드 계정(ksyu33 계정)을 사용하여 파일 서버에 로그온을 시도한다. ksyu33 계정이 파일 서버에 존재하고 암호가 일치한다면 로그온은 성공할 것이다. 혹은 파일 서버와 클라이언트가 같은 도메인 소속이며 ksyu33 계정이 도메인 계정이라면 파일 서버에 ksyu33 계정이 없더라도 로그온은 성공할 것이다(항상 그렇지는 않다. 파일 서버에서 네트워크 로그온을 거부하도록 설정되어 있다면 로그온은 실패한다). 만약 파일 서버에 ksyu33 계정이 존재하지 않거나, 존재 하더라도 암호가 같지 않거나, ksyu33 계정이 도메인 계정도 아니라면 로그온은 실패하고 공유 폴더 액세스 역시 이 때문에 실패하게 된다.

로그온에 성공한다고 해서 다가 아니다. 해당 공유 폴더의 사용 권한이 클라이언트가 로그온 한 계정에 없다면 역시 액세스 거부 오류가 발생한다. 현재 테스트의 경우 ksyu33 계정으로 로그온을 성공했다 할지라도 공유 폴더에는 파일 서버의 ShareGroup 그룹에 대해서만 사용 권한이 있으므로 ksyu33 계정이 이 그룹에 포함되어 있지 않다면 공유 폴더의 액세스가 실패하는 것이다.

만약 클라이언트가 사용한 계정이 공유 폴더에 권한이 있다고 하더라도 실제 파일 서버의 파일 시스템(NTFS 파일 시스템)을 클라이언트 계정이 액세스할 권한(디렉토리 내용 읽기, 파일 읽기 등)이 없다면 이 역시 액세스 거부를 유발하게 된다.

FileAccess 프로그램이 공유 폴더 액세스를 성공하고자 한다면, 파일 서버에 로그온을 해야 하고 클라이언트가 제시한 계정이 공유 폴더, 그리고 공유된 파일 시스템 폴더에 최소한 읽기 권한이 있어야 한다는 결론이다. 하지만 클라이언트 컴퓨터의 현재 사용자가 ksyu33 계정이라면 클라이언트의 탐색기를 통해 수행되거나 명령 프롬프트에서 수행되는 모든 프로그램은 ksyu33 계정 하에서 수행될 터이니 공유 폴더 액세스가 안될 것 아닌가?

공유 폴더를 액세스 하고자 한다면, 네트워크 드라이브를 연결하던가 아니면 프로세스 혹은 쓰레드의 계정을 바꾸어야 한다. 그리고 그 계정은 파일 서버에 로그온 가능해야 하고, 공유 폴더, 파일 시스템 폴더에 적절한 권한이 있어야 할 것이다. (네트워크 드라이브 연결에 대한 사항은 다른 포스트에서 다루기로 하자)

Changing Process Account

가장 간단한 방법은 프로세스 계정을 바꾸는 것이다. 즉, TestUser 계정으로 FileAccess 프로그램이 작동하도록 만드는 것이다. 이렇게 하기 위해서는 도메인 사용 여부에 따라 작업이 약간 달라진다. 도메인이 사용 중이라면 TestUser 가 이미 도메인 계정일 것이므로 특별히 로컬 컴퓨터에 설정할 필요가 없다. 하지만 도메인을 사용하지 않는다면 클라이언트에도 TestUser 로컬 계정을 만들어야 한다. 그리고 이 계정의 암호는 파일 서버의 TestUser 계정에 사용했던 동일한 암호를 사용해야 한다. 이렇게 해야만 클라이언트의 TestUser 계정이 파일 서버에 로그온 할 수 있다. (졸라 복잡해 보이지만 잘 생각해보면 당연한 얘기 되겠다)

프로세스 계정을 바꾸기 위해서는 로컬 계정 혹은 도메인 계정인 TestUser 로 콘솔에 로그온하고 FileAccess 프로그램(리스트1 코드)를 수행시키면 되겠지만, 우리가 원하는 것은 그것이 아니다. 로그온 하지 않고 프로세스 계정을 바꾸는 방법으로는 Runas 커맨드를 수행하거나 탐색기에서 쉬프트 키를 누른 채로 exe 파일을 오른쪽 클릭하면 나타나는 "다음 계정으로 실행" 메뉴를 이용하거나 CreateProcessAsUser 함수(WIN32 API)를 호출해야 한다. "다음 계정으로 실행" 메뉴는 사용법이 쉬우므로 패스하고, Runas 커맨드 사용법을 예를 들어 보자 (두 방법 모두 CreateProcessAsUser API를 사용한다). 다음 명령 예제는 로컬 계정 TestUser를 사용하여 명령 프롬프트를 수행하는 것이다.

runas.exe /noprofile /user:workman\TestUser cmd.exe

위와 같은 명령을 입력하면 TestUser 의 암호를 묻게 되고, 암호를 입력하면 TestUser 계정 하에서 작동하는 명령 프롬프트가 나타난다(runas의 명령행 옵션은 도움말을 참고하자). 이 명령 프롬프트의 캡션에서 TestUser 계정으로 작동 중임을 확인할 필요가 있다. 그리고 이 명령 프롬프트에서 FileAccess 프로그램을 수행해 보자. 결과는 오류 없이 수행될 것이다! (감격... 감격...)

화면3. Runas 커맨드로 실행한 명령 프롬프트

프로세스가 다른 프로세스를 수행시키면 일반적으로 부모 프로세스의 계정을 상속받게 된다. 따라서 TestUser 계정에서 작동하는 cmd.exe가 다시 FileAccess.exe를 수행시킴에 따라 FileAccess.exe 프로세스 역시 TestUser 계정 하에서 작동하게 되고 파일 액세스는 성공적으로 종료되는 것이다.

Changing Thread Account

프로세스 계정을 바꾸지 않고 쓰레드 계정만 바꾸는 방법은 없을까? 물론 있다. 소위 가장(impersonation)이란 것을 수행하면 쓰레드의 계정이 프로세스 계정에서 주어진 계정으로 바뀐다. 예를 들어 프로세스 계정이 ksyu33 인 프로세스에서 어느 한 쓰레드의 계정만을 TestUser 로 바꿀 수 있는 것이다. 이것을 쓰레드 가장(impersonation)이라고 한다. 가장은 ASP.NET을 비롯한 다수의 서버 응용 프로그램들이 클라이언트가 권한이 있는가를 확인하기 위해 많이 사용되는 기법이다. 가장이 서버에서 어떻게 사용되는가에 대해서는 필자의 글, 닷넷과 IIS Security에서 Impersonation 토픽 부분을 읽어 보기 바란다.

구체적으로 쓰레드의 계정을 바꾸는 impersonation 방법에 대해서는 다음 포스트에서 다루도록 하겠다. 여기서 impersonation을 또 다루자면 너무 길어지기 때문에...

Summary

요약하자면 이렇다. 파일 서버에 공유된 공유 폴더를 액세스하기 위해서는 적절한 계정으로 파일 서버에 로그온 해야 한다. 네트워크 드라이브를 사용하지 않는 한, 공유 폴더에 접근할 때는 현재 쓰레드의 쓰레드 계정이 로그온 계정으로서 사용되어짐에 주목해야 한다. 쓰레드 계정은 일반적으로 프로세스 계정을 그대로 상속하며 프로세스 계정은 대개 현재 콘솔 혹은 터미널에 로그온 된 사용자 계정이 사용된다(정확히 말하자면 쉘, 즉 탐색기 계정을 상속 받는다). 따라서... 프로그램 상에서 공유 폴더에 성공적으로 액세스하려면 쓰레드 계정이 파일 서버에 로그온 가능해야 하고, 공유 폴더가 이 계정에 대해 사용 권한이 있으며, 공유된 파일 시스템 폴더에 대해서도 권한이 있어야 한다는 결론아닌 결론이 쏟아지는 것이다! ( 헥... 헥... -_-; )

대개의 경우, 클라이언트가 어떤 계정을 사용하여 공유 폴더를 액세스할 것인가를 알고 있다면 프로그램적인 공유 폴더 접근에 대해 적절한 권한 설정을 해줄 수 있다. 그렇지만 ASP.NET 작업 프로세스나 COM+ 서버 타입 프로세스, Windows 서비스 프로세스 등의 경우에는 쓰레드가 어떤 계정 하에서 작동하고 있는지 잘 모르기 때문에(빡시게 공부 안 해서 모르는 것이다... -_-) 파일 서버에서 어떤 계정에 권한을 주어야 할지 난감하기 마련이다. 이 때문에 항상 이 게시판 저 게시판에 ASP.NET에서 공유 폴더 액세스가 되네 안되네 하는 질문들이 반복, 또 반복되어 나오곤 한다. 이번에는 확실히 ASP.NET에서의 파일 공유에 대해 꿰어 보도록 하자.

지금까지 공유 폴더에 접근하는 다양한 기본 사항들에 대해 살펴보았으니 다음 포스트에서는 프로그램적으로 쓰레드 계정을 바꾸는 방법을 살펴보도록 하겠다. 아웅.... 졸라 빡시당...



Comments (read-only)
#re: ASP.NET에서 공유 폴더 액세스 (II) / wee / 7/28/2007 11:30:00 PM
jay
#re: ASP.NET에서 공유 폴더 액세스 (II) / oyun / 12/30/2007 6:58:00 AM
Thanks for article
#re: ASP.NET에서 공유 폴더 액세스 (II) / 김병윤 / 9/2/2008 10:38:00 AM
감사할 때가 많습니다. 정말 시원시원하네요.