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분
정성태

... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...
NoWriterDateCnt.TitleFile(s)
12503정성태1/21/20218733오류 유형: 696. C# - HttpClient: Requesting HTTP version 2.0 with version policy RequestVersionExact while HTTP/2 is not enabled.
12502정성태1/21/20219498.NET Framework: 1016. .NET Core HttpClient의 HTTP/2 지원파일 다운로드1
12501정성태1/21/20218585.NET Framework: 1015. .NET 5부터 HTTP/1.1, 2.0 선택을 위한 HttpVersionPolicy 동작 방식파일 다운로드1
12500정성태1/21/20219159.NET Framework: 1014. ASP.NET Core(Kestrel)의 HTTP/2 지원 여부파일 다운로드1
12499정성태1/20/202110383.NET Framework: 1013. .NET Core Kestrel 호스팅 - 포트 변경, non-localhost 접속 지원 및 https 등의 설정 변경 [1]파일 다운로드1
12498정성태1/20/20219343.NET Framework: 1012. .NET Core Kestrel 호스팅 - 비주얼 스튜디오의 Kestrel/IIS Express 프로파일 설정
12497정성태1/20/202110236.NET Framework: 1011. C# - OWIN Web API 예제 프로젝트 [1]파일 다운로드2
12496정성태1/19/20219102.NET Framework: 1010. .NET Core 콘솔 프로젝트에서 Kestrel 호스팅 방법 [1]
12495정성태1/19/202111164웹: 40. IIS의 HTTP/2 지원 여부 - h2, h2c [1]
12494정성태1/19/202110389개발 환경 구성: 522. WSL2 인스턴스와 호스트 측의 Hyper-V에 운영 중인 VM과 네트워크 연결을 하는 방법 [2]
12493정성태1/18/20218735.NET Framework: 1009. .NET 5에서의 네트워크 라이브러리 개선 (1) - HTTP 관련 [1]파일 다운로드1
12492정성태1/17/20218125오류 유형: 695. ASP.NET 0x80131620 Failed to bind to address
12491정성태1/16/20219749.NET Framework: 1008. 배열을 반환하는 C# COM 개체의 메서드를 C++에서 사용 시 메모리 누수 현상 [1]파일 다운로드1
12490정성태1/15/20219308.NET Framework: 1007. C# - foreach에서 열거 변수의 타입을 var로 쓰면 object로 추론하는 문제 [1]파일 다운로드1
12489정성태1/13/202110240.NET Framework: 1006. C# - DB에 저장한 텍스트의 (이모티콘을 비롯해) 유니코드 문자가 '?'로 보인다면? [1]
12488정성태1/13/202110493.NET Framework: 1005. C# - string 타입은 shallow copy일까요? deep copy일까요? [2]파일 다운로드1
12487정성태1/13/20219028.NET Framework: 1004. C# - GC Heap에 위치한 참조 개체의 주소를 알아내는 방법파일 다운로드1
12486정성태1/12/20219961.NET Framework: 1003. x64 환경에서 참조형의 기본 메모리 소비는 얼마나 될까요? [1]
12485정성태1/11/202110687Graphics: 38. C# - OpenCvSharp.VideoWriter에 BMP 파일을 1초씩 출력하는 예제파일 다운로드1
12484정성태1/9/202111345.NET Framework: 1002. C# - ReadOnlySequence<T> 소개파일 다운로드1
12483정성태1/8/20218470개발 환경 구성: 521. dotPeek - 훌륭한 역어셈블 소스 코드 생성 도구
12482정성태1/8/20219953.NET Framework: 1001. C# - 제네릭 타입/메서드에서 사용 시 경우에 따라 CS8377 컴파일 에러
12481정성태1/7/20219635.NET Framework: 1000. C# - CS8344 컴파일 에러: ref struct 타입의 사용 제한 메서드파일 다운로드1
12480정성태1/6/202112270.NET Framework: 999. C# - ArrayPool<T>와 MemoryPool<T> 소개파일 다운로드1
12479정성태1/6/20219642.NET Framework: 998. C# - OWIN 예제 프로젝트 만들기
12478정성태1/5/202111280.NET Framework: 997. C# - ArrayPool<T> 소개파일 다운로드1
... 31  32  33  34  35  36  37  38  39  40  41  42  43  44  [45]  ...