Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 
(연관된 글이 2개 있습니다.)

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" 버튼으로 진행해야 합니다.




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

[연관 글]






[최초 등록일: ]
[최종 수정일: 10/5/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at 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분
정성태
2021-09-12 04시32분
정성태

... 16  17  18  19  20  [21]  22  23  24  25  26  27  28  29  30  ...
NoWriterDateCnt.TitleFile(s)
13104정성태7/23/202210692Linux: 51. WSL - init에서 systemd로 전환하는 방법
13103정성태7/22/20227241오류 유형: 818. WSL - systemd-genie와 관련한 2가지(systemd-remount-fs.service, multipathd.socket) 에러
13102정성태7/19/20226646.NET Framework: 2033. .NET Core/5+에서는 구할 수 없는 HttpRuntime.AppDomainAppId
13101정성태7/15/202215503도서: 시작하세요! C# 10 프로그래밍
13100정성태7/15/20228011.NET Framework: 2032. C# 11 - shift 연산자 재정의에 대한 제약 완화 (Relaxing Shift Operator)
13099정성태7/14/20227885.NET Framework: 2031. C# 11 - 사용자 정의 checked 연산자파일 다운로드1
13098정성태7/13/20226151개발 환경 구성: 647. Azure - scale-out 상태의 App Service에서 특정 인스턴스에 요청을 보내는 방법 [1]
13097정성태7/12/20225539오류 유형: 817. Golang - binary.Read: invalid type int32
13096정성태7/8/20228319.NET Framework: 2030. C# 11 - UTF-8 문자열 리터럴
13095정성태7/7/20226423Windows: 208. AD 도메인에 참여하지 않은 컴퓨터에서 Kerberos 인증을 사용하는 방법
13094정성태7/6/20226159오류 유형: 816. Golang - "short write" 오류 원인
13093정성태7/5/20227076.NET Framework: 2029. C# - HttpWebRequest로 localhost 접속 시 2초 이상 지연
13092정성태7/3/20228025.NET Framework: 2028. C# - HttpWebRequest의 POST 동작 방식파일 다운로드1
13091정성태7/3/20226817.NET Framework: 2027. C# - IPv4, IPv6를 모두 지원하는 서버 소켓 생성 방법
13090정성태6/29/20225973오류 유형: 815. PyPI에 업로드한 패키지가 반영이 안 되는 경우
13089정성태6/28/20226444개발 환경 구성: 646. HOSTS 파일 변경 시 Edge 브라우저에 반영하는 방법
13088정성태6/27/20225540개발 환경 구성: 645. "Developer Command Prompt for VS 2022" 명령행 환경의 폰트를 바꾸는 방법
13087정성태6/23/20228536스크립트: 41. 파이썬 - FastAPI / uvicorn 호스팅 환경에서 asyncio 사용하는 방법 [1]
13086정성태6/22/20227931.NET Framework: 2026. C# 11 - 문자열 보간 개선 2가지파일 다운로드1
13085정성태6/22/20228008.NET Framework: 2025. C# 11 - 원시 문자열 리터럴(raw string literals)파일 다운로드1
13084정성태6/21/20226647개발 환경 구성: 644. Windows - 파이썬 2.7을 msi 설치 없이 구성하는 방법
13083정성태6/20/20227245.NET Framework: 2024. .NET 7에 도입된 GC의 메모리 해제에 대한 segment와 region의 차이점 [2]
13082정성태6/19/20226280.NET Framework: 2023. C# - Process의 I/O 사용량을 보여주는 GetProcessIoCounters Win32 API파일 다운로드1
13081정성태6/17/20226355.NET Framework: 2022. C# - .NET 7 Preview 5 신규 기능 - System.IO.Stream ReadExactly / ReadAtLeast파일 다운로드1
13080정성태6/17/20226978개발 환경 구성: 643. Visual Studio 2022 17.2 버전에서 C# 11 또는 .NET 7.0 preview 적용
13079정성태6/17/20224641오류 유형: 814. 파이썬 - Error: The file/path provided (...) does not appear to exist
... 16  17  18  19  20  [21]  22  23  24  25  26  27  28  29  30  ...