JupyterHub 화면을 iframe에 넣을 때의 CSP 설정
포털 화면 안에 JupyterHub를 iframe으로 표시하면서 CSP에 막히는 문제를 다뤘습니다. Hub 화면만 설정하면 끝나는 것이 아니라, 사용자가 실행한 Notebook 서버의 응답도 확인해야 했습니다.
아래는 당시 LocalProcessSpawner와 구형 NotebookApp을 사용한 환경의 설정입니다. 현재 Jupyter Server 계열에서는 설정 이름과 적용 위치가 다를 수 있으므로 설치 버전에 맞춰 확인해야 합니다.
Frame-ancestors의 역할
frame-ancestors는 해당 페이지를 iframe 등에 넣을 수 있는 부모 페이지의 출처를 지정합니다. 프로토콜과 호스트, 포트가 다르면 서로 다른 출처입니다. 포트를 다르게 띄운 로컬 환경에서도 이 차이가 생깁니다.
'self'는 응답 페이지와 같은 출처를 뜻합니다. 다른 포털에서 삽입하려면 그 포털의 출처도 허용해야 합니다. 브라우저의 CORS 설정과는 역할이 다릅니다. 자세한 의미는 CSP frame-ancestors 설명 에 정리돼 있습니다.
Hub 응답 설정
당시 jupyterhub_config.py에는 다음 형태로 설정했습니다. 주소는 설명용 예시입니다.
c.JupyterHub.tornado_settings = {
'headers': {
'Content-Security-Policy':
"frame-ancestors 'self' https://portal.example.com"
}
}이 코드는 해당 헤더 설정 부분만 보여줍니다. 이미 다른 CSP 지시문을 사용한다면 그것까지 지우지 않도록 기존 정책에 필요한 출처를 반영해야 합니다.
사용자 Notebook 서버 설정
실제 Notebook은 사용자별 서버에서 제공하므로 그 응답에도 설정이 필요했습니다. 당시 /etc/jupyter/jupyter_notebook_config.py에 적용한 형태는 다음과 같습니다.
c.NotebookApp.tornado_settings = {
'headers': {
'Content-Security-Policy':
"frame-ancestors 'self' https://portal.example.com"
}
}포털이 Hub를 표시할 수 있어도 Notebook 응답이 그 부모 출처를 허용하지 않으면 다음 화면에서 다시 막힐 수 있습니다. Hub와 사용자 서버를 따로 확인한 이유입니다.
실제 응답 헤더 확인
설정 후에는 해당 프로세스에 변경이 반영됐는지와 브라우저가 받은 응답 헤더를 확인해야 합니다. 이미 실행 중인 사용자 서버나 앞단 프록시에서 기존 헤더를 계속 보내고 있을 수 있습니다.
포털 안에서 로그인과 Notebook 화면까지 이동해 보고, 어느 응답에서 차단되는지 확인하는 방식이 도움이 됐습니다. 프록시가 추가하는 CSP나 X-Frame-Options도 함께 봐야 합니다.