docs: harden ops guidance and CI smoke test
TaxBaik CI/CD / build-and-deploy (push) Successful in 50s
TaxBaik CI/CD / build-and-deploy (push) Successful in 50s
This commit is contained in:
@@ -35,6 +35,12 @@ TaxBaik.Web ASP.NET Core 앱 (포트 5001)
|
||||
- 관리자: `/taxbaik/admin` (Blazor Server)
|
||||
- 로그인: `/taxbaik/admin/login`
|
||||
|
||||
**운영 원칙:**
|
||||
- 단일 앱, 단일 서비스, 단일 배포 경로를 유지한다.
|
||||
- 운영 변경은 코드 또는 CI에서만 반영한다.
|
||||
- 서버에 임시 수동 수정이나 파일 드리프트가 생기지 않도록 한다.
|
||||
- 공개 사이트와 관리자 UI는 같은 앱에서 처리하되, 보안 경계는 인증과 권한으로 분리한다.
|
||||
|
||||
### 2.2 계층 책임
|
||||
- **Domain**: 비즈니스 규칙, 엔티티 정의
|
||||
- **Infrastructure**: DB 접근, Dapper 구현체, 마이그레이션 실행
|
||||
@@ -65,6 +71,11 @@ TaxBaik.Web ASP.NET Core 앱 (포트 5001)
|
||||
- 복잡한 조인, 페이징, 성능 제어 용이
|
||||
- EF Core 대비 SQL 완전 제어 가능
|
||||
|
||||
**왜 이 운영 모델인가?**
|
||||
- 운영 복잡도를 낮춰 장애 포인트를 줄인다.
|
||||
- 배포를 CI로 고정하면 서버 간 상태 드리프트를 줄인다.
|
||||
- 민감 정보는 코드/문서/로그에 남기지 않고 환경 변수와 서버 비밀 저장소에만 둔다.
|
||||
|
||||
---
|
||||
|
||||
## 3. 로컬 개발 환경 설정
|
||||
@@ -139,6 +150,11 @@ dotnet run
|
||||
|
||||
**중요**: 로컬 appsettings.json은 버전 관리에서 제외 또는 .local suffix 사용
|
||||
|
||||
**보안 규칙**:
|
||||
- `appsettings.Production.json`에는 비밀값을 두지 않는다.
|
||||
- JWT Secret, DB 비밀번호, 외부 API 키는 환경 변수 또는 서버 전용 비밀 경로에서만 읽는다.
|
||||
- 값이 비어 있으면 조용히 넘어가지 말고 시작 시 즉시 실패시킨다.
|
||||
|
||||
```bash
|
||||
# 로컬 오버라이드
|
||||
appsettings.Development.json # gitignore에 추가
|
||||
@@ -168,6 +184,10 @@ CREATE TABLE IF NOT EXISTS new_table (
|
||||
- 테스트 블로그 포스트 5개
|
||||
- 테스트 카테고리 5개
|
||||
|
||||
**운영 보안 주의**:
|
||||
- 시드 계정은 운영 초기화용이다. 배포 후에는 반드시 별도 강한 비밀번호로 교체한다.
|
||||
- 테스트 계정이 운영에 남아 있으면, 배포 후 즉시 비밀번호 재설정 또는 계정 비활성화를 수행한다.
|
||||
|
||||
수동 추가:
|
||||
```sql
|
||||
-- Admin 추가
|
||||
@@ -209,6 +229,11 @@ git push "http://kjh2064:${token}@localhost:3000/kjh2064/taxbaik.git" master
|
||||
- ✅ 안전 (token은 로컬 루프백)
|
||||
- ✅ 신뢰성 높음
|
||||
|
||||
**보안 규칙**:
|
||||
- 토큰은 채팅/문서/스크린샷에 붙이지 않는다.
|
||||
- push URL에 토큰이 남아 있으면 즉시 제거한다.
|
||||
- 가능하면 SSH key 기반 인증을 우선 사용한다.
|
||||
|
||||
#### 방법 B: SSH로 직접 Push (SSH key 필요)
|
||||
|
||||
```bash
|
||||
@@ -261,10 +286,13 @@ ssh kjh2064@178.104.200.7
|
||||
- 로컬 또는 서버에서 수동 `dotnet publish`로 운영 배포하지 않는다
|
||||
- `rsync`로 직접 아티팩트를 올리지 않는다
|
||||
- 배포 실패 시 CI 로그를 먼저 본다
|
||||
- 배포된 아티팩트는 CI가 만든 것만 신뢰한다
|
||||
- 배포 후 검증은 홈, 관리자 로그인 페이지, 로그인 API를 모두 포함한다
|
||||
|
||||
**롤백**:
|
||||
- 이전 정상 커밋을 `master`에 revert 또는 hotfix로 되돌린다
|
||||
- 서버 파일을 수동으로 복구하지 않는다
|
||||
- 롤백은 커밋 단위로 추적 가능해야 한다
|
||||
|
||||
### 3.4 서비스 파일 위치
|
||||
```
|
||||
@@ -298,6 +326,11 @@ location /taxbaik {
|
||||
|
||||
**참고**: 단일 `/taxbaik` 블록이 공개 사이트와 관리자(Blazor WebSocket)를 모두 처리합니다. 운영은 `5001` 통합 앱 기준이며, 설정 반영은 CI 배포로만 수행한다.
|
||||
|
||||
**Nginx 보안**:
|
||||
- `Upgrade` 헤더는 Blazor WebSocket 경로에만 허용하고, 필요 없는 location에는 넣지 않는다.
|
||||
- `Host`와 `X-Forwarded-Proto`는 유지해 원본 URL과 스킴을 보존한다.
|
||||
- `/taxbaik/admin`는 robots.txt에서 차단한다.
|
||||
|
||||
---
|
||||
|
||||
## 6. 데이터베이스
|
||||
@@ -311,6 +344,12 @@ Environment=ConnectionStrings__Default=Host=localhost;Database=taxbaikdb;Usernam
|
||||
|
||||
**절대 appsettings.Production.json에 비밀값을 하드코딩하지 말 것.**
|
||||
|
||||
**운영 보안 규칙**:
|
||||
- DB 계정은 애플리케이션 전용 최소 권한으로 둔다.
|
||||
- 관리자 비밀번호는 bcrypt로 해시하고, 평문 저장/전송을 금지한다.
|
||||
- `PasswordHash`는 null이 되면 안 되며, null이면 인증 실패로 즉시 처리한다.
|
||||
- 로그인 실패 로그는 사용자 이름만 남기고 비밀번호/해시를 절대 남기지 않는다.
|
||||
|
||||
### 3.2 Dapper 사용 패턴
|
||||
|
||||
**DbConnectionFactory.cs**:
|
||||
@@ -342,6 +381,7 @@ public async Task<BlogPost?> GetBySlugAsync(string slug, CancellationToken ct)
|
||||
- 항상 `@ParameterName` 파라미터 사용 (SQL injection 방지)
|
||||
- 절대 문자열 연결 금지
|
||||
- PostgreSQL `snake_case` 컬럼은 Dapper underscore 매핑을 전제로 함
|
||||
- 조회 쿼리는 필요한 컬럼만 명시한다. `SELECT *`는 스키마 변경 시 매핑 사고를 만든다.
|
||||
|
||||
### 3.3 마이그레이션
|
||||
|
||||
@@ -495,6 +535,11 @@ builder.Services.AddAuthorizationCore();
|
||||
|
||||
토큰은 localStorage에 저장되며, `CustomAuthenticationStateProvider`가 자동으로 복원:
|
||||
|
||||
**보안 규칙**:
|
||||
- JWT 만료 시간을 짧고 명확하게 유지한다.
|
||||
- localStorage 토큰은 XSS가 없다는 전제 없이 다뤄야 한다.
|
||||
- 관리자 기능은 `[Authorize]`로 감싸고, 클라이언트 렌더링만으로 권한을 믿지 않는다.
|
||||
|
||||
```csharp
|
||||
// CustomAuthenticationStateProvider.cs
|
||||
public async Task LoginAsync(string token)
|
||||
@@ -578,6 +623,9 @@ Admin 로그인 페이지만 [AllowAnonymous]:
|
||||
- [x] 카테고리 목록 캐시 (IMemoryCache, 10분 유효)
|
||||
- [x] 비밀값은 환경 변수에서 읽기
|
||||
- [x] `[ValidateAntiForgeryToken]` POST 메서드에 추가
|
||||
- [x] 운영 배포는 CI-only
|
||||
- [x] 관리자 로그인은 서버에서 직접 bypass하지 않기
|
||||
- [x] DB/인증 문제는 로그와 쿼리로 먼저 확인
|
||||
|
||||
### DON'T ❌
|
||||
- [ ] 비밀값을 appsettings.Production.json에 하드코딩
|
||||
@@ -589,6 +637,9 @@ Admin 로그인 페이지만 [AllowAnonymous]:
|
||||
- [ ] robots.txt에서 `/taxbaik/admin` allow 금지 (disallow 필수)
|
||||
- [ ] 폼 제출 후 redirect (fire-and-forget 또는 same-page 응답)
|
||||
- [ ] 절대 `Thread.Sleep` 또는 `Task.Delay` in request handler
|
||||
- [ ] 운영 서버에서 수동 publish/rsync/파일 교체
|
||||
- [ ] 비밀번호/토큰을 로그에 출력
|
||||
- [ ] `SELECT *`로 인증/권한 테이블 조회
|
||||
|
||||
---
|
||||
|
||||
@@ -651,7 +702,7 @@ dotnet build TaxBaik.sln
|
||||
ssh kjh2064@178.104.200.7
|
||||
|
||||
# DB 확인
|
||||
psql -U kjh2064 -d taxbaikdb -c "\dt"
|
||||
psql -U taxbaik -d taxbaikdb -c "\dt"
|
||||
|
||||
# 서비스 상태 (통합 Web 앱만)
|
||||
systemctl status taxbaik
|
||||
@@ -672,7 +723,7 @@ curl -X POST http://178.104.200.7/taxbaik/contact \
|
||||
|
||||
# 관리자 DB에서 확인
|
||||
ssh kjh2064@178.104.200.7
|
||||
psql -U kjh2064 -d taxbaikdb
|
||||
psql -U taxbaik -d taxbaikdb
|
||||
SELECT * FROM inquiries ORDER BY created_at DESC LIMIT 1;
|
||||
```
|
||||
|
||||
@@ -687,6 +738,7 @@ SELECT * FROM inquiries ORDER BY created_at DESC LIMIT 1;
|
||||
| 404 /taxbaik | Nginx 설정 재로드: `sudo nginx -t && sudo systemctl reload nginx` |
|
||||
| Blazor WebSocket 안 됨 | `/taxbaik/admin` 경로에 Upgrade 헤더 필요 (Nginx 설정 확인) |
|
||||
| 배포 후 503 | 서비스 시작 대기 (startup 시간 ~5초), `systemctl status taxbaik` 확인 |
|
||||
| 로그인 실패 | `admin_users.password_hash`와 bcrypt 해시, `AuthService` 로그, `/api/auth/login` 응답 확인 |
|
||||
|
||||
---
|
||||
|
||||
|
||||
Reference in New Issue
Block a user