Traefik 미들웨어 - 정방향 인증

23370 단어
이 기사에서는 Traefik 미들웨어 및 라우터를 사용하여 Kubernetes의 많은 애플리케이션에 대한 인증을 관리하는 방법을 설명합니다.

맞춤 인증



사용자 지정 인증은 관리를 외부 서버에 위임합니다.
사용자 지정 인증을 관리하기 위해 ForwardAuth Middleware을 사용합니다.

이 예에서는 JWT 토큰이 포함된 쿠키로 인증을 유지하는 NodeJS Express 서버를 만듭니다.

미들웨어를 통과하는 모든 요청에 ​​대해 JWT 토큰을 확인합니다.

존재하지 않거나 만료된 경우 상태 코드401와 함께 클라이언트에 로그인 양식을 보냅니다.

이전 비밀basic-auth-users-secret을 사용하여 계정을 관리합니다.

이것은 일종의 구현이지만 많은 인증 워크플로를 상상할 수 있습니다.

NodeJS 서버를 고정 표시하는 방법은 설명하지 않지만 많은 자습서를 찾을 수 있습니다.

서버 구현



다음 환경 변수를 정의해야 합니다.
  • COOKIE : JWT를 유지할 쿠키의 이름
  • SECRET : JWT를 관리하는 JWT 암호
  • USERS_PATH : 사용자 파일의 경로
  • VALIDITY : 인증 유효 기간(초)

  • 이 예제에서는 요청 본문이 서버로 전달되지 않기 때문에 양식 값이 HTTP 헤더로 전송됩니다.

    const express = require('express');
    const router = express.Router();
    const jwt = require('jsonwebtoken');
    const atob = require('atob');
    const htpasswd = require('htpasswd-js');
    const cookie = process.env.COOKIE;
    const key = process.env.SECRET;
    const usersPath = process.env.USERS_PATH;
    const validity = parseInt(process.env.VALIDITY);//in seconds
    
    router.all('/', function (req, res, next) {
      let forwarded = {
        method: req.header("X-Forwarded-Method"),
        protocol: req.header("X-Forwarded-Proto"),
        host: req.header("X-Forwarded-Host"),
        uri: req.header("X-Forwarded-Uri"),
        ip: req.header("X-Forwarded-For"),
      };
    
      let url = `${forwarded.protocol}://${forwarded.host}${forwarded.uri}`;
      let roles = req.query.roles;
    
      try {
        if (cookie in req.cookies) {
          // Check JWT token
          let decoded = jwt.verify(req.cookies[cookie], key);
          res.sendStatus(200);
          return;
        }
        else if (forwarded.method.toUpperCase() == "POST") {
          // Check login/password
          let form = req.header("Form-Content");
          form = atob(form);
          let [login, password] = form.split(":");
          let loggedIn = htpasswd.authenticate({
            username: login,
            password,
            file: usersPath
          });
          if (!loggedIn) throw new Error("Not logged in");
          console.log(`Logged in ${login}. Redirect to ${url}`);
          // Create JWT token
          let val = (req.query.validity || validity) * 1000;
          let expire = val + Date.now();
          let token = jwt.sign({ user: login, exp: Math.floor(expire / 1000) }, key);
          res.cookie(cookie, token, { 
            secure: true,
            httpOnly: true,
            expire: new Date(expire)
          });
          res.status(302);
          res.header("Location", url);
          // redirect to initial url
          res.render('redirect', { title: 'Redirect', url });
          return;
        }
      }
      catch (er) { 
        console.error(er);
        res.clearCookie(cookie);
      }
      res.status(401);
      res.render('index', { title: 'Login', url , js: `function sendForm(e) {
        var form = e.currentTarget;
        var xhr = new XMLHttpRequest();
        var formData = new FormData(form);
        xhr.addEventListener('load', function(event) {
          window.location.reload();
        });
        xhr.addEventListener('error', function(event) {
          alert('Oups! Something went wrong.');
        });
        xhr.open('POST', '');
        xhr.setRequestHeader('Form-Content', btoa(form.login.value+':'+form.password.value));
        xhr.send(formData);
    
        e.preventDefault();
        e.stopPropagation();
        return false;
      }`});
    });
    
    module.exports = router;
    


    다음은 index 보기입니다.

    extends layout
    
    block content
      h1= "Login"
      form(method="post", onsubmit="return sendForm(event);")
        input(name="login")
        input(name="password", type="password")
        input(name="redirect", type="hidden", value= url)
        button= "Login"
      script= js
    


    다음은 redirect 보기입니다.

    extends layout
    
    block content
      h1= "Redirection en cours"
      script= `window.location = "${url}";`
    


    인증 미들웨어 배포



    먼저 필요한 환경 변수를 포함하는 비밀을 정의합니다.

    apiVersion: v1
    kind: Secret
    metadata:
      name: custom-auth-secret
    stringData:
      # The cookie name
      COOKIE: my-cookie-name
      # The JWT 
      SECRET: The jwt secret key
      # The JWT and cookie validity in seconds
      VALIDITY: "1800"
    


    서버 배포 및 서비스를 정의해 보겠습니다.

    kind: Deployment
    apiVersion: apps/v1
    metadata:
      name: custom-auth
      labels:
        k8s-app: custom-auth
    spec:
      replicas: 1
      selector:
        matchLabels:
          k8s-app: custom-auth
      template:
        metadata:
          name: custom-auth
          labels:
            k8s-app: custom-auth
        spec:
          containers:
            - name: custom-auth
              image: custom-auth-server:latest
              env:
                - name: USERS_PATH
                  value: /auth/users
              envFrom:
                - secretRef:
                    name: custom-auth-secret
              volumeMounts:
                - name: users
                  mountPath: "/auth"
                  readOnly: true
          volumes:
          - name: users
            secret:
              secretName: basic-auth-users-secret
    ---
    kind: Service
    apiVersion: v1
    metadata:
      name: custom-auth
      labels:
        k8s-app: custom-auth
    spec:
      ports:
        - name: http
          protocol: TCP
          port: 8080
          targetPort: 8080
      selector:
        k8s-app: custom-auth
    


    이제 서버의 동일한 네임스페이스에서 Traefik ForwardAuth 미들웨어를 정의합니다.

    apiVersion: traefik.containo.us/v1alpha1
    kind: Middleware
    metadata:
      name: custom-auth
    spec:
      forwardAuth:
        address: http://auth.traefik:8080
    


    이제 Traefik 라우터에서 사용할 수 있습니다.

    apiVersion: traefik.containo.us/v1alpha1
    kind: IngressRoute
    metadata:
      name: test-custom-auth
    spec:
      entryPoints:
        - web
      routes:
        - kind: Rule
          match: Host(`test-custom-auth.lenra.io`)
          middlewares:
            - name: test-custom-auth
              # Define the middleware namespace if you use is it another one
              # namespace: my-namespace
          services:
            - kind: Service
              name: my-service
              port: 8080
    


    더 나아가



    이 인증 시스템을 개선하기 위해 많은 것을 상상할 수 있습니다.
  • 미들웨어 주소 필드에서 정의하여 허용된 사용자를 사용자 정의 인증 서버로 전달합니다
  • .
  • 한 번만 로그인하기 위해 기본 도메인에서 인증 쿠키를 정의합니다
  • .
  • Github
  • 와 같은 타사 OAuth 공급자로 인증 관리

    좋은 웹페이지 즐겨찾기