티스토리 뷰

지금은, 할야드(Halyard)를 이용해서 쉽고 간편하게, 사용자인증, 데크 및 게이트 노출을 처리하고 있다. 지금은 아래 설정 방법을 직접 할 필요는 없다. 할야드를 사용하면 알아서 적절한 위치에 관련 설정을 잘 적용해 주기 때문이다. 다음은 OAuth2 인증을 적용하는 예제를 보여준다. 공식적으로는 구글(Google), 깃헙(Github), 애저(Azure)를 지원하고 있지만, 다른 사용자정보 제공자(IdP, Identity Provider)도 사용할 수 있다. 그럴 경우에는 PROVIDER 부분에 OTHER라고 입력한다. 그 외 나머지 정보는 프로바이더에서 제공하는 URL을 알맞게 연결해주면 된다.


CLIENT_ID=myClientId
CLIENT_SECRET=myClientSecret
PROVIDER=google|github|azure

hal config security authn oauth2 edit \
  --client-id $CLIENT_ID \
  --client-secret $CLIENT_SECRET \
  --provider $PROVIDER
hal config security authn oauth2 enable

또는 SAML2를 이용해서 사용자 인증을 처리할 수도 있다.


KEYSTORE_PATH= # /path/to/keystore.jks
KEYSTORE_PASSWORD=changeme
METADATA_PATH= # /path/to/metadata.xml
SERVICE_ADDR_URL=https://localhost:8084
ISSUER_ID=https://test.spinnaker.cloud

hal config security authn saml edit \
   --keystore $KEYSTORE_PATH \
   --keystore-alias saml \
   --keystore-password $KEYSTORE_PASSWORD \
   --metadata $METADATA_PATH \
   --issuer-id $ISSUER_ID \
   --service-address-url $SERVICE_ADDR_URL
      
hal config security authn saml enable

위와 같이 할야드를 이용하면 사용자인증 기능을 관리할 수 있으며, 자세한 내용은 공식 문서를 참조하면 된다. https://www.spinnaker.io/setup/security/authentication/


스핀에커가 사람들에게 많이 알려지기 전, 2016년 말에 OAuth2를 이용한 사용자 인증을 설정하도록 시도 했었다. 정보가 부족한 상태였고 스핀에커의 구조를 더듬더듬 알아가면서 설정할 수 밖에 없었다. 그래서 많은 어려움이 있었다. 전문용어로 말하자면 무수한 삽질을 했었다. 그 때 최종적으로 얻은 설정은 아래에서 언급한 설정과 많이 비슷했다. 그 당시에는 게이트에 ELB를 따로 붙이지 않고 웹 서버에서 리버스 프록시 (Reverse Proxy)설정을 추가해서 데크가 게이트를 호출하도록 했다. 그래서 데크만 외부로 노출이 될 뿐 모든 서비스는 데크 뒤에 숨어서 동작하도록 했었다. 무엇이 더 좋은 지 생각해 보면 각각 장단이 있겠지만, 게이트를 위하 ELB를 따로 떼어놓는 방법이 데크와 게이트의 의존성을 줄일 수 있는 방법이라서 더 좋다고 생각한다.


스핀에커(Spinnaker)에서는 사용자 관리를 직접 하지 않는다. 대신 사용자정보 제공자를 연결하여 사용자 정보를 제공받는다. 따라서, 사용자 정보 제공자에게 사용자 인증을 위임하고 인증처리 결과를 통보 받는 형식을 따른다. 현재 일반적으로 사용하는 OAuth2, SAML 2.0, LDAP 등 외부 시스템을 활용할 수 있다. 따라서 사용자가 스핀에커에 접속하려고 하면, 스핀에커는 사용자 정보 제공자에게 돌려보내고, 사용자는 자신의 계정을 가지고 인증 절차를 거친다. 인증에 성공하면, 다시 스핀에커로 돌아오게 된다.

이러한 인증 처리 과정은 스핀에커를 이루는 여러 요소(Microservices) 중에서 게이트(Gate)가 담당하고 있다. 게이트는 스핀에커의 API 게이트웨이(Gateway)다. API 게이트웨이는 마이크로서비스 아키텍처 (MSA; Microservices Architecture)에서 중요한 역할을 담당하는 설계 패턴이다. 기회가 된다면, 다른 글에서 설명한다.
 
OAuth2
게이트는 스핀에커에서 API 게이트웨이 역할을 하고 있기 때문에 사용자 인증 기능은 여기서 설정해야 한다. 스핀에커는 스프링 부트(Spring Boot)를 이용해서 개발하였고, 최근 스프링 기반의 자바 어플리케이션(Java Application) 환경설정을 적용하는 방식을 따르고 있다. 그 것은 기본 설정을 내부에 미리 갖고 있고, 사용자가 필요에 의해서 재정의해서 덮어쓰게 하는 방식이다. 스핀에커는 모든 서비스(Service)들이 각자의 이름과 동일한 설정파일 (e.g., spinnaker.yml, gate.yml, fiat.yml, clouddriver.yml) 을 갖고 있고, 그 안의 기본 값들을 사용한다. 이 것만으로도 실행이 가능하지만, 사용자가 원하는 것으로 변경하고 싶을 수 있다. 이럴 때는 사용자가 *-local.yml 과 같은 형식의 파일을 만들고 그 안에서 값을 설정하면 (기본 *.yml을 읽어서 설정 적용한 다음 *-local.yml 의 정보를 읽어서) 최종 적용된다.
다음은 GitHub에서 OAuth2 어플리케이션 연동을 설정한 다음 얻을 수 있는 클라이언트 아이디(Client Id)와 클라이언트 시크릿(Client Secret) 값을 게이트 설정으로 추가하는 방법을 보여준다. 다음과 같이 gate-local.yml을 만들고 스핀에커 설정이 모여 있는 위치에 저장한다. 그 다음 게이트를 다시 시작하면 OAuth2 인증을 사용할 수 있다. 환경설정들은 /opt/spinnaker/config 또는 /opt/gate/config 에 모여있다. 아래 예제를 /opt/gate/config/gate-local.yml에 적용하면 GitHub의 가입자 정보를 그대로 스핀에커 사용자로 활용할 수 있다. 즉 GitHub의 사용자라면 동시에 스핀에커의 사용자가 되는 것이다.
추가적으로 보안을 강화하기 위해서, 특정 정보를 가진 사용자만 접근할 수 있도록 걸러낼 수 있다. 아래 예제의 맨 아랫부분을 보면, userInfoRequirements 라는 설정이 있다. 이 것은 OAuth2를 이용해서 사용자가 인증에 성공한 다음, 가져온 사용자 계정정보를 정규식표현과 비교해서 조건에 맞는 지를 확인하는 설정이다. 아래 예제에서는 사용자의 전자우편 주소가 특정 조건을 만족하는 경우에만 최종 허가하도록 설정하는 것을 보여준다. 조건은 정규식으로 표현할 수 있다.
 
[주의] Gate 4.0.0 이후 부터는 맨 처음의 spring 대신 security를 사용해야 한다.


security:
  oauth2:
    client:
      clientId: changeme
      clientSecret: changeme
      useCurrentUri: false
      scope: user:email
      accessTokenUri: https://github.com/login/oauth/access_token
      userAuthorizationUri: https://github.com/login/oauth/authorize
    resource:
      userInfoUri: https://api.github.com/user
    userInfoMapping:
      email: email
      firstName: name
      username: login
    userInfoRequirements:
      email: /(.*)@(company|corp)\.com$/

 
Public Access
스핀에커를 처음 설치하면, 터널링(SSH Tunneling)을 통해서만 접속할 수 있다. 그 이유는 스핀에커가 클라우드 환경에 대해 꽤 큰 권한을 갖고 있기 때문이다. 그래서 보안강화를 위해 사용자 접근을 최소화해야 하기 때문인데, 스핀에커 초창기에는 사용자 인증과 권한관리 기능을 따로 제공하지 않았다. 그래서 SSH사용자를 관리하는 것으로 대신하는 정책을 기본으로 했던 것이다.

그러나 OAuth2 를 통한 사용자 인증은 재전송 위치(Redirect URI)를 전달하면 그 주소로 정보를 받을 수 있어야 한다. 그래서 게이트를 공개된 공간에서 접근할 수 있도록 열어야 한다. 그래서 게이트에 ELB를 붙이고 외부에서 접근할 수 있도록 처리해야 했다. 여기서 매우 중요한 주의사항이 있는데, 그 것은 ELB에서 게이트를  연결할 때 헬스체크(Health Check)를 http:8084/ 로 지정하면 안된다. 게이트가 해당 주소에 대해서 HTTP 200 OK를 응답하지 않고 302 Found를 전달하기 때문이다. 그래서 게이트가 제대로 동작하고 있더라도 ELB에서는 게이트가 제대로 동장하는 지 알지 못한다. 좋은 방법은 헬스체크를 TCP:8084 로 지정하면 된다.

ELB와 Route 53를 위와 같은 형태로 구성했다면, 스핀에커의 사용자 인터페이스(UI) 설정에서 게이트의 주소를 알맞게 변경해 주어야 한다. 이 설정은 /opt/deck/html/settings.js에 있다. Node.js를 이용해서 스핀에커 데크(Deck)를 실행한 경우에는 직접 settings.js를 편집하지 않고 환경 변수에 API_HOST의 주소를 입력해 주면 된다. 하지만 Apache2를 통해서 데크를 실행한 경우에는 환경 변수를 사용하지 않기 떄문에 아래와 같이 settings.js에서 직접 변수의 값을 편집해 주어야한다.

var gateHost = 'https://spin-api.domain.com'

게이트를 외부에서 접속가능하게 만들었다면, 스핀에커의 데크도 외부에서 접근가능하게 만들어 주어야 한다. 데크를 사용자 환경을 담당하고 있는 마이크로서비스이며, HTML과 Javascript로 되어 있다. 그래서 웹어플리케이션 서버(Web Application Server) 대신 웹서버(Web Server)인 Apache2를 사용한다. 그래서 처음 스핀에커를 설치하면 Apache2 설정 (/etc/apache2/available-sites/spinnaker)에 virtualhost로 localhost:9000 이 지정되어 있다. 그러나 외부 접근을 허용하기 위해서는 이 부분의 설정을 바꿔 주어야 한다. 설정에서 <Virtualhost localhost:9000>을 <Virtualhost *:9000> 으로 바꿔서 외부로부터의 연결(Bind)을 허용해 주어야 한다. 만약 포트(Port) 설정 (/etc/apache2/ports.conf)에서도 localhost:9000 이 지정되어 있다면 여기도 *.9000으로 변경해 주어야 한다.

여기까지 완료했다면, https://spin.domain.com 로 접속했을 때 데크와 게이트,그리고 GitHub (또는 적절한 OAuth2 공급자)가 잘 연동하여 인증처리까지 완료되는 것을 볼 수 있다.


 

공지사항