Community코딩 & 개발github.com

dotnet-devcert-trust

Linux에서 .NET HTTPS 개발 인증서 신뢰 문제를 진단하고 해결합니다. 인증서 생성부터 시스템 CA 번들 포함까지 전체 수명 주기를 다루며, Ubuntu, Fedora, Arch 및 WSL2에 대한 배포판별 지침을 제공합니다.

dotnet-devcert-trust란 무엇인가요?

이 스킬은 Linux 환경에서 .NET 개발 인증서(dev cert) 신뢰 문제를 해결하기 위한 종합적인 진단 및 복구 절차를 제공합니다. Windows나 macOS와 달리 Linux에서 `dotnet dev-certs https --trust` 명령어는 인증서를 시스템 신뢰 저장소에 자동으로 추가하지 않기 때문에, Aspire 13.1.0+에서 Redis TLS 연결 실패(`UntrustedRoot`), HTTPS localhost 오류, gRPC 호출 실패 등이 발생합니다. 이 스킬은 5단계 진단 절차(인증서 존재 여부, 시스템 저장소 확인, CA 번들 포함 여부, 환경 변수 오버라이드, 심볼릭 링크 무결성)를 통해 문제를 정확히 파악하고, 각 단계별 구체적인 해결 방법을 제시합니다. 특히 `update-ca-certificates` 실행 누락이 가장 흔한 실수임을 강조하며, 전체 복구 셸 스크립트와 Ubuntu/Debian, Fedora/RHEL, Arch Linux, WSL2 각각에 맞는 맞춤형 지침을 포함합니다. 또한 CI/CD 파이프라인(GitHub Actions, Azure DevOps)에서의 자동화 방법과 Aspire 관련 고려사항(Redis TLS, 대시보드 HTTPS, `ASPIRE_ALLOW_UNSECURED_TRANSPORT` 우회)도 다룹니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/Aaronontheweb/dotnet-skills/tree/main/skills/dotnet-devcert-trust

Installed? Explore more 코딩 & 개발 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

Linux에서 .NET 개발 인증서 신뢰 문제 해결

이 스킬을 사용해야 하는 경우

다음 상황에서 이 스킬을 사용하세요:

  • Aspire에서 Redis TLS 연결이 UntrustedRoot 또는 RemoteCertificateNameMismatch 오류로 실패할 때
  • dotnet dev-certs https --check --trust 명령어가 종료 코드 7을 반환할 때
  • HTTPS localhost 연결이 인증서 유효성 검사 오류로 실패할 때
  • dotnet dev-certs https --clean 실행 후 신뢰를 복원해야 할 때
  • .NET HTTPS 개발을 위해 새 Linux 개발 머신을 설정할 때
  • Aspire 대시보드 또는 서비스 간 gRPC 호출이 TLS 오류로 실패할 때
  • Aspire < 13.1.0에서 업그레이드할 때(이전 버전은 Redis에서 기본적으로 TLS를 사용하지 않음)

문제

Windows와 macOS에서는 dotnet dev-certs https --trust가 모든 작업을 자동으로 처리합니다. 인증서를 생성하고, 사용자 저장소에 설치하며, 시스템 신뢰 저장소에 추가합니다. 하지만 Linux에서는 거의 유용한 작업을 수행하지 않습니다. 이 명령어는 인증서를 생성하여 사용자 저장소에 넣지만:

  1. 인증서를 시스템 CA 디렉터리로 내보내지 않습니다
  2. CA 번들을 재구축하기 위해 update-ca-certificates를 실행하지 않습니다
  3. 브라우저 신뢰 저장소(NSS/NSSDB)에 인증서를 추가하지 않습니다
  4. --trust 플래그는 조용히 성공하지만 인증서는 신뢰할 수 없는 상태로 남습니다

따라서 .NET 애플리케이션, OpenSSL, curl, 브라우저 모두 개발 인증서를 거부합니다. dotnet dev-certs https --check에서 인증서가 존재한다고 보고하더라도 마찬가지입니다.

Aspire 13.1.0+에서 이 문제가 두드러지는 이유

Aspire 13.1.0 이전에는 Redis 연결이 일반 텍스트를 사용했습니다. 13.1.0부터 Aspire는 Redis에 대해 TLS를 기본적으로 활성화합니다. 개발 인증서가 시스템 수준에서 신뢰되지 않으면 Redis 연결이 즉시 실패합니다:

System.Security.Authentication.AuthenticationException:
  The remote certificate is invalid because of errors in the certificate chain: UntrustedRoot

Linux 인증서 신뢰 작동 방식

아키텍처를 이해하면 맹목적인 디버깅을 방지할 수 있습니다:

┌─────────────────────────────────────────────────────┐
│ 애플리케이션(.NET, curl, OpenSSL)                   │
│   읽는 위치: /etc/ssl/certs/ca-certificates.crt         │
│          (통합 CA 번들)                               │
└──────────────────────┬──────────────────────────────┘
                       │ 구축 주체
┌──────────────────────▼──────────────────────────────┐
│ update-ca-certificates                              │
│   읽는 위치:                                        │
│     /usr/share/ca-certificates/      (배포판 CA)   │
│     /usr/local/share/ca-certificates/ (로컬 CA)   │
│   쓰는 위치:                                         │
│     /etc/ssl/certs/ca-certificates.crt (번들)     │
│     /etc/ssl/certs/*.pem (개별 심볼릭 링크)      │
└─────────────────────────────────────────────────────┘

핵심 인사이트: /usr/local/share/ca-certificates/에 .crt 파일을 넣는 것은 필요하지만 충분하지 않습니다. /etc/ssl/certs/ca-certificates.crt의 통합 번들은 update-ca-certificates를 실행하여 재구축해야 합니다. 애플리케이션은 개별 파일이 아닌 번들을 읽습니다.

5단계 진단 절차

다음 검사를 순서대로 실행하세요. 첫 번째 FAIL에서 멈추고 해당 수정을 적용한 후 계속 진행하세요.

검사 1: 개발 인증서 존재 여부

dotnet dev-certs https --check
echo "종료 코드: $?"
종료 코드의미조치
0사용자 저장소에 인증서 있음통과 — 계속 진행
0이 아님유효한 개발 인증서 없음dotnet dev-certs https 실행

검사 2: 시스템 신뢰 저장소 — 단일 인증서, 올바른 권한

ls -la /usr/local/share/ca-certificates/ | grep -iE 'dotnet|aspnet'
결과의미
dotnet-dev-cert.crt만 있고 권한이 -rw-r--r-- (644)인 경우통과
여러 인증서 파일, 잘못된 권한, 또는 오래된 aspnet* 파일이 있는 경우실패

이전 세션에서 흔히 남는 오래된 파일:

파일문제
aspnetcore-dev.crt종종 0600 권한으로 생성됨(update-ca-certificates가 읽을 수 없음)
aspnet/https.crt이전 규칙, 현재 개발 인증서와 지문이 다를 수 있음
dotnet-dev-cert.crt 권한 0600올바른 이름이지만 잘못된 권한

수정:

# 모든 오래된 인증서 파일 제거
sudo rm -f /usr/local/share/ca-certificates/aspnetcore-dev.crt
sudo rm -rf /usr/local/share/ca-certificates/aspnet/

# 개발 인증서에 올바른 권한 설정(존재하는 경우)
sudo chmod 644 /usr/local/share/ca-certificates/dotnet-dev-cert.crt

검사 3: CA 번들 포함 여부

이것은 가장 자주 실패하는 검사입니다. 인증서 파일은 존재하지만 번들에 추가되지 않았습니다.

openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \
  /usr/local/share/ca-certificates/dotnet-dev-cert.crt
결과의미
dotnet-dev-cert.crt: OK통과 — 인증서가 통합 번들에 있음
error 20 at 0 depth lookup: unable to get local issuer certificate실패 — 번들이 재구축되지 않음
error 2 at 0 depth lookup: unable to get issuer certificate실패 — 동일한 문제, 다른 OpenSSL 버전

수정:

sudo update-ca-certificates
# 예상 출력에 "1 added" 등이 포함됨

# 재확인
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \
  /usr/local/share/ca-certificates/dotnet-dev-cert.crt

검사 4: 환경 변수 오버라이드

SSL 환경 변수가 시스템 번들이 아닌 다른 곳으로 인증서 조회를 리디렉션할 수 있습니다:

echo "SSL_CERT_DIR=${SSL_CERT_DIR:-<설정되지 않음>}"
echo "SSL_CERT_FILE=${SSL_CERT_FILE:-<설정되지 않음>}"
echo "DOTNET_SSL_CERT_DIR=${DOTNET_SSL_CERT_DIR:-<설정되지 않음>}"
echo "DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER=${DOTNET_SYSTEM_NET_HTTP_USESOCKETSHTTPHANDLER:-<설정되지 않음>}"
결과의미
모두 <설정되지 않음>통과
어떤 변수라도 설정됨실패 — 인증서 조회를 리디렉션할 수 있음

수정: 셸 프로필(~/.bashrc, ~/.zshrc, ~/.profile)에서 문제가 되는 변수를 제거하고 새 셸을 시작하세요.

검사 5: 심볼릭 링크 무결성

이전에 제거된 인증서의 끊어진 심볼릭 링크는 OpenSSL을 혼동시킬 수 있습니다:

find /etc/ssl/certs/ -xtype l 2>/dev/null | head -5
결과의미
출력 없음통과
끊어진 심볼릭 링크 나열됨실패

수정:

sudo update-ca-certificates --fresh
# 모든 심볼릭 링크를 처음부터 다시 구축

전체 복구 절차

여러 검사가 실패하거나 깨끗한 상태로 시작하려면 다음 전체 시퀀스를 실행하세요:

#!/usr/bin/env bash
set -euo pipefail

echo "=== .NET 개발 인증서 신뢰 복구 ==="

# 1단계: 모든 오래된 인증서 파일 제거
echo "--- 오래된 인증서 파일 제거 중 ---"
sudo rm -f /usr/local/share/ca-certificates/aspnetcore-dev.crt
sudo rm -rf /usr/local/share/ca-certificates/aspnet/
sudo rm -f /usr/local/share/ca-certificates/dotnet-dev-cert.crt

# 2단계: 개발 인증서 정리 및 재생성
echo "--- 개발 인증서 재생성 중 ---"
dotnet dev-certs https --clean
dotnet dev-certs https

# 3단계: PEM으로 내보내고 시스템 신뢰 저장소에 설치
echo "--- 시스템 신뢰 저장소에 설치 중 ---"
dotnet dev-certs https --export-path /tmp/dotnet-dev-cert.crt --format PEM --no-password
sudo cp /tmp/dotnet-dev-cert.crt /usr/local/share/ca-certificates/dotnet-dev-cert.crt
sudo chmod 644 /usr/local/share/ca-certificates/dotnet-dev-cert.crt
rm /tmp/dotnet-dev-cert.crt

# 4단계: CA 번들 재구축 (중요 — 가장 자주 누락되는 단계)
echo "--- CA 번들 재구축 중 ---"
sudo update-ca-certificates

# 5단계: 확인
echo "--- 확인 중 ---"
openssl verify -CAfile /etc/ssl/certs/ca-certificates.crt \
  /usr/local/share/ca-certificates/dotnet-dev-cert.crt

echo "=== 완료! .NET 애플리케이션을 다시 시작하세요. ==="

이 내용을 ~/fix-devcert.sh로 저장하고 필요할 때 bash ~/fix-devcert.sh로 실행하세요.

배포판별 참고 사항

Ubuntu / Debian

위 절차는 Ubuntu/Debian용으로 작성되었으며 그대로 작동합니다.

  • CA 디렉터리: /usr/local/share/ca-certificates/
  • 번들 명령어: sudo update-ca-certificates
  • 번들 출력: /etc/ssl/certs/ca-certificates.crt
  • 인증서 형식: .crt 확장자의 PEM 필요

Fedora / RHEL / CentOS

Fedora는 update-ca-certificates 대신 update-ca-trust를 사용합니다:

# 인증서 내보내기
dotnet dev-certs https --export-path /tmp/dotnet-dev-cert.pem --format PEM --no-password

# Fedora 신뢰 저장소에 설치 (다른 디렉터리!)
sudo cp /tmp/dotnet-dev-cert.pem /etc/pki/ca-trust/source/anchors/dotnet-dev-cert.pem
sudo chmod 644 /etc/pki/ca-trust/source/anchors/dotnet-dev-cert.pem
rm /tmp/dotnet-dev-cert.pem

# 신뢰 번들 재구축
sudo update-ca-trust

# 확인
openssl verify /etc/pki/ca-trust/source/anchors/dotnet-dev-cert.pem

주요 차이점:

Ubuntu/DebianFedora/RHEL
CA 디렉터리/usr/local/share/ca-certificates//etc/pki/ca-trust/source/anchors/
재구축 명령어update-ca-certificatesupdate-ca-trust
번들 경로/etc/ssl/certs/ca-certificates.crt/etc/pki/tls/certs/ca-bundle.crt
확장자.crt.pem (모든 확장자 가능)

Arch Linux

Arch는 Fedora와 동일한 update-ca-trust 방식을 사용합니다:

sudo cp /tmp/dotnet-dev-cert.pem /etc/ca-certificates/trust-source/anchors/dotnet-dev-cert.pem
sudo chmod 644 /etc/ca-certificates/trust-source/anchors/dotnet-dev-cert.pem
sudo update-ca-trust

WSL2

WSL2는 자체 인증서 저장소를 가진 실제 Linux 커널을 실행합니다. Windows 호스트와는 별개입니다. 표준 Ubuntu/Debian 절차가 작동하지만 다음 사항에 주의하세요:

  1. 공유 파일 시스템 (/mnt/c/) — Windows 파일 시스템의 인증서 파일은 644가 아닐 수 있는 Windows 권한을 가집니다. 항상 기본 Linux 경로에 먼저 복사하세요.
  2. systemd가 실행 중이 아닌 경우 — 일부 오래된 WSL2 설정에는 systemd가 없어 update-ca-certificates 후크가 이에 의존할 수 있습니다. 명령어가 멈추면 sudo dpkg-reconfigure ca-certificates를 대신 시도하세요.
  3. Docker Desktop 통합 — Docker Desktop의 WSL2 백엔드를 사용하는 경우 컨테이너는 WSL2 배포판의 CA 번들을 상속받습니다. WSL2에서 신뢰를 수정하면 컨테이너에서도 수정됩니다.

Aspire 관련 고려 사항

Redis TLS (Aspire 13.1.0+)

Aspire 13.1.0은 Redis에 대해 TLS를 기본적으로 활성화합니다. Redis 연결 오류에 UntrustedRoot가 표시되면 개발 인증서가 시스템 수준에서 신뢰되지 않는 것입니다. 위의 전체 복구 절차를 실행하세요.

Aspire 대시보드 HTTPS

Aspire 대시보드는 HTTPS에 개발 인증서를 사용합니다. 브라우저에 인증서 경고가 표시되면 인증서가 브라우저의 신뢰 저장소에 없는 것입니다. 개발 중에는 경고를 무시하고 진행해도 됩니다. 시스템 수준 신뢰(Redis, gRPC 등에 필요)가 우선입니다.

ASPIRE_ALLOW_UNSECURED_TRANSPORT

ASPIRE_ALLOW_UNSECURED_TRANSPORT=true를 설정하는 것은 해결책이 아닌 임시 방편입니다. 서비스 간 통신에 대해 TLS를 비활성화하여:

  • 근본적인 신뢰 문제를 가립니다
  • 프로덕션 동작과 일치하지 않습니다
  • 프로덕션에서 볼 수 있는 것과 다른 버그를 유발할 수 있습니다

대신 인증서 신뢰를 수정하세요.

인증서 수명 주기

개발 인증서는 생성일로부터 1년 동안 유효합니다. 만료되면:

  1. dotnet dev-certs https --check가 유효한 인증서가 없다고 보고합니다
  2. 전체 복구 절차를 실행하여 새 인증서를 생성합니다
  3. /usr/local/share/ca-certificates/에 있는 이전 인증서 파일이 교체됩니다
  4. update-ca-certificates가 번들에서 이전 인증서를 새 인증서로 교체합니다

시스템 재부팅이 필요하지 않습니다. 애플리케이션은 다음 TLS 핸드셰이크 시 새 번들을 사용합니다(앱을 다시 시작하세요).

자동화: CI/CD 파이프라인

Linux 러너의 CI/CD에서는 개발 인증서가 거의 필요하지 않습니다(일반적으로 실제 인증서로 테스트하거나 테스트 도구에서 TLS 유효성 검사를 비활성화합니다). 그러나 통합 테스트에 신뢰할 수 있는 개발 인증서가 필요한 경우:

GitHub Actions

- name: .NET 개발 인증서 신뢰
  run: |
    dotnet dev-certs https
    dotnet dev-certs https --export-path /tmp/dotnet-dev-cert.crt --format PEM --no-password
    sudo cp /tmp/dotnet-dev-cert.crt /usr/local/share/ca-certificates/dotnet-dev-cert.crt
    sudo chmod 644 /usr/local/share/ca-certificates/dotnet-dev-cert.crt
    rm /tmp/dotnet-dev-cert.crt
    sudo update-ca-certificates

Azure DevOps

- script: |
    dotnet dev-certs https
    dotnet dev-certs https --export-path /tmp/dotnet-dev-cert.crt --format PEM --no-password
    sudo cp /tmp/dotnet-dev-cert.crt /usr/local/share/ca-certificates/dotnet-dev-cert.crt
    sudo chmod 644 /usr/local/share/ca-certificates/dotnet-dev-cert.crt
    rm /tmp/dotnet-dev-cert.crt
    sudo update-ca-certificates
  displayName: '.NET 개발 인증서 신뢰'

일반적인 함정

1. 인증서를 넣었지만 번들을 재구축하지 않음

증상: /usr/local/share/ca-certificates/에 인증서 파일이 있지만 openssl verify가 실패합니다.

원인: 파일을 넣은 후 update-ca-certificates를 실행하지 않았습니다.

수정: sudo update-ca-certificates

이것은 가장 흔한 실수입니다. CA 디렉터리는 번들 자체가 아니라 번들 생성 프로세스의 입력입니다.

2. 잘못된 권한의 오래된 인증서 파일

증상: update-ca-certificates가 실행되지만 0 added를 보고합니다.

원인: 0600 권한의 인증서 파일은 update-ca-certificates가 읽을 수 없습니다(루트로 실행되지만 세계 읽기 가능 여부를 확인하는 프로세스를 통해 파일을 읽습니다). 파일은 644여야 합니다.

수정: sudo chmod 644 /usr/local/share/ca-certificates/*.crt

3. 여러 세션의 여러 인증서 파일

증상: update-ca-certificates가 여러 인증서를 추가하지만 애플리케이션이 여전히 실패합니다.

원인: 이전 dotnet dev-certs https --clean / 재생성 주기의 오래된 인증서 파일이 CA 디렉터리에 남아 있습니다. 이전 인증서의 지문이 현재 개발 인증서와 일치하지 않습니다.

수정: 모든 dotnet* 및 aspnet* 파일을 제거한 다음 현재 인증서를 다시 내보내세요.

4. 정리/재생성 후 지문 불일치

증상: openssl verify는 통과하지만 .NET이 여전히 UntrustedRoot를 보고합니다.

원인: /usr/local/share/ca-certificates/의 인증서가 이전 개발 인증서에서 내보내졌습니다. dotnet dev-certs https --clean && dotnet dev-certs https 후에는 새 인증서가 생성됩니다. 이전 인증서(이전 지문)가 CA 디렉터리에 남아 있으면 애플리케이션은 일치하는 항목을 찾지 못합니다.

수정: CA 디렉터리에서 모든 개발 인증서 파일을 제거하고 현재 인증서만 다시 내보내세요.

이 스킬의 작동 방식

이 스킬은 에이전트(예: Claude Code, Cursor)에게 Linux에서 .NET HTTPS 개발 인증서 신뢰 문제를 진단하고 해결하기 위한 구조화된 절차를 제공합니다. 에이전트는 5단계 진단 절차를 따르고, 각 검사 결과에 따라 특정 수정을 적용하며, 모든 단계를 완료한 후 성공을 확인할 수 있습니다. 이 스킬은 배포판별 차이점(Ubuntu, Fedora, Arch, WSL2)과 Aspire 관련 문제(Redis TLS, 대시보드 HTTPS)를 모두 다룹니다. 에이전트는 이 정보를 사용하여 수동 개입 없이 UntrustedRoot 오류를 해결하는 명령어를 실행할 수 있습니다.

Individual skills in this repo

This repo contains 20 individual skills — each has its own dedicated page.

akka-hosting-actor-patterns

Patterns for building entity actors with Akka.Hosting - GenericChildPerEntityParent, message extractors, cluster sharding abstraction, akka-reminders, and ITimeProvider. Supports both local testing and clustered production modes.

akka-net-aspire-configuration

Configure Akka.NET with .NET Aspire for local development and production deployments. Covers actor system setup, clustering, persistence, Akka.Management integration, and Aspire orchestration patterns.

akka-net-best-practices

Critical Akka.NET best practices including EventStream vs DistributedPubSub, supervision strategies, error handling, Props vs DependencyResolver, work distribution patterns, and cluster/local mode abstractions for testability.

akka-net-management

Akka.Management for cluster bootstrapping, service discovery (Kubernetes, Azure, Config), health checks, and dynamic cluster formation without static seed nodes.

akka-net-testing-patterns

Write unit and integration tests for Akka.NET actors using modern Akka.Hosting.TestKit patterns. Covers dependency injection, TestProbes, persistence testing, and actor interaction verification. Includes guidance on when to use traditional TestKit.

api-design

Design stable, compatible public APIs using extend-only design principles. Manage API compatibility, wire compatibility, and versioning for NuGet packages and distributed systems.

aspire-configuration

Configure Aspire AppHost to emit explicit app config via environment variables; keep app code free of Aspire clients and service discovery.

aspire-integration-testing

Write integration tests using .NET Aspire

aspire-service-defaults

Create a shared ServiceDefaults project for Aspire applications. Centralizes OpenTelemetry, health checks, resilience, and service discovery configuration across all services.

crap-analysis

Analyze code coverage and CRAP (Change Risk Anti-Patterns) scores to identify high-risk code. Use OpenCover format with ReportGenerator for Risk Hotspots showing cyclomatic complexity and untested code paths.

csharp-concurrency-patterns

Choosing the right concurrency abstraction in .NET - from async/await for I/O to Channels for producer/consumer to Akka.NET for stateful entity management. Avoid locks and manual synchronization unless absolutely necessary.

database-performance

Database access patterns for performance. Separate read/write models, avoid N+1 queries, use AsNoTracking, apply row limits, and never do application-side joins. Works with EF Core and Dapper.

dependency-injection-patterns

Organize DI registrations using IServiceCollection extension methods. Group related services into composable Add* methods for clean Program.cs and reusable configuration in tests.

dotnet-local-tools

Managing local .NET tools with dotnet-tools.json for consistent tooling across development environments and CI/CD pipelines.

dotnet-project-structure

Modern .NET project structure including .slnx solution format, Directory.Build.props, central package management, SourceLink, version management with RELEASE_NOTES.md, and SDK pinning with global.json.

dotnet-slopwatch

Use Slopwatch to detect LLM reward hacking in .NET code changes. Run after every code modification to catch disabled tests, suppressed warnings, empty catch blocks, and other shortcuts that mask real problems.

efcore-patterns

Entity Framework Core best practices including NoTracking by default, query splitting for navigation collections, migration management, dedicated migration services, and common pitfalls to avoid.

ilspy-decompile

Understand implementation details of .NET code by decompiling assemblies. Use when you want to see how a .NET API works internally, inspect NuGet package source, view framework implementation, or understand compiled .NET binaries.

mailpit-integration

Test email sending locally using Mailpit with .NET Aspire. Captures all outgoing emails without sending them. View rendered HTML, inspect headers, and verify delivery in integration tests.

marketplace-publishing

Workflow for publishing skills and agents to the dotnet-skills Claude Code marketplace. Covers adding new content, updating plugin.json, validation, and release tagging.

관련 스킬