SSO 무한 리다이렉트 디버깅: 로그의 'POST /' 한 줄

2026.10.04
현장 데이터 서비스 구축기 12편

Kubernetes로 옮긴 Flask 서비스에 사내 SSO(ADFS)를 붙이며 겪은 MSIS9224 오류와 무한 리다이렉트를 해결한 기록이다. 도메인, TLS, SSO를 어떤 순서로 붙여야 하는지와 단계별 확인 방법을 정리했다.

이 글의 순서

    붙이는 순서가 절반이다

    Kubernetes 플랫폼으로 옮긴 Flask 서비스에 사내 SSO(ADFS 기반)를 붙이는 데 생각보다 오래 걸렸다. 돌아보면 각 단계는 어렵지 않았고, 순서를 몰라서 헤맨 시간이 대부분이었다. 공식 안내는 도메인 → 인증서 → TLS 적용 → SSO 신청 순서였고, 실제로는 승인 기간 때문에 SSO 신청을 먼저 넣고 인증서를 나중에 진행했다. 신청은 병렬로 해도 되지만, 확인은 반드시 아래 순서로 해야 한다.

    확인 순서

    1. 도메인클러스터 진입 IP로 연결
    2. TLShttps 정상, 인증서 경고 없음
    3. SSO 왕복/acs 콜백 성공
    4. 인증 강제SSO_ENFORCE=1

    앞 단계가 확인되기 전에는 다음 단계를 테스트하지 않는다.

    도메인: DNS 서버 IP로 등록했다

    DNS 신청서에 매핑 IP를 적는 칸이 있었는데, 잘못 이해해서 DNS 서버 자체의 IP를 적었다. 도메인은 등록됐는데 어디에도 연결되지 않았다. 매핑할 IP는 서비스가 배포된 클러스터의 진입 IP(ClusterVIP)다. 모르면 플랫폼 운영 측에 문의하면 알려 준다.

    진입 IP를 확인하다가 하나 더 알게 됐다. 클러스터 도메인 최상위 주소로 nslookup을 하면 IP가 나오지 않는다. 와일드카드 레코드라서 앞에 아무 이름이나 붙여야 나온다.

    bash
    nslookup apps.cluster.example.com        # 결과 없음
    nslookup anything.apps.cluster.example.com   # 클러스터 진입 IP가 나온다

    도메인을 연결한 뒤 IP로 직접 접속하면 404(default backend)가 뜬다. 정상이다. Ingress는 요청의 도메인을 보고 서비스를 찾기 때문에 IP로 오면 갈 곳이 없다.

    TLS 먼저, SSO는 그다음

    TLS를 적용하기 전에 SSO부터 테스트했더니 브라우저가 ERR_CERT_AUTHORITY_INVALID를 띄웠고, 경고를 넘겨도 SSO 콜백이 끝나지 않았다. SSO 서버는 신뢰할 수 있는 https 주소로만 결과를 돌려보낸다. 인증서를 발급받아 TLS Secret으로 만들고 Ingress에 적용한 뒤에야 SSO 테스트가 의미가 있다. 인증서를 Secret으로 바꾸는 과정은 13편에 따로 정리했다.

    MSIS9224: 옛 ClientID를 그대로 썼다

    TLS를 붙이고 로그인하자 ADFS 화면에 MSIS9224 오류가 떴다. redirect_uri가 등록된 값과 다르다는 뜻이다. 처음에는 등록 주소의 경로를 의심했는데, 원인은 ClientID였다.

    새 플랫폼용 SSO를 별도로 신청했고, 그 신청 건에는 새 ClientID와 Secret Key가 발급돼 있었다. 그런데 설정 파일에는 예전 플랫폼 신청 건의 ClientID가 그대로 있었다. 예전 ClientID에는 예전 도메인만 등록돼 있으니 새 도메인으로 돌아오라는 요청을 거부한 것이다. 승인 메일 두 통을 나란히 놓고 대조해서 찾았다.

    팁환경을 새로 신청하면 ID와 키가 새로 나온다고 가정하고, 설정 파일의 인증 관련 값을 전부 승인 메일과 대조한다.

    무한 리다이렉트: 로그의 'POST /' 한 줄

    ClientID를 고치자 이번에는 로그인 후 화면이 SSO와 서비스 사이를 끝없이 오갔다. 브라우저만 보면 원인을 알 수 없다. Pod 로그를 열자 단서가 보였다. SSO 서버가 인증 결과를 /acs가 아니라 /로 POST하고 있었다.

    Pod 로그
    GET  /        302  → SSO 로그인 페이지
    POST /        302  → 다시 SSO로   ← 콜백이 / 로 옴
    POST /        302  → 다시 SSO로
    POST /        302  → 다시 SSO로
    ...

    인증 결과를 받는 라우트가 /acs라서, /로 온 결과는 처리되지 않고 다시 로그인으로 보낸다.

    Pod 로그
    GET  /        302  → SSO 로그인 페이지
    POST /acs     302  → 세션 생성 후 / 로
    GET  /        200

    콜백이 /acs로 와야 세션이 만들어진다.

    원인은 설정 파일의 RedirectUrl에 /acs가 빠져 있던 것이다. 서비스는 SSO에 "인증 끝나면 도메인 루트로 보내 줘"라고 요청하고 있었다. 흥미로운 점은 SSO 등록 정보에는 /acs가 없어도 문제가 없었다는 것이다. 이 환경의 ADFS는 도메인 단위로 허용 여부를 판단했다. 결국 고칠 곳은 앱 설정 한 줄이었다.

    config.py (일부)
    import os
    
    _ENVS = {
        "prod": {
            "CLIENT_ID": os.environ.get("SSO_CLIENT_ID", ""),      # 새 신청 건의 값
            "REDIRECT_URL": "https://service.example.com/acs",     # /acs까지 포함
        },
        "dev": {
            "CLIENT_ID": os.environ.get("SSO_CLIENT_ID", ""),
            "REDIRECT_URL": "http://localhost:8080/acs",
        },
    }
    ENV = os.environ.get("APP_ENV", "dev")
    SSO = _ENVS[ENV]
    
    # 1이면 모든 페이지에 로그인 강제, 0이면 앱 동작만 확인
    SSO_ENFORCE = os.environ.get("SSO_ENFORCE", "1") == "1"

    SSO_ENFORCE 스위치 쓰는 법

    SSO 승인을 기다리는 동안 앱 기능을 먼저 검증하려고 로그인 강제 여부를 환경변수 하나로 끄고 켤 수 있게 했다. 유용했지만 한 번 크게 막혔다.

    시점SSO_ENFORCE이유
    SSO 승인 대기0로그인 없이 화면과 데이터 동작 확인
    /acs 왕복 확인 중0로그인 버튼으로만 SSO를 타서 콜백 확인
    왕복 성공 확인 후1모든 페이지 보호

    함정/acs 왕복이 성공하는 걸 보기 전에 1로 켜면, 모든 요청이 SSO로 가고 돌아오는 길이 막혀 있으니 무한 리다이렉트로 서비스 전체가 잠긴다. 다시 0으로 돌려 배포해야 풀린다. 승인 후 1로 되돌리는 것도 잊지 않는다.

    정리

    • 신청은 병렬로 해도 되지만 확인은 도메인 → TLS → SSO 왕복 → 인증 강제 순서로 한다.
    • DNS 매핑 IP는 클러스터 진입 IP다. IP로 접속했을 때의 404는 정상이다.
    • 새 환경으로 SSO를 신청하면 ClientID와 키도 새로 나온다. 승인 메일과 대조한다.
    • 무한 리다이렉트는 브라우저가 아니라 서버 로그에서 찾는다. 콜백이 어느 경로로 오는지가 핵심이다.
    이전 편11편. Flask 앱을 Kubernetes로 옮길 때 체크리스트: 옛 코드 배포, OOM, 쿼터 초과
    다음 편13편. pfx 인증서를 Kubernetes TLS Secret으로 바꾸기
    #SSO#ADFS#OIDC#리다이렉트#Flask#Kubernetes#TLS#트러블슈팅

    댓글