Table of contents
Open Table of contents
개요
기존에 개발한 markdown-blog-uploader 웹을 개인서버의 k3s환경을 통해 배포했었다.
하지만 이 웹은 자동으로 git push되기 때문에 아무나 들어오면 안된다. 그러기에 로그인 기능을 추가하여 보안을 강화하기로 했다.
조사해보니, oauth2-proxy라는 미들웨어가 있었다. 이 미들웨어를 웹 앞에 구성해놓으면 내 웹에 접속하기전에 oauth2-proxy가 요청을 가로채고 인증을 위한 웹화면으로 리디렉션된다.
그 다음, 인증이 완료되면 다시 이동되어야할 웹화면으로 다시 리디렉션되는 구조이다. 아키택쳐는 다음과 같다.

이 다음부터 구성법에 대해 설명하겠다.
oauth-proxy 설치
helm 설치
우선 helm을 통해 oauth-proxy를 설치해준다.
helm repo add oauth2-proxy https://oauth2-proxy.github.io/manifests
helm repo update
helm upgrade --install oauth2-proxy \
oauth2-proxy/oauth2-proxy --version "7.12.6" \
-f ./custom_values.yaml --namespace o11y --create-namespace
여기서 custom_values.yaml이 보이는데 여기에 세부 설정을 해준다.
config:
existingSecret: oauth2-secret
configFile: |
provider = "<인증할 서비스 예:google>"
authenticated_emails_file = "<허용 url 리스트 txt파일 path>"
cookie_secure = true
redirect_url = "<인증 후 리다이렉트 url>"
pass_authorization_header = true
set_authorization_header = true
cookie_name = "_oauth2_proxy"
cookie_refresh = "2m"
cookie_expire = "24h"
whitelist_domains = ["<도메인 화이트리스트 예:kro.kr>"]
set_xauthrequest = true
skip_auth_regex = "<특정 url이 포함 시 인증에서 제외>"
extraArgs:
- --reverse-proxy
- --skip-provider-button
- --proxy-prefix=/oauth2
- --custom-templates-dir=/templates
- --cookie-csrf-per-request=true
- --cookie-csrf-expire=5m
extraVolumes:
- name: auth-emails
configMap:
name: oauth2-proxy-auth-emails
extraVolumeMounts:
- name: auth-emails
mountPath: /etc/oauth2-proxy
여기서 중요한 것은 extraArgs의 —cookie-csrf-per-request=true인데, 이 옵션이 없으면 google 로그인 후에, 403에러로 넘어가지 못한다(csrf 토큰발급이 안되기 때문) 그리고 아래에 extraVolumes와 extraVolumeMounts가 보이는데 authenticated_emails_file = “<허용 url 리스트 txt파일 path>” 을 위한 설정들이다. 이 txt파일에 허용할 email들을 입력하면 된다. 그 설정 명령어는
kubectl create configmap oauth2-proxy-auth-emails \
--from-literal=authenticated-emails.txt="jwsjws99@gmail.com" \
-n o11y
위와 같다. 우선 위 명령어로 configmap을 생성하고 oauth2-proxy를 설치하는 것을 추천한다.
yaml 설정
다음 3개의 yaml을 kubectl apply를 통해 설정해준다.
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: forward-auth
namespace: o11y
spec:
forwardAuth:
address: http://oauth2-proxy.o11y.svc.cluster.local/oauth2/
trustForwardHeader: true
authResponseHeaders:
- "X-Auth-Request-User"
- "X-Auth-Request-Email"
- "Authorization"
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: oauth-errors
namespace: o11y
spec:
errors:
status:
- "401-403"
service:
name: oauth2-proxy
port: 80
query: "/oauth2/sign_in?rd={url}"
미들웨어 설정으로써 모든 ingress 설정의 annotation에 이 두 개의 미들웨어를 입력하면 동작한다.
위는 접근 시 oauth.proxy로 넘어가는 미들웨어이고,
아래는 로그인 전 상태의 핸들러이고 리다이렉트 이후 동작(query)을 설정해주는 미들웨어이다.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: oauth2-proxy-ingress
namespace: o11y
annotations:
cert-manager.io/cluster-issuer: letsencrypt-staging
spec:
ingressClassName: traefik
rules:
- host: <리다이렉트 url>
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: oauth2-proxy
port:
number: 80
tls:
- hosts:
- <리다이렉트 url>
secretName: cert-manager-staging
apiVersion: v1
kind: Secret
metadata:
name: oauth2-secret
namespace: o11y
type: Opaque
stringData:
client-id: "<google client id>"
client-secret: "<google secret key>"
cookie-secret: "<랜덤 생성 base64키>"
여기에 랜덤 생성 base64키가 있는데 아래 코드를 이용해서 키 생성해서 넣어도 상관없다.
import os
import base64
# 32바이트(=256비트) 랜덤 키 생성
random_key = os.urandom(32)
# Base64 인코딩
key_base64 = base64.b64encode(random_key).decode()
print("랜덤 키 (Base64):", key_base64)
그리고 현재 google 관련 키들이 있는데 발급방법은 다음 링크에서 따라하면 된다.
마지막으로 실제로 운영하는 웹의 ingress에 이 annotation을 추가해주면 된다.
annotations:
...
traefik.ingress.kubernetes.io/router.middlewares: "o11y-forward-auth@kubernetescrd"
...
tls 설정
위 yaml에서 ingress에 cert-manager.io/cluster-issuer: letsencrypt-staging가 있는 것을 볼 수 있다.
google oauth는 리다이렉션 사이트를 무조건 https로 되어있어야 인증이 된다고 한다.
그러기에 tls설정을 해주어야 한다.
cert manager 설치
우선 helm을 통해 cert manager를 설치해준다.
kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.15.1/cert-manager.yaml
yaml 설정
다음 yaml을 kubectl apply 하여 기본적인 clusterissuer를 설정해준다.
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-staging
spec:
acme:
# server: https://acme-v02.api.letsencrypt.org/directory
server: https://acme-staging-v02.api.letsencrypt.org/directory
email: <사용할 email>
privateKeySecretRef:
name: letsencrypt-staging
solvers:
- http01:
ingress:
class: <사용하고 있는 proxy class 이름 예:traefik>
실제로 사용할 때는 annotation애 다음과 같은 문구를 추가해주고,
annotations:
...
cert-manager.io/cluster-issuer: letsencrypt-prod
...
각 서비스 마다(네임스페이스 별로) Certificate.yaml을 만들어 다음 내용처럼 작성한다.
apiVersion: cert-manager.io/v1
kind: Certificate
metadata:
name: cert-manager-staging
namespace: <서비스의 네임스페이스>
spec:
secretName: cert-manager-staging # 사용할 이름
issuerRef:
name: letsencrypt-staging # cluster issuer 이름 적기
kind: ClusterIssuer
dnsNames:
- <지정할 웹 url>
그리고 실제 서비스의 ingress에서 spec 내에 rules 상단에 다음 내용을 작성한다.
이 때 secretName이 위에 Certificate에서 쓴 이름을 쓰면 된다.
tls:
- hosts:
- <사용할 dns>
secretName: cert-manager-prod
rules: ...
ClusterIssuer부분을 보면 server: https://acme-staging-v02.api.letsencrypt.org/directory 로 되어있고 이름도 -staging이 붙어있다.
이는 실제 acme 인증서발급이 아니라 테스트를 위한 임시 인증서 발급이다. 실제 인증서를 발급하려면 위 주석을 해제하고 아래거는 지운 다음 모든 곳에 -staging이라고 붙은 것들을 지워준다.
참고 사항
위 내용처럼 가짜 인증서를 발급하여 테스트하고자 하려면 맨 위에 cert-manager를 설치할 때 yaml부분에서
cookie_secure = false
로 바꿔주면 임시적으로 잘 동작시킬 수 있다.