Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

Azure VM/App Services(Web Apps)에 Let's Encrypt 무료 인증서 적용 방법

Azure 서비스에도 물론 Let's Encrypt 무료 인증서를 적용할 수 있습니다.

우선, 예전의 "Cloud Services (classic)" 유형의 웹 애플리케이션이라면 다음의 글에 따라 적용해야 합니다.

"Let's Encrypt" SSL 인증서를 Azure Cloud Services(classic)에 업데이트하는 방법
; https://www.sysnet.pe.kr/2/0/11054

주의할 것은, 3개월마다 저렇게 인증서 배포 작업을 해야 합니다.

반면, 가상 머신(VM)을 통해 웹 서비스를 하는 경우라면 일반적인 IIS 웹 서버와 동일하게 해당 VM에 RDP로 로그인한 후 다음의 글에 따라 설정하시면 됩니다.

"Let's Encrypt"에서 제공하는 무료 SSL 인증서를 IIS에 적용하는 방법 (2)
; https://www.sysnet.pe.kr/2/0/11483

이 경우, "Cloud Services (classic)"과는 다르게 VM 내부에 설정된 작업 스케줄러를 통해 인증서 갱신 작업을 자동화하기 때문에 사용자가 인증서 만료를 걱정할 필요가 없습니다.

마지막으로, Azure의 "App Services" 유형에 대해 "Let's Encrypt" 인증서를 적용하는 방법을 알아볼 텐데요. "Web App"의 경우 기본적으로 다음과 같은 도메인 이름을 사용할 수 있습니다.

http://[your_service_name].azurewebsites.net

마이크로소프트는 *.azurewebsites.net에 대한 인증서를 제공하므로 https 통신을 별도의 인증서 비용 없이 무료로 제공받게 됩니다. 따라서 여러분의 서비스가 다음과 같은 URL로 접근하는 것이 허용된다면 더 이상의 부가 작업은 필요 없습니다.

https://[your_service_name].azurewebsites.net




하지만 대개의 경우 자사의 고유한 도메인 이름이 있을 것이기 때문에 그것과 연결하게 될 것입니다.

Azure Web Apps(App Services)에 사용자 DNS를 지정하는 방법
; https://www.sysnet.pe.kr/2/0/11494

그다음, 저렇게 등록한 DNS에 대해 SSL 인증서를 설치해야 하는데요. 원래 App Services는 PaaS 유형의 서비스이기 때문에 "Cloud Services (classic)" 유형과 마찬가지로 인증서를 쉽게 적용할 수 없는 구조입니다. 그런데, "App Services" 유형에는 마이크로소프트가 특별히 Azure Site 확장 갤러리를 통해 외부 모듈들을 설치할 수 있는 기능을 제공하고 있기 때문에 Cloud services보다 더 쉽게 SSL 인증서를 관리하도록 만들 수 있습니다.

실제로 "Let's Encrypt" 인증서를 App Services에 연동할 수 있도록 해주는 '확장'이 이미 배포되고 있습니다. 따라서, Azure Portal에서 여러분의 App Service로 들어가 "개발 도구" 의 "확장"을 선택한 후,

app_services_lets_encrypt_1.png

"추가" 버튼을 누르면 다음 화면과 같이 "Azure Let's Encrypt"를 선택할 수 있습니다.

app_services_lets_encrypt_2.png

그다음, "고급 도구" 메뉴로 들어가 "이동"을 눌러,

app_services_lets_encrypt_3.png
(또는, 곧바로 https://[webapp_name].scm.azurewebsites.net 주소로 곧바로 이동할 수 있습니다.)

Kudu 화면의 "Site extensions"에서 "Azure Let's Encrypt"를 시작해 줍니다.

app_services_lets_encrypt_4.png

그럼 다음 화면과 같이 "Authentication Settings" 화면으로 넘어갑니다.

app_services_lets_encrypt_5.png

넣어야 하는 값이 좀 익숙하죠?

  • Tenant
  • SubscriptionId
  • ClientId
  • ClientSecret
  • ResourceGroupName
  • ServicePlanResourceGroupName
  • UseIPBasedSSL
  • SiteSlotName

이 중에서 Tenant, SubscriptionId는 Login-AzureRmAccount 명령어로 출력된 값을 사용하면 됩니다.

PS C:\> Login-AzureRmAccount

Account          : kevin2@jennifersoft.com
SubscriptionName : Pay-As-You-Go
SubscriptionId   : 4183b166-e0ee-493d-a7cb-addb11e39a0b
TenantId         : 1f4ab031-6c95-4c8e-800b-7f8c9094375e
Environment      : AzureCloud

그다음 ClientId, ClientSecret 값은 "Azure Let's Encrypt" 확장이 사용할 App 계정을 하나 만들어야 합니다.

Azure - PowerShell로 Access control(IAM)에 새로운 계정 만드는 방법
; https://www.sysnet.pe.kr/2/0/11479

C# - API를 사용해 Azure에 접근하는 방법
; https://www.sysnet.pe.kr/2/0/11480

New-AzureRmADServicePrincipal로 생성한 계정의 clientSecret, key 값을 구하는 방법
; https://www.sysnet.pe.kr/2/0/11501

위의 글에 따라 간략히 정리해 보면 다음과 같이 새로운 계정과 권한 부여를 하고,

PS> $plainText = "testpassword"
PS> $secureString = ConvertTo-SecureString $plainText -AsPlainText -Force
PS> $sp = New-AzureRmADServicePrincipal -DisplayName "NewUser1" -Password $secureString
PS> New-AzureRmRoleAssignment -ServicePrincipalName $sp.ApplicationId -RoleDefinitionName Contributor

PS> $sp | Select DisplayName, ApplicationId

DisplayName     ApplicationId
-----------     -------------
NewUser1     c5129bac-96f4-43fe-b5d7-dee8ce7ed2a0

출력된 ApplicationId == c5129bac-96f4-43fe-b5d7-dee8ce7ed2a0 값을 ClientId로 사용하면 됩니다. 그다음 ClientSecret 값은 "New-AzureRmADServicePrincipal로 생성한 계정의 clientSecret, key 값을 구하는 방법" 글에 따라 Azure Portal의 "App registrations(앱 등록)" 메뉴에서 등록해 구할 수 있습니다.

그 외 나머지 값은 상황에 따라 넣으면 됩니다.

ResourceGroupName: Web App(App Service)이 속한 리소스 그룹의 이름
ServicePlanResourceGroupName: App Service와 그것이 속한 Service Plan의 리소스 그룹이 다르다면 지정
                
UseIPBasedSSL: IP 기반 SSL을 쓸 일이 없으니.
SiteSlotName: 도움말 참조(https://github.com/ohadschn/letsencrypt-webapp-renewer)

설정을 완료한 다음, "Update Application Settings" 체크 박스를 누르고 "Next" 버튼을 눌러 진행합니다. 그럼, 다음의 화면으로 넘어갑니다.

app_services_lets_encrypt_6.png

Next를 한번 더 눌러서 진행하면, 다음과 같이 SSL 인증서의 이름으로 사용할 DNS를 묻는 단계로 갑니다.

app_services_lets_encrypt_7.png

Hostnames 목록에서 해당 도메인을 선택하고 이메일을 입력한 후 "Request and Install certificate" 버튼을 눌러 진행하면 인증서 발급 및 설치 절차가 완료됩니다. 이후로는 "https://jpublictest.sysnet.pe.kr" URL로 접속하면 정상적인 서비스가 되는 것을 확인할 수 있습니다.

참고로, 마지막 단계에서 "UseStaging" 옵션을 체크하고 "Request and Install certificate" 버튼을 누르는 것을 추천합니다. 이것은 테스트 용 인증서를 발급하고 설치까지 잘 진행되는지 확인하는 효과를 갖습니다. 이 작업의 의미는 아래에서 다룹니다.




Let's encrypt 확장 설정 시, ServicePlanResourceGroupName을 비워놓고 진행할 때 다음과 같은 오류가 발생했습니다.

The Service Plan Resource Group registered on the Web App in Azure in the ServerFarmId property 'myservice' does not match the value you entered here

이상하군요. "Optional"로 web app과 다른 리소스 그룹에 Service Plan이 설정된 경우에만 입력하라고 하는데 ResourceGroupName이 지정된 경우 무조건 지정해야 하는 것으로 보입니다. 따라서 ResourceGroupName과 같은 값을 넣고 진행하면 됩니다.




"Request and Install certificate" 버튼을 눌러 진행할 때 다음과 같은 오류가 발생하는 경우가 있다면?

Server Error in '/letsencrypt' Application.
Response status code does not indicate success: 403 (Forbidden).
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Net.Http.HttpRequestException: Response status code does not indicate success: 403 (Forbidden).

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 

[HttpRequestException: Response status code does not indicate success: 403 (Forbidden).]
   System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode() +92620
   LetsEncrypt.Azure.Core.Services.WebAppCertificateService.Install(ICertificateInstallModel model) in J:\Projects\letsencrypt-siteextension\LetsEncrypt.SiteExtension.Core\Services\WebAppCertificateService.cs:56
   LetsEncrypt.Azure.Core.<RequestAndInstallInternalAsync>d__13.MoveNext() in J:\Projects\letsencrypt-siteextension\LetsEncrypt.SiteExtension.Core\CertificateManager.cs:187
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   LetsEncrypt.SiteExtension.Controllers.<Install>d__7.MoveNext() +594
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
...[생략]...
   System.Web.CallHandlerExecutionStep.InvokeEndHandler(IAsyncResult ar) +152
   System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +125

App 계정의 권한이 "구독" 레벨에 지정하지 않았기 때문에 발생한 것입니다. 즉 '구독' 레벨이 아닌 그 하위의 리소스 레벨에 App 계정을 부여하면 저런 오류가 발생합니다.

사실 개인적으로 '왜 App Service의 SSL 인증서 설정에 구독 레벨의 권한 부여를 해야만 했을까?' 하는 의문이 듭니다. 게다가 아래의 글에 보면,

Response status code does not indicate success: 403 (Forbidden).
; https://github.com/sjkp/letsencrypt-siteextension/issues/141

구독 레벨은 아니어도 "Resource group" 레벨에 권한 부여를 해 성공한 사례를 써 놓고 있습니다. 심지어 답변은 "App Service"와 그것의 "App Service Plan"에만 부여해도 된다는 식으로 나와 있습니다. 암튼, 제가 해본 결과 반드시 "구독" 레벨로 권한 부여를 해야 403 Forbidden 오류가 발생하지 않습니다.




다음과 같은 오류를 만난다면?

Server Error in '/letsencrypt' Application.
The remote server returned an error: (429) Too Many Requests.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code. 

Exception Details: System.Net.WebException: The remote server returned an error: (429) Too Many Requests.

Source Error: 

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace: 


[WebException: The remote server returned an error: (429) Too Many Requests.]
   System.Net.HttpWebRequest.GetResponse() +1399
   ACMESharp.AcmeClient.RequestHttpPost(Uri uri, Object message) in J:\Projects\letsencrypt-siteextension\ACMESharp\ACMESharp\ACMESharp\AcmeClient.cs:708

[AcmeWebException: Unexpected error
 +Response from server:
    + Code: 429
    + Content: {
  "type": "urn:acme:error:rateLimited",
  "detail": "Error creating new cert :: too many certificates already issued for exact set of domains: jpublictest.sysnet.pe.kr: see https://letsencrypt.org/docs/rate-limits/",
  "status": 429
}]
   ACMESharp.AcmeClient.RequestCertificate(String csrContent) in J:\Projects\letsencrypt-siteextension\ACMESharp\ACMESharp\ACMESharp\AcmeClient.cs:574
   LetsEncrypt.Azure.Core.Services.AcmeService.GetCertificate(AcmeClient client) in J:\Projects\letsencrypt-siteextension\LetsEncrypt.SiteExtension.Core\Services\AcmeService.cs:201
   LetsEncrypt.Azure.Core.Services.<RequestCertificate>d__5.MoveNext() in J:\Projects\letsencrypt-siteextension\LetsEncrypt.SiteExtension.Core\Services\AcmeService.cs:46
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   LetsEncrypt.Azure.Core.<RequestInternalAsync>d__12.MoveNext() in J:\Projects\letsencrypt-siteextension\LetsEncrypt.SiteExtension.Core\CertificateManager.cs:173
   System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +99
   System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
   LetsEncrypt.Azure.Core.<RequestAndInstallInternalAsync>d__13.MoveNext() in J:\Projects\letsencrypt-siteextension\LetsEncrypt.SiteExtension.Core\CertificateManager.cs:186
...[생략]...
   System.Web.CallHandlerExecutionStep.OnAsyncHandlerCompletion(IAsyncResult ar) +125

"Request and Install certificate" 버튼을 눌러 진행하다가 오류를 5번 이상 겪었다면 위와 같은 오류로 넘어가게 됩니다. letsencrypt는 단일 도메인에 대해 한 주에 5개의 인증서만을 발급하는 제약이 있는데, 따라서 이미 5번의 인증서 발급을 모두 사용했기 때문에 urn:acme:error:rateLimited 오류가 발생하는 것입니다.

이 때문에라도 "Request and Install certificate" 버튼을 누르기 전 "UseStaging" 체크 박스를 설정하고 정상적으로 적용이 되는지 확인을 해보는 것이 좋습니다. 괜스레 저처럼 ^^ App 계정의 권한 부여 수준을 바꿔가면서 테스트하다가 오류가 5번을 넘기게 되면... 저런 제약을 만나게 됩니다. 어쩔 수 없습니다. 1주일 기다려야 합니다. ^^;




"Automated Installation" 단계에서 "Next" 버튼을 눌렀을 때 다음과 같은 오류가 발생할 수 있습니다.

azure_lets_encrypt_error_1.png

The ClientId registered under application settings 00000000-0000-0000-0000-000000000000 does not match the ClientId you entered here ...guid...


원인은 간단합니다.

Let's Encrypt: ClientID reqistered under application settings differs from what I entered?
; https://stackoverflow.com/questions/46728797/lets-encrypt-clientid-reqistered-under-application-settings-differs-from-what

"Update Application Settings" 체크 박스를 설정한 다음에 "Next" 버튼으로 진행해야 합니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 7/21/2020 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 



2018-05-29 11시43분
Securing an Azure App Service Website under SSL in minutes with Let's Encrypt
; https://www.hanselman.com/blog/SecuringAnAzureAppServiceWebsiteUnderSSLInMinutesWithLetsEncrypt.aspx
정성태
2018-06-22 12시32분
정성태

... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...
NoWriterDateCnt.TitleFile(s)
11647정성태8/8/20182424오류 유형: 476. 음수의 음수는 여전히 음수가 되는 수(절대값이 음수인 수)
11646정성태8/8/20182146오류 유형: 475. gacutil.exe 실행 시 "Failure initializing gacutil" 오류 발생
11645정성태8/8/20182643오류 유형: 474. 닷넷 COM+ - Failed to load the runtime. [1]
11644정성태8/8/20183289디버깅 기술: 118. windbg - 닷넷 개발자를 위한 MEX Debugging Extension 소개
11643정성태8/12/20183490사물인터넷: 20. 아두이노 레오나르도 R3 호환 보드의 3.3v 핀의 LED 전압/전류 테스트 [1]파일 다운로드1
11642정성태8/3/20183487Graphics: 20. Unity - LightMode의 ForwardBase에 따른 _WorldSpaceLightPos0 값 변화
11641정성태8/3/20184958Graphics: 19. Unity로 실습하는 Shader (10) - 빌보드 구현파일 다운로드1
11640정성태8/3/20183556Graphics: 18. Unity - World matrix(unity_ObjectToWorld)로부터 Position, Rotation, Scale 값을 복원하는 방법파일 다운로드1
11639정성태8/2/20182867디버깅 기술: 117. windbg - 덤프 파일로부터 추출한 DLL을 참조하는 방법
11638정성태8/2/20182267오류 유형: 473. windbg - 덤프 파일로부터 추출한 DLL 참조 시 "Resolved file has a bad image, no metadata, or is otherwise inaccessible." 빌드 오류
11637정성태8/1/20183308Graphics: 17. Unity - World matrix(unity_ObjectToWorld)로부터 TRS(이동/회전/크기) 행렬로 복원하는 방법파일 다운로드1
11636정성태8/1/20185761Graphics: 16. 3D 공간에서 두 점이 이루는 각도 구하기파일 다운로드1
11635정성태8/1/20182701오류 유형: 472. C# 컴파일 오류 - Your project is not referencing the ".NETFramework,Version=v3.5" framework.
11634정성태8/1/20182629.NET Framework: 790. .NET Thread 상태가 Cooperative일 때 GC hang 현상 재현 방법파일 다운로드1
11633정성태7/29/20184810Graphics: 15. Unity - shader의 World matrix(unity_ObjectToWorld)를 수작업으로 구성 [1]
11632정성태7/28/20187971Graphics: 14. C# - Unity에서 캐릭터가 바라보는 방향을 기준으로 카메라의 위치 이동 및 회전하는 방법
11631정성태7/27/20185901Graphics: 13. Unity로 실습하는 Shader (9) - 투명 배경이 있는 텍스처 입히기
11630정성태2/11/20203462개발 환경 구성: 391. (GitHub 등과 직접 연동해) 소스 코드 디버깅을 쉽게 해 주는 SourceLink [2]
11629정성태7/26/20182829.NET Framework: 789. C# 컴파일 옵션 - Check for arithmetic overflow/underflow [1]
11628정성태7/26/20184704Graphics: 12. Unity로 실습하는 Shader (8) - 다중 패스(Multi-Pass Shader)
11627정성태7/25/20182184개발 환경 구성: 390. C# - 컴파일러 옵션 OSS signing / Public Signing
11626정성태7/25/20182953오류 유형: 471. .C++ 함수를 const로 바꾼 경우 C2440 컴파일 오류가 발생한다면?
11625정성태7/24/20182238Math: 49. GeoGebra 기하 (25) - 타원의 중심점 찾기파일 다운로드1
11624정성태7/24/20182785개발 환경 구성: 389. C# - 재현 가능한 빌드(reproducible builds) == Deterministic builds [4]
11623정성태7/24/20183399Math: 48. C# - 가우시안 함수의 이산형(discrete) 커널 값 생성파일 다운로드1
11622정성태7/23/20183986개발 환경 구성: 388. Windows 환경에서 Octave 패키지 설치하는 방법
... 16  17  18  19  20  21  22  23  24  25  26  27  28  [29]  30  ...