LabHub
开始
学习 学习路径 课程

构建 EAI 中间层

证书不同、规格不同 — 搭建对外网关

在 LabHub 中继续学习

한국어 원문으로 표시합니다.

목표

두 대외 기관과 상호 TLS 로 붙는 대외계를 만든다. 기관별 인증서를 발급받고, 기관 프로필을 데이터로 두고, 기관 규격으로 번역해 보내고, 허용 주소에서만 받고, 인증서 만료를 감시한다.

왜 중요한가

대외 기관은 우리 표준에 맞춰 주지 않는다. 규격·인증서·허용 주소의 차이를 대외계 한 곳에 가두지 않으면 기관이 늘 때마다 안쪽 시스템을 고치게 된다. 그리고 대외 장애의 단골 — 인증서 만료, 인증서 뒤바뀜, 길이 기준 착각 — 은 전부 이 계층에서 막거나 일찍 알아챌 수 있다.

단계

  1. python3 /opt/lab/fixtures/eaimw/fep/pki.py init /root/eaimw/fep/partners 로 두 기관의 CA 를 만든다. 기관마다 개인키와 CSR(주체에 O=LabHub Bank, CN 에 labhub 포함)을 만들고 python3 /opt/lab/fixtures/eaimw/fep/pki.py sign A <CSR> /root/eaimw/fep/a.crt, … sign B <CSR> /root/eaimw/fep/b.crt 로 서명받는다. 키는 /root/eaimw/fep/a.key·/root/eaimw/fep/b.key.
  2. 정의서 /opt/lab/fixtures/eaimw/fep/ORGS.md 를 보고 /root/eaimw/fep/orgs.json 을 쓴다. 최상위 열쇠는 기관 코드(201·301), 값은 host·port·format(fixed/jsonencoding(euc_kr/utf-8len_includes_self(A 는 true, B 는 null)·ca·cert·key(절대경로)·allow_ips(목록).
  3. 기관 A 서버를 띄우고(nohup python3 /opt/lab/fixtures/eaimw/fep/org_server.py A > /root/eaimw/fep/a.out 2>&1 &, 9441) openssl s_client 로 a.crt·a.key·기관 A CA 를 주고 PING 한 줄을 보내, 받은 PONG <지문>/root/eaimw/fep/a-ping.txt 에 저장한다.
  4. 같은 명령에 b.crt·b.key 를 끼워 기관 A 에 붙어 보고, 표준출력과 표준오류를 함께(2>&1) /root/eaimw/fep/wrong.txt 에 저장한다. 핸드셰이크가 거절돼야 한다.
  5. /root/eaimw/fep/fepgw.py send <기관코드> <요청JSON파일>: 프로필(FEP_ORGS, 기본 /root/eaimw/fep/orgs.json)에서 기관을 찾아 그 기관의 인증서로 상호 TLS 연결하고, 기관 서버 인증서를 그 기관 CA 로 검증한다. 포트는 환경변수 FEP_PORT_<기관코드> 가 있으면 그것을 쓴다. 201 은 요청 {guid,date,amount,acct,name} 을 A 규격 고정길이로(길이가 자기 포함) 보내고 결과를 {"org","rspCode","guid","realName","clientFp"} JSON 한 줄로, 301 은 {guid,custId}POST /v1/credit-check 로 보내 {"org","rspCode","guid","grade","clientFp"} 로 출력한다. 응답코드는 LH-STD 로(00→0000, 14→B202, 30→E102, 그 밖 E500).
  6. /root/eaimw/fep/inbound.py --port <P>: 대외 기관이 거는 연결(평문 TCP, A 규격)을 받는다. 출발지 주소가 어느 기관의 allow_ips 에도 없으면 읽지 않고 끊고 FEP_LOG(기본 /root/eaimw/fep/inbound.log)에 DENY <주소> 한 줄을 남긴다. 허용된 주소면 A 규격 응답(전문종별 0210, 같은 거래고유번호, 응답코드 00, 실명 칸 공백 20, 지문 칸 - 64개, 길이는 자기 포함)을 돌려준다.
  7. /root/eaimw/fep/certcheck.py --warn-days <N>: 프로필의 기관마다 <기관코드> <만료일 YYYY-MM-DD> <남은일수> <OK|WARN> 한 줄을 기관 코드순으로 출력한다. 남은 일수가 N 미만이면 WARN 이고, WARN 이 하나라도 있으면 0 이 아닌 코드로 끝난다.

참고

기관별 인증서를 발급받는다

두 기관 CA 를 만들고, 기관마다 키·CSR 을 만들어 서명받아 a.crt/a.key·b.crt/b.key 를 둔다.

개인키는 우리 쪽에 두고 CSR 만 보냅니다. 기관 A 에는 A 가, B 에는 B 가 서명해야 합니다 — 한 CA 가 둘 다 서명하면 기관이 다른 쪽 인증서를 거절하지 못합니다.

기관 프로필을 데이터로

ORGS.md 를 보고 /root/eaimw/fep/orgs.json 에 두 기관의 주소·형식·인코딩·길이 기준·인증서·허용 IP 를 쓴다.

코드에 기관 이름이 등장하지 않게, 기관마다 다른 값은 전부 이 파일로 옮깁니다. 길이 기준은 JSON 기관에는 해당이 없으니 null 입니다.

기관 A 와 상호 TLS 로 붙는다

기관 A 서버를 띄우고 a.crt 로 PING 을 보내 PONG <지문> 을 /root/eaimw/fep/a-ping.txt 에 저장한다.

openssl s_client 에 -cert·-key(내 신원)와 -CAfile(기관 CA, 서버 검증)을 함께 줍니다. -quiet 면 응답 본문만 남습니다. 지문은 기관이 본 내 인증서입니다.

인증서를 바꿔 끼우면 거절된다

b.crt·b.key 로 기관 A 에 붙어 보고, 출력과 오류를 함께 /root/eaimw/fep/wrong.txt 에 저장한다.

3단계 명령에서 인증서와 키만 바꿉니다. 거절 사유(TLS alert)는 표준오류로 나오니 2>&1 로 함께 저장하세요.

기관마다 골라 보낸다

/root/eaimw/fep/fepgw.py send <기관> 이 기관별 인증서·규격으로 보내고 결과를 LH-STD 코드로 정규화한 JSON 한 줄을 낸다.

프로필에서 format 을 보고 어댑터를 고릅니다. A 는 본문을 다 만든 뒤 len(본문)+4 를 길이로 씁니다(자기 포함). 서버 검증은 create_default_context(cafile=기관CA), 내 신원은 load_cert_chain.

들어오는 쪽 — 허용 주소에서만

/root/eaimw/fep/inbound.py --port P 가 프로필의 allow_ips 에서 온 연결만 받아 A 규격으로 답하고, 나머지는 끊고 DENY 를 기록한다.

socketserver 핸들러의 self.client_address[0] 가 출발지 주소입니다. 허용 목록은 모든 기관 프로필의 allow_ips 를 합친 것입니다. 거절할 때는 읽지 말고 바로 돌아오세요.

만료를 먼저 안다

/root/eaimw/fep/certcheck.py --warn-days N 이 기관마다 만료일·남은 일수·OK/WARN 을 출력하고, WARN 이 있으면 0 이 아닌 코드로 끝난다.

인증서의 notAfter 를 읽어 지금(UTC)과의 차이를 일수로 셉니다. 교체에 몇 주가 걸리는 기관이 있으니 기준은 넉넉하게 — 감시 도구는 종료 코드로 알아챕니다.