<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
  <channel>
    <title>기억저장소</title>
    <link>https://bitgadak.tistory.com/</link>
    <description></description>
    <language>ko</language>
    <pubDate>Wed, 13 May 2026 01:40:47 +0900</pubDate>
    <generator>TISTORY</generator>
    <ttl>100</ttl>
    <managingEditor>빛가닥</managingEditor>
    <item>
      <title>[kubernetes] Ingress-nginx --enable-ssl-passthrough 옵션 적용해보기 (with argocd)</title>
      <link>https://bitgadak.tistory.com/12</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 에서 운영을 하다보면 가끔 필요한 기능이 있다. ssl 을 직접 구현하고 있는 백엔드 서비스에 ingress-nginx 로 https 요청을 바이패스만 해 주고 싶은 기능이 그것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같은 상황에서 필요한 기능이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;687&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dkzziB/btsCg0vTQQa/tcQauaVeq4O8lQcjQarpl1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dkzziB/btsCg0vTQQa/tcQauaVeq4O8lQcjQarpl1/img.png&quot; data-alt=&quot;ingress-nginx 은 https 요청에 바이패스만 수행&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dkzziB/btsCg0vTQQa/tcQauaVeq4O8lQcjQarpl1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdkzziB%2FbtsCg0vTQQa%2FtcQauaVeq4O8lQcjQarpl1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1125&quot; height=&quot;687&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1125&quot; data-origin-height=&quot;687&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;ingress-nginx 은 https 요청에 바이패스만 수행&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress 는 https 요청이 들어왔을 때, ssl 처리를 하지 않고 요청을 그대로 백엔드 서비스로 바이패스하는 상황이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능을 일부 희생해도 된다면 ingress-nginx 옵션만 추가하면 바로 구현할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1703088617997&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nginx.ingress.kubernetes.io/ssl-passthrough: &quot;true&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만, 이 옵션은 default 값으로 비활성화 되어 있기 때문에 ingress 옵션을 수정해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에선, ssl 을 직접 구현하는 argocd 서비스를 대상으로 Ingress-nginx 의 ssl-passthrough 옵션을 적용해 본다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;환경&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nhncloud 환경&lt;/li&gt;
&lt;li&gt;kubernetes: v1.27.3&lt;/li&gt;
&lt;li&gt;ingress-nginx: v1.8.2&lt;/li&gt;
&lt;li&gt;argocd: v2.9.3&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ingress-nginx 설치 및 옵션 적용&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress-nginx 로 Ingress controller 로 사용하려고 한다. 설치 관련 링크: &lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/deploy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.github.io/ingress-nginx/deploy/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703083188717&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Installation Guide - Ingress-Nginx Controller&quot; data-og-description=&quot;Installation Guide There are multiple ways to install the Ingress-Nginx Controller: with Helm, using the project repository chart; with kubectl apply, using YAML manifests; with specific addons (e.g. for minikube or MicroK8s). On most Kubernetes clusters, &quot; data-og-host=&quot;kubernetes.github.io&quot; data-og-source-url=&quot;https://kubernetes.github.io/ingress-nginx/deploy/&quot; data-og-url=&quot;https://kubernetes.github.io/ingress-nginx/deploy/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/deploy/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.github.io/ingress-nginx/deploy/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Installation Guide - Ingress-Nginx Controller&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Installation Guide There are multiple ways to install the Ingress-Nginx Controller: with Helm, using the project repository chart; with kubectl apply, using YAML manifests; with specific addons (e.g. for minikube or MicroK8s). On most Kubernetes clusters,&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubectl 로 설치를 한다. 가이드에 따라 아래와 같이 수행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1703083649912&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.8.2/deploy/static/provider/cloud/deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
   (...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ingress-nginx 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다만 현재 시점에서는&lt;/p&gt;
&lt;pre id=&quot;code_1703088592838&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;nginx.ingress.kubernetes.io/ssl-passthrough: &quot;true&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;옵션을 바로 적용할 수 없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때는, ingress-controller 의 옵션을 변경하면 된다. 옵션 목록: &lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1703088781593&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Command line arguments - Ingress-Nginx Controller&quot; data-og-description=&quot;Command line arguments The following command line arguments are accepted by the Ingress controller executable. They are set in the container spec of the ingress-nginx-controller Deployment manifest Argument Description --annotations-prefix Prefix of the In&quot; data-og-host=&quot;kubernetes.github.io&quot; data-og-source-url=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/&quot; data-og-url=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/cli-arguments/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Command line arguments - Ingress-Nginx Controller&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Command line arguments The following command line arguments are accepted by the Ingress controller executable. They are set in the container spec of the ingress-nginx-controller Deployment manifest Argument Description --annotations-prefix Prefix of the In&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;kubernetes.github.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1703088908455&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;--enable-ssl-passthrough=true&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;를 적용하면 된다. 관련 설명: &lt;a href=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;링크&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 옵션을 확인해 본다. spec.template.spec.containers 를 확인해 본다.&lt;/p&gt;
&lt;pre id=&quot;code_1703089463819&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get deployment/ingress-nginx-controller -n ingress-nginx -o yaml
   (...)
spec:
   (...)
  template:
   (...)
    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
        - --election-id=ingress-nginx-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
   (...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 edit 명령어를 통해 옵션을 적용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1703090979094&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl edit deployment/ingress-nginx-controller -n ingress-nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spec.template.spec.containers 의 args 에 옵션을 추가한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해본다.&lt;/p&gt;
&lt;pre id=&quot;code_1703091329297&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get deployment/ingress-nginx-controller -n ingress-nginx -o yaml
   (...)
spec:
   (...)
  template:
   (...)
    spec:
      containers:
      - args:
        - /nginx-ingress-controller
        - --publish-service=$(POD_NAMESPACE)/ingress-nginx-controller
        - --election-id=ingress-nginx-leader
        - --controller-class=k8s.io/ingress-nginx
        - --ingress-class=nginx
        - --configmap=$(POD_NAMESPACE)/ingress-nginx-controller
        - --validating-webhook=:8443
        - --validating-webhook-certificate=/usr/local/certificates/cert
        - --validating-webhook-key=/usr/local/certificates/key
        - --enable-ssl-passthrough=true
   (...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enable-ssl-passthrough 옵션이 추가되었다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;argocd 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argocd 는 gitOps 를 구현하기 위해 사용되는 툴이다. 다만 이 글은 argocd 에 대한 것이 아니라 자세한 설명은 생략한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argocd 를 설치하면 기본적으로 자체 ssl 인증서를 통해 ssl 을 지원한다. 그래서 테스트로 좋을 것 같아서 선택하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argocd 및 설치 방법에 대한 것은 공식홈페이지에 자세히 나와있다: &lt;a href=&quot;https://argo-cd.readthedocs.io/en/stable/getting_started/&quot; target=&quot;_blank&quot; rel=&quot;noopener&amp;nbsp;noreferrer&quot;&gt;https://argo-cd.readthedocs.io/en/stable/getting_started/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식홈페이지에서 소개하는 방법으로 설치하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 argocd 를 위한 네임스페이스를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1703074485636&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl create namespace argocd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 최신 stable 버전(현시점 v2.9.3)으로 설치를 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1703074574354&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl apply -n argocd -f https://raw.githubusercontent.com/argoproj/argo-cd/stable/manifests/install.yaml
customresourcedefinition.apiextensions.k8s.io/applications.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/applicationsets.argoproj.io created
customresourcedefinition.apiextensions.k8s.io/appprojects.argoproj.io created
serviceaccount/argocd-application-controller created
serviceaccount/argocd-applicationset-controller created
   (...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 다른 것은 중요하지 않고 서버에 접근하는 것만 확인하려고 하니 연결된 서비스를 확인해 본다.&lt;/p&gt;
&lt;pre id=&quot;code_1703076645923&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get service/argocd-server -n argocd -o wide
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE   SELECTOR
argocd-server   ClusterIP   10.254.56.106   &amp;lt;none&amp;gt;        80/TCP,443/TCP   33m   app.kubernetes.io/name=argocd-server&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 서비스를 통해 argocd 서버에 접근 할 수 있다. 설치 시 ClusterIp 타입으로 설정되어 있다. 이를 LoadBalancer 나 NodePort 로 변환하면 외부에서 접근이 가능한데, 이 글에서는 Ingress 로 접근할 것이기 때문에 여기서는 그냥 ClusterIp 타입으로 둔다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;Ingress 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;argocd-server 서비스로 연결할 Ingress 를 만들어 본다. 이 때, ssl-passthrough 옵션을 준다.&lt;/p&gt;
&lt;pre id=&quot;code_1703086636051&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: argocd-server-ingress
  namespace: argocd
  annotations:
    nginx.ingress.kubernetes.io/force-ssl-redirect: &quot;true&quot;
    nginx.ingress.kubernetes.io/ssl-passthrough: &quot;true&quot;
spec:
  ingressClassName: nginx
  rules:
    - host: argocd.crudewebtools.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: argocd-server
                port:
                  name: https&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;namespace: argocd-server 가 argocd 네임스페이스에 존재하기 때문에 통일하였다.&lt;/li&gt;
&lt;li&gt;nginx.ingress.kubernetes.io/force-ssl-redirect: http 요청이 온다면 https 로 리다이렉트 하라는 응답을 전달한다. (308 Permanent Redirect)&lt;/li&gt;
&lt;li&gt;nginx.ingress.kubernetes.io/ssl-passthrough: 위에서 설명했듯이, https 요청을 그대로 백엔드 서비스에 전달한다.&lt;/li&gt;
&lt;li&gt;host 는 필자가 테스트를 위해 임시로 설정한 값이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Ingress 적용&lt;/p&gt;
&lt;pre id=&quot;code_1703138880700&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl apply -f argocd-ingress.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인&lt;/p&gt;
&lt;pre id=&quot;code_1703138988663&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ kubectl get ingress
NAME                    CLASS   HOSTS                      ADDRESS          PORTS   AGE
argocd-server-ingress   nginx   argocd.crudewebtools.com   10.10.10.10      80      38m&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ADDRESS 는 임시 값이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 접근해 본다. 브라우저로 접근이 잘 된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;783&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bCVQCR/btsCqxfGRu3/JkOUATQKwwmaTiq10VNMe0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bCVQCR/btsCqxfGRu3/JkOUATQKwwmaTiq10VNMe0/img.png&quot; data-alt=&quot;브라우저 접근. 리다이렉트 되었음.&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bCVQCR/btsCqxfGRu3/JkOUATQKwwmaTiq10VNMe0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbCVQCR%2FbtsCqxfGRu3%2FJkOUATQKwwmaTiq10VNMe0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1293&quot; height=&quot;783&quot; data-filename=&quot;5.png&quot; data-origin-width=&quot;1293&quot; data-origin-height=&quot;783&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;브라우저 접근. 리다이렉트 되었음.&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1295&quot; data-origin-height=&quot;765&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/R91qL/btsCpO9Gomr/guhgwHTAdPyFl3hOeJU6zK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/R91qL/btsCpO9Gomr/guhgwHTAdPyFl3hOeJU6zK/img.png&quot; data-alt=&quot;curl 로 접근&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/R91qL/btsCpO9Gomr/guhgwHTAdPyFl3hOeJU6zK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FR91qL%2FbtsCpO9Gomr%2FguhgwHTAdPyFl3hOeJU6zK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1295&quot; height=&quot;765&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1295&quot; data-origin-height=&quot;765&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;curl 로 접근&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h4 style=&quot;color: #000000; text-align: start;&quot; data-ke-size=&quot;size20&quot;&gt;참고 사항&lt;/h4&gt;
&lt;p style=&quot;color: #333333; text-align: start;&quot; data-ke-size=&quot;size16&quot;&gt;한편 문서에 따르면 ssl-passthrough 옵션 적용시 다른 어노테이션 옵션을 무시한다고 하며, 성능상 단점이 있다고 하니 유의한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;196&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/XHBkv/btsCpCH9nat/mEf5ujVloEKo7zKRJJkKX1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/XHBkv/btsCpCH9nat/mEf5ujVloEKo7zKRJJkKX1/img.png&quot; data-alt=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#ssl-passthrough&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/XHBkv/btsCpCH9nat/mEf5ujVloEKo7zKRJJkKX1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FXHBkv%2FbtsCpCH9nat%2FmEf5ujVloEKo7zKRJJkKX1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1154&quot; height=&quot;196&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1154&quot; data-origin-height=&quot;196&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/#ssl-passthrough&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;190&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bKaMSK/btsCraLfyXQ/bdQY7sRL4dQHqIX0Aomtd0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bKaMSK/btsCraLfyXQ/bdQY7sRL4dQHqIX0Aomtd0/img.png&quot; data-alt=&quot;https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bKaMSK/btsCraLfyXQ/bdQY7sRL4dQHqIX0Aomtd0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbKaMSK%2FbtsCraLfyXQ%2FbdQY7sRL4dQHqIX0Aomtd0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1148&quot; height=&quot;190&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1148&quot; data-origin-height=&quot;190&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;https://kubernetes.github.io/ingress-nginx/user-guide/tls/#ssl-passthrough&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>kubernetes</category>
      <category>ArgoCD</category>
      <category>enable-ssl-passthrough</category>
      <category>https</category>
      <category>ingress</category>
      <category>Ingress-nginx</category>
      <category>nginx.ingress.kubernetes.io/ssl-passthrough</category>
      <category>SSL</category>
      <category>ssl-passthrough</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/12</guid>
      <comments>https://bitgadak.tistory.com/12#entry12comment</comments>
      <pubDate>Thu, 21 Dec 2023 17:40:38 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot &amp;quot;You are asking Spring Security to ignore Ant [pattern]&amp;quot; 경고 해결(WebSecurity ignoring -&amp;gt; HttpSecurity permitAll)</title>
      <link>https://bitgadak.tistory.com/11</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 의 구조에 대해서 알면 도움이 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Spring Security 공식 문서: &lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-security/reference/servlet/architecture.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- HttpSecurity, WebSecurity 를 통한 SecurityFilterChain 설정: &lt;a href=&quot;https://bitgadak.tistory.com/10&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/10&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring 버전&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring&amp;nbsp;Security&amp;nbsp;의&amp;nbsp;경우&amp;nbsp;Deprecated&amp;nbsp;된&amp;nbsp;부분이&amp;nbsp;있기&amp;nbsp;때문에&amp;nbsp;나중에&amp;nbsp;변경될&amp;nbsp;수&amp;nbsp;있다.&amp;nbsp;이번에&amp;nbsp;사용한&amp;nbsp;버전은&amp;nbsp;아래와&amp;nbsp;같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Boot: 2.7.8&lt;/li&gt;
&lt;li&gt;Spring Security: 5.7.6&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;경고가 발생하는 경우&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 에서는 SecurityFilterChain, WebSecurityCustomizer 를 Bean으로 설정함으로 Security 설정을 할 수 있다. (이전에는 WebSecurityConfigurerAdapter 를 상속하여 설정하는 방식이었는데, deprecated 되었다.) 예를 들어 아래와 같이 설정할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675230756750&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SecurityConfig {

  @Bean
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests()
            .anyRequest().authenticated();  // 모든 요청에 대해 권한 처리
    http.formLogin();  // 로그인페이지
    return http.build();
  }

  @Bean
  public WebSecurityCustomizer webSecurityCustomizer() {
    return (web) -&amp;gt; web.ignoring().antMatchers(&quot;/static/**&quot;);  // &quot;/static/**&quot; 으로 들어오는 요청 무시
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, WebSecurityCustomizer 에서 WebSecurity의 ignoring 을 사용하면 경고 메시지가 뜬다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;223&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EpxHr/btrXPxem2Qe/Ljp01GRKI6D8kUKTgpjLm0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EpxHr/btrXPxem2Qe/Ljp01GRKI6D8kUKTgpjLm0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EpxHr/btrXPxem2Qe/Ljp01GRKI6D8kUKTgpjLm0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEpxHr%2FbtrXPxem2Qe%2FLjp01GRKI6D8kUKTgpjLm0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1754&quot; height=&quot;223&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;1754&quot; data-origin-height=&quot;223&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;pre id=&quot;code_1675230961126&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;You are asking Spring Security to ignore Ant [pattern='/static/**']. This is not recommended -- please use permitAll via HttpSecurity#authorizeHttpRequests instead.&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;메시지가 발생하는 이유&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;찾아보면 메시지가 출력되는 이유를 설명하고 있는 곳을 발견할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-security/issues/10938&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/spring-projects/spring-security/issues/10938&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675233333489&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;object&quot; data-og-title=&quot;WARN when ignoring antMatchers - please use permitAll &amp;middot; Issue #10938 &amp;middot; spring-projects/spring-security&quot; data-og-description=&quot;When I use web.ignoring().antMatchers() I'd like to see a DEBUG message instead of a WARNING for each ignored pattern. I'm confused by the message saying it's not recommended and I shou...&quot; data-og-host=&quot;github.com&quot; data-og-source-url=&quot;https://github.com/spring-projects/spring-security/issues/10938&quot; data-og-url=&quot;https://github.com/spring-projects/spring-security/issues/10938&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/UUYzw/hyRsohJoOU/7rOKWsWa1zXiKxXqA5teMk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600&quot;&gt;&lt;a href=&quot;https://github.com/spring-projects/spring-security/issues/10938&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://github.com/spring-projects/spring-security/issues/10938&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/UUYzw/hyRsohJoOU/7rOKWsWa1zXiKxXqA5teMk/img.png?width=1200&amp;amp;height=600&amp;amp;face=0_0_1200_600');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;WARN when ignoring antMatchers - please use permitAll &amp;middot; Issue #10938 &amp;middot; spring-projects/spring-security&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;When I use web.ignoring().antMatchers() I'd like to see a DEBUG message instead of a WARNING for each ignored pattern. I'm confused by the message saying it's not recommended and I shou...&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;github.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이곳에 따르면 WebSecurity.ignoring() 을 사용할 경우 Spring Security의 어떠한 보호도 받을 수 없기 때문에 permitAll 를 사용하라고 권장하고 있다. 또한 ignoring() 을 사용할 때만 얻을 수 있는 성능상의 이점도 없다고 하며 성능을 고려한 설정 방법 또한 제시하고 있다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;수정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위 사이트를 참고하여 처음의 설정파일을 바꾸면 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1675233761652&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SecurityConfig {

  @Bean
  @Order(2)
  public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
    http.authorizeHttpRequests()
            .anyRequest().authenticated();  // 모든 요청에 대해 권한 처리
    http.formLogin();  // 로그인페이지
    return http.build();
  }

  @Bean
  @Order(1)
  public SecurityFilterChain exceptionSecurityFilterChain(HttpSecurity http) throws Exception {
    http
            .requestMatchers((matchers) -&amp;gt; matchers.antMatchers(&quot;/static/**&quot;))
            .authorizeHttpRequests((authorize) -&amp;gt; authorize.anyRequest().permitAll())
            .requestCache().disable()
            .securityContext().disable()
            .sessionManagement().disable();

    return http.build();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;exceptionSecurityFilterChain 메서드를 통해 새로 생성한 SecurityFilterChain은 기존에 있는 SecurityFilterChain 보다 먼저 경로 체크를 해야 하기 때문에 순서를 정할 필요가 있다. @Order 어노테이션을 통해 /static/** 경로로 오는 요청에 대해 먼저 체크하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사이트에서 언급한 대로 requestCache, securityContext, sessionManagement 에 대한 필터를 제거하도록 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;검사&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만든 SecurityFilterChain 의 경우 어떤 필터가 있는지 기존 필터체인과 비교해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디버거를 이용하여 확인해 보면 기존의 필터체인은 15개의 필터를 가지고 있음을 알 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675234957964&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;filters = {ArrayList@7327}  size = 15
 0 = {DisableEncodeUrlFilter@7339} 
 1 = {WebAsyncManagerIntegrationFilter@7340} 
 2 = {SecurityContextPersistenceFilter@7341} 
 3 = {HeaderWriterFilter@7342} 
 4 = {CsrfFilter@7343} 
 5 = {LogoutFilter@7344} 
 6 = {UsernamePasswordAuthenticationFilter@7345} 
 7 = {DefaultLoginPageGeneratingFilter@7346} 
 8 = {DefaultLogoutPageGeneratingFilter@7347} 
 9 = {RequestCacheAwareFilter@7348} 
 10 = {SecurityContextHolderAwareRequestFilter@7349} 
 11 = {AnonymousAuthenticationFilter@7350} 
 12 = {SessionManagementFilter@7351} 
 13 = {ExceptionTranslationFilter@7352} 
 14 = {AuthorizationFilter@7353}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 생성한 필터체인의 경우는 8개의 필터를 가지고 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675234993707&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;filters = {ArrayList@7324}  size = 8
 0 = {WebAsyncManagerIntegrationFilter@7330} 
 1 = {HeaderWriterFilter@7331} 
 2 = {CsrfFilter@7332} 
 3 = {LogoutFilter@7333} 
 4 = {SecurityContextHolderAwareRequestFilter@7334} 
 5 = {AnonymousAuthenticationFilter@7335} 
 6 = {ExceptionTranslationFilter@7336} 
 7 = {AuthorizationFilter@7337}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;성능에 영향을 줄 수 있는 세션, 캐시, 컨텍스트와 관련된 필터가 사라진 것을 확인할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고) authorizeRequests 가 아닌 authorizeHttpRequests 를 사용하였기 때문에 맨 마지막에 FilterSecurityInterceptor가 아닌 AuthrizationFilter가 있음을 알 수 있다.&lt;/p&gt;</description>
      <category>spring</category>
      <category>Filter</category>
      <category>HttpSecurity</category>
      <category>ignoring</category>
      <category>Spring Boot</category>
      <category>Spring Security</category>
      <category>WebSecurity</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/11</guid>
      <comments>https://bitgadak.tistory.com/11#entry11comment</comments>
      <pubDate>Wed, 1 Feb 2023 16:21:07 +0900</pubDate>
    </item>
    <item>
      <title>Spring Boot 에서 @Component로 추가한 filter가 WebSecurity의 ignoring에 의해 무시되지 않는 이유</title>
      <link>https://bitgadak.tistory.com/10</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 는 WAS 를 내장하고 있어서 WAS 에서 사용하는 Filter 를 Bean 으로 등록하여 쉽게 사용 가능하다. 새 필터를 등록하고 싶다면 Filter 인터페이스를 상속하고 @Component 어노테이션만 붙이면 된다. Spring Boot 가 알아서 필터 체인에 추가시켜 준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데 Spring Security 가 필터를 처리하는 방식을 간과하면 실수할 수 있는 부분이 있다. 예를 들어 위와 같은 방법으로 필터를 추가하고 나서 WebSecurityConfigurerAdapter 에서 WebSecurity를 통해 특정 경로에 대해 ignoring 설정을 한 경우가 있다. 이 때, ignoring 설정한 경로로 접근하면 등록한 필터를 지나지 않을 것이라 생각하기 쉽지만 그렇지 않다. ignoring된 경로로 접근하더라도 필터를 지나는 것을 볼 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 글에서는 이러한 현상이 발생하는 경우와 그 이유를 확인해 보았다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(FilterRegistrationBean 을 이용하여 추가하거나 @WebFilter 를 이용하여 추가한 필터에도 비슷한 현상이 있다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;Spring 버전&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 의 경우 Deprecated 된 부분이 있기 때문에 나중에 변경될 수 있다. 이번에 사용한 버전은 아래와 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Spring Boot: 2.7.8&lt;/li&gt;
&lt;li&gt;Spring Security: 5.7.6&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Spring Security 구조&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-security/reference/servlet/architecture.html&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1675045729135&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Architecture :: Spring Security&quot; data-og-description=&quot;Spring Security&amp;rsquo;s Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first. The following image shows the typical layering of the handlers for a single HTTP request. The client sends a request to the ap&quot; data-og-host=&quot;docs.spring.io&quot; data-og-source-url=&quot;https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot; data-og-url=&quot;https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot; data-og-image=&quot;https://scrap.kakaocdn.net/dn/jJx6t/hyRsi7Rc4H/qokwTQkkqPZqrPk7ybxfIk/img.png?width=698&amp;amp;height=610&amp;amp;face=0_0_698_610,https://scrap.kakaocdn.net/dn/cfcy4O/hyRq1s8A3P/s5WXFkrzp1CfrwEu8ouKIk/img.png?width=741&amp;amp;height=508&amp;amp;face=0_0_741_508,https://scrap.kakaocdn.net/dn/VeblQ/hyRrbP5SN2/URAWixerMEW2WpbABK4ME0/img.png?width=686&amp;amp;height=508&amp;amp;face=0_0_686_508&quot;&gt;&lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url('https://scrap.kakaocdn.net/dn/jJx6t/hyRsi7Rc4H/qokwTQkkqPZqrPk7ybxfIk/img.png?width=698&amp;amp;height=610&amp;amp;face=0_0_698_610,https://scrap.kakaocdn.net/dn/cfcy4O/hyRq1s8A3P/s5WXFkrzp1CfrwEu8ouKIk/img.png?width=741&amp;amp;height=508&amp;amp;face=0_0_741_508,https://scrap.kakaocdn.net/dn/VeblQ/hyRrbP5SN2/URAWixerMEW2WpbABK4ME0/img.png?width=686&amp;amp;height=508&amp;amp;face=0_0_686_508');&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Architecture :: Spring Security&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Spring Security&amp;rsquo;s Servlet support is based on Servlet Filters, so it is helpful to look at the role of Filters generally first. The following image shows the typical layering of the handlers for a single HTTP request. The client sends a request to the ap&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;docs.spring.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Security 의 구조를 먼저 이해할 필요가 있다. Spring Boot 에서 Spring Security 는 두개의 필터 체인을 생성한다. FilterChain(ApplicationFilterChain)과 SecurityFilterChain 이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;811&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cD94sK/btrXChJMQpq/U4p3Yvat8My1kU0WcGyOTK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cD94sK/btrXChJMQpq/U4p3Yvat8My1kU0WcGyOTK/img.png&quot; data-alt=&quot;원본에 화살표 추가. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cD94sK/btrXChJMQpq/U4p3Yvat8My1kU0WcGyOTK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcD94sK%2FbtrXChJMQpq%2FU4p3Yvat8My1kU0WcGyOTK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1072&quot; height=&quot;811&quot; data-filename=&quot;그림1.png&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;811&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원본에 화살표 추가. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;왼쪽에 FilterChain 은 기존의 WAS 의 필터체인(ApplicationFilterChain)이다. 오른쪽의 SecurityFilterChain 은 Spring Security 에서 사용하는 필터체인이고 실제 인증 절차가 진행된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;거칠게 요약을 하면, SecurityFilterChain 이라는 기존 필터체인과 분리된 필터체인이 존재하는데, HTTP 요청을 가져와야 하기 때문에 특별한 필터(DelegatinFilterProxy)를 기존 WAS의 FilterChain 에 추가하여 SecurityFilterChain 으로 연결시켜 주는 역할을 하도록 한 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필터가 요청을 받는 순서는 화살표와 같다. 요청은 ApplicationFilterChain의 순서대로 전달되다가 DelegatinFilterProxy 에서 SecurityFilterChain 로 전달되어 그 내부 필터들을 순서대로 지난다. 그리고 다시 ApplicationFilterChain의 나머지 필터들로 전달된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;SecurityFilterChain 를 통해 WAS 의 필터체인에서 분리되어 좀 더 Spring 스러운 환경에서 복잡한 필터링 조건을 추가하거나 세세한 작업을 할 수 있게 되어있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;한편, SecurityFilterChain 은 여러 개 존재할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림2.png&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;866&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/AGke6/btrXt9UfPPm/54OYUZ2MBNW1bXtsILd3Kk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/AGke6/btrXt9UfPPm/54OYUZ2MBNW1bXtsILd3Kk/img.png&quot; data-alt=&quot;원본에 화살표 추가. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/AGke6/btrXt9UfPPm/54OYUZ2MBNW1bXtsILd3Kk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FAGke6%2FbtrXt9UfPPm%2F54OYUZ2MBNW1bXtsILd3Kk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1158&quot; height=&quot;866&quot; data-filename=&quot;그림2.png&quot; data-origin-width=&quot;1158&quot; data-origin-height=&quot;866&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원본에 화살표 추가. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 다른 경로에 따라 다른 SecurityFilterChain 을 지나도록 할 수 있다. 예를 들어, 위의 그림처럼 /api/** 경로에 대한 SecurityFilterChain과 나머지 경로&lt;span&gt;(/**)&lt;span&gt; &lt;/span&gt;&lt;/span&gt;에 대한 SecurityFilterChain을 다르게 설정했다면, 다른 경로에 대한 요청이 서로 다른 필터들을 통과하게 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. @Component 로 Filter 추가하기&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring Boot 에서 Filter를 추가할 때, 두 필터체인 중 어느 필터체인에 추가되는지 알면 도움이 된다.&lt;/p&gt;
&lt;pre id=&quot;code_1675134336877&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Component
public class MyCustomFilter implement Filter {
   ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위와 같이 @Component 어노테이션과 Filter 인터페이스를 이용하면 필터체인에 추가되는데, 이 경우 WAS의 ApplicationFilterChain에 추가된다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림3.png&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;893&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b5N5KG/btrXwjbjYME/21owxMKnzh0AdRKCINkJwk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b5N5KG/btrXwjbjYME/21owxMKnzh0AdRKCINkJwk/img.png&quot; data-alt=&quot;원본에 가공. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b5N5KG/btrXwjbjYME/21owxMKnzh0AdRKCINkJwk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb5N5KG%2FbtrXwjbjYME%2F21owxMKnzh0AdRKCINkJwk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1072&quot; height=&quot;893&quot; data-filename=&quot;그림3.png&quot; data-origin-width=&quot;1072&quot; data-origin-height=&quot;893&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원본에 가공. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고1) 아래와 같이 FilterRegistrationBean 을 통해 추가하는 경우도 WAS의 ApplicationFilterChain에 추가된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1675134732292&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  @Bean
  public FilterRegistrationBean&amp;lt;MyCustomFilter&amp;gt; filterRegistrationBean() {
    FilterRegistrationBean&amp;lt;MyCustomFilter&amp;gt; registrationBean = new FilterRegistrationBean&amp;lt;&amp;gt;();
    registrationBean.setFilter(new MyCustomFilter());
      ...
    return registrationBean;
  }&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고2) @ServletComponentScan 어노테이션과 @WebFilter 어노테이션을 통해 추가하는 경우도 WAS의 ApplicationFilterChain에 추가된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. HttpSecurity, WebSecurity 설정과 SecurityFilterChain&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Spring security 에 대한 설정을 하는 경우 보통 WebSecurityConfigurerAdapter 를 상속받아 HttpSecurity, WebSecurity 를 방법을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(Spring Security 5.7.6 기준으로 WebSecurityConfigurerAdapter의 경우 deprecated 처리되었다. 이제 SecurityFilterChain와 WebSecurityCustomizer를 이용하여 설정을 하면 되는데, 필터 구조에 대한 부분은 동일하다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 아래와 같이 WebSecurity 를 이용하여 &quot;/api/**&quot; 경로에 대한 ignoring 설정을 할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675134966860&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity httpSecurity) throws Exception {
    httpSecurity.authorizeRequests()
      ...
  }

  @Override
  public void configure(WebSecurity webSecurity) {
    webSecurity
        .ignoring()
        .antMatchers(&quot;/api/**&quot;);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 생성되는 구조를 그림으로 표시하면 아래와 같다. WebSecurity.ignoring() 은 필터가 없는 SecurityFilterChain 을 만들게 되고, &quot;/api/**&quot; 경로에 대한 요청을 이쪽 SecurityFilterChain으로 전달한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림4.png&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;837&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/v9fHX/btrXBmdY3WZ/pBpJkyw59ySjsL0LQfkc51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/v9fHX/btrXBmdY3WZ/pBpJkyw59ySjsL0LQfkc51/img.png&quot; data-alt=&quot;원본에 가공. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/v9fHX/btrXBmdY3WZ/pBpJkyw59ySjsL0LQfkc51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fv9fHX%2FbtrXBmdY3WZ%2FpBpJkyw59ySjsL0LQfkc51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1517&quot; height=&quot;837&quot; data-filename=&quot;그림4.png&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;837&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;원본에 가공. 원본출처: https://docs.spring.io/spring-security/reference/servlet/architecture.html&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;HttpSecurity에 대한 설정도 SecurityFilterChain을 만들고 나머지 요청을 받게 될 것이다. 자세한 설명은 생략한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 원인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 MyCustomFilter가 왜 ignoring 설정에 영향을 받지 않는지 알 수 있다. @Component로 등록한 MyCustomFilter는 WAS의 ApplicationFilterChain 에 등록되어 있기 때문에 요청이 어느 SecurityFilterChain을 타든지와 무관하게 지나게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래는 /api/** 경로에 대한 요청이라도 MyCustomFilter를 포함한 ApplicationFilterChain 의 모든 필터를 지나는 것을 보여준다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림5.png&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;893&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cZwBhg/btrXBBaT5zi/AvKk4g2Z7OztCUxQWNfMVk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cZwBhg/btrXBBaT5zi/AvKk4g2Z7OztCUxQWNfMVk/img.png&quot; data-alt=&quot;1. MyCustomFilter를 ApplicationFilterChain에 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cZwBhg/btrXBBaT5zi/AvKk4g2Z7OztCUxQWNfMVk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcZwBhg%2FbtrXBBaT5zi%2FAvKk4g2Z7OztCUxQWNfMVk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1517&quot; height=&quot;893&quot; data-filename=&quot;그림5.png&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;893&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;1. MyCustomFilter를 ApplicationFilterChain에 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 해결 방법&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyCustomFilter가 WebSecurity.ignoring에 영향을 받게 하기 위해서는 여러 방법이 있겠지만, 가장 간단하다고 생각하는 생각하는 것은 MyCustomFilter 를 ApplicationFilterChain 이 아니라 아래와 같이 HttpSecurity가 생성하는 SecurityFilterChain에 넣는것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;그림6.png&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;893&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/0R3Sg/btrXrUiQ3cz/xCUzRLM35KdcczHTD52TUK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/0R3Sg/btrXrUiQ3cz/xCUzRLM35KdcczHTD52TUK/img.png&quot; data-alt=&quot;2. MyCustomFilter를 SecurityFilterChain에 추가&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/0R3Sg/btrXrUiQ3cz/xCUzRLM35KdcczHTD52TUK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2F0R3Sg%2FbtrXrUiQ3cz%2FxCUzRLM35KdcczHTD52TUK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1517&quot; height=&quot;893&quot; data-filename=&quot;그림6.png&quot; data-origin-width=&quot;1517&quot; data-origin-height=&quot;893&quot;/&gt;&lt;/span&gt;&lt;figcaption&gt;2. MyCustomFilter를 SecurityFilterChain에 추가&lt;/figcaption&gt;
&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우 /api/** 경로의 요청이 MyCustomFilter를 지나지 않게 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;MyCustomFilter를 SecurityFilterChain 에 넣으려면 HttpSecurity를 설정하는 곳에서 필터를 추가하면 된다. 추가하고자 하는 필터가 Spring Security 에서 제공하는 필터거나 이를 상속했다면, http.addFilter(new 필터()) 로 추가할 수 있다. 그렇지 않다면 addFilterAfter(new 필터(), 기존필터.class) 형식이나 addFilterBefore(new 필터(), 기존필터.class) 형식으로 추가할 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1675144735277&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;  @Override
  protected void configure(HttpSecurity http) throws Exception {
       ...
    http.addFilterBefore(new MyCustomFilter(), DisableEncodeUrlFilter.class);
       ...
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기존 필터들의 순서는 &lt;a href=&quot;https://docs.spring.io/spring-security/reference/servlet/configuration/xml-namespace.html#ns-custom-filters&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.spring.io/spring-security/reference/servlet/configuration/xml-namespace.html#ns-custom-filters&lt;/a&gt; 에서 확인할 수 있다.&lt;/p&gt;</description>
      <category>spring</category>
      <category>@Component</category>
      <category>Filter</category>
      <category>HttpSecurity</category>
      <category>securityfilterchain</category>
      <category>Spring Boot</category>
      <category>Spring Security</category>
      <category>WebSecurity</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/10</guid>
      <comments>https://bitgadak.tistory.com/10#entry10comment</comments>
      <pubDate>Tue, 31 Jan 2023 16:16:39 +0900</pubDate>
    </item>
    <item>
      <title>spring security Pre-Authentication 구현 (AbstractPreAutehnticatedProcessingFilter)</title>
      <link>https://bitgadak.tistory.com/9</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;spring security 의 구조를 어느정도 알고 있다고 가정하고 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최종 코드는 여기서 확인할 수 있다: &lt;a href=&quot;https://github.com/bitgadak/tistory-spring-security-preauth-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/bitgadak/tistory-spring-security-preauth-sample&lt;/a&gt;&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;sprint security 필터에는 서비스에서 요구하는 인증 스펙에 따라 다양한 필터를 사용할 수 있다. 인증에 대한 여러가지 방식이 있겠지만 그 중 하나는 인증이 spring 외부에서 진행되는 pre-authentication 방식이다. 예를 들어 외부에 인증 모듈이 있고, spring 은 인증 모듈을 통과하여 인증된 사용자 ID 와 권한만 받아서 처리 하는 것이다. spring 에서는 이런 형태의 시나리오에 대한 사용할 수 있는 필터 AbstractPreAuthenticatedProcessingFilter 가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템의 다른 모듈에 api 를 제공하거나 이미 인증된 사용자에 대해 api 를 제공하는 spring 모듈을 만들어야 할 경우가 생겨 구축한 것을 기록해 보려고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러한 api 서버를 도식화하면 아래와 같다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1403&quot; data-origin-height=&quot;851&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bc2N18/btrvNS6Aw9u/xdKeyNlqpSBHRhQOb4kF51/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bc2N18/btrvNS6Aw9u/xdKeyNlqpSBHRhQOb4kF51/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bc2N18/btrvNS6Aw9u/xdKeyNlqpSBHRhQOb4kF51/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fbc2N18%2FbtrvNS6Aw9u%2FxdKeyNlqpSBHRhQOb4kF51%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1403&quot; height=&quot;851&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1403&quot; data-origin-height=&quot;851&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;목표&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;요청의 HTTP 헤더 값에 따라 권한을 판단할 것이다. 크게 두 가지 종류가 있다. 인증모듈을 통과한 사용자 요청과 시스템 내 다른 모듈에서 부터의 시스템 요청이 있다. 아래와 같이 헤더의 값에 따라서 구분하고 권한을 지정할 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;250&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLGdbO/btrvX7uOYij/p1RjljPu0QMSXaKDBKE8hk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLGdbO/btrvX7uOYij/p1RjljPu0QMSXaKDBKE8hk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLGdbO/btrvX7uOYij/p1RjljPu0QMSXaKDBKE8hk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLGdbO%2FbtrvX7uOYij%2Fp1RjljPu0QMSXaKDBKE8hk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1044&quot; height=&quot;250&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1044&quot; data-origin-height=&quot;250&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;헤더 값에 따라 spring security 에서 제공하는 Authentication 에 사용자 정보와 권한을 넘긴다. 이것은 PreAuthorize 어노테이션의 hasRole() 를 통해 컨트롤러 별로 접근 권한을 구분할 것이다.&amp;nbsp;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;591&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/uvFGa/btrvXVB5dh8/w3opFIUadyyoG1eKOfmK4K/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/uvFGa/btrvXVB5dh8/w3opFIUadyyoG1eKOfmK4K/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/uvFGa/btrvXVB5dh8/w3opFIUadyyoG1eKOfmK4K/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FuvFGa%2FbtrvXVB5dh8%2Fw3opFIUadyyoG1eKOfmK4K%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1804&quot; height=&quot;591&quot; data-filename=&quot;3.png&quot; data-origin-width=&quot;1804&quot; data-origin-height=&quot;591&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예를 들어 일반 사용자는 @PreAuthorize(&quot;hasRole('USER')&quot;) 가 붙은 메소드는 호출 가능하지만, @PreAuthorize(&quot;hasRole('ADMIN')&quot;) 붙은 메소드는 호출 불가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자 요청을 위한 필터와 시스템 요청에 대한 필터 이렇게 두 필터를 AbstractPreAuthenticatedProcessingFilter 를 상속하여 구현하도록 한다. 각각 SystemAuthenticationFilter, UserAuthenticationFilter 이렇게 지정하도록 한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;845&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b3RFPQ/btrvZ4rwhT1/AnuPbfhZ6XyKo6lxKOcaj0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b3RFPQ/btrvZ4rwhT1/AnuPbfhZ6XyKo6lxKOcaj0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b3RFPQ/btrvZ4rwhT1/AnuPbfhZ6XyKo6lxKOcaj0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb3RFPQ%2FbtrvZ4rwhT1%2FAnuPbfhZ6XyKo6lxKOcaj0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1768&quot; height=&quot;845&quot; data-filename=&quot;4.png&quot; data-origin-width=&quot;1768&quot; data-origin-height=&quot;845&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. gradle 을 통한 spring 프로젝트 시작&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;gradle 로 프로젝트를 구축한다. spring boot plugin 을 통해 build.gradle 를 작성한다. 현시점(2020-03-15)에서 최신 버전을 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647321979799&quot; class=&quot;routeros&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;plugins {
    id 'org.springframework.boot' version '2.6.4'
    id 'io.spring.dependency-management' version '1.0.11.RELEASE'
    id 'java'
}

group 'org.example'
version '1.0-SNAPSHOT'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-security'
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

test {
    useJUnitPlatform()
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;spring-boot-starter-security, spring-boot-starter-web 를 추가한다. 둘 다 2.6.4 버전이다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 프로젝트 구성&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같이 구성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647322524300&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;org.example.tistorysecuritypreauthsample
+- config
|  +- auth
|  |  +- SystemAuthenticationFilter.java   // System 요청 filter
|  |  +- SystemAuthenticationProvider.java // SystemAuthenticationFilter 의 Provider
|  |  +- UserAuthenticationFilter.java     // User 요청 filter
|  |  +- UserAuthenticationProvider.java   // UserAuthenticationFilter 의 Provider
|  +- SecurityConfig.java                  // spring security config
+- controller
|  +- SampleController.java                // 테스트 용 controller
+- Application.java&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Application.java 에는 main 메소드가 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1647322841071&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

  public static void main(String[] args) {
    SpringApplication.run(Application.class);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. 설정&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-1. SecurityConfig.java&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 spring security 를 사용하도록 한다. 이를 위해 SecurityConfig.java 를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647322930701&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.csrf().disable();
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;WebSecurityConfigurerAdapter 를 상속하여 구현한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;PreAuthorize 를 사용하기 위해 @EnableGlobalMethodSecurity(prePostEnabled = true) 를 추가한다.&lt;/li&gt;
&lt;li&gt;api 서버로 사용할 것이기 때문에 csrf 를 비활성화한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-2. SystemAuthenticationFilter.java&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시스템 요청을 위한 필터 SystemAuthenticationFilter.java 를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647324393602&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample.config.auth;

import javax.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.stereotype.Component;

@Order(1)
@Component
public class SystemAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {

  public static final String SYSTEM_AUTH_HEADER = &quot;SYSTEM-AUTH-HEADER&quot;;

  public SystemAuthenticationFilter(SystemAuthenticationProvider systemAuthenticationProvider) {
    super.setCheckForPrincipalChanges(true);
    super.setAuthenticationManager(new ProviderManager(systemAuthenticationProvider));
  }

  @Override
  protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
    return request.getHeader(SYSTEM_AUTH_HEADER);
  }

  @Override
  protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
    return &quot;&quot;;
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AbstractPreAuthenticatedProcessingFilter 를 상속하여 필터를 만든다. HttpServletRequest 에서 헤더 정보를 추출한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Order(1): 이 필터가 가장 먼저 나와야 하기 때문에 값을 주어 순서를 고정한다.&lt;/li&gt;
&lt;li&gt;Authentication 을 전달할 Provider 를 주입해준다. 이 Provider 는 3-3 에서 설명한다.&lt;/li&gt;
&lt;li&gt;setCheckForPrincipalChanges(true): true 로 해주어야 이 필터가 사용된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;boot 에서 자동으로 추가하는 filter 를 타고 default Authentication 이 전달되는데, 여기서는 그것을 사용하지 않고, 서비스에 필요한 Authentication 로 갱신해야 하기 때문에 true 로 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;getPreAuthenticatedPrincipal: 여기서 헤더에 있는 값을 추출해 전달한다. SYSTEM-AUTH-HEADER 헤더가 있으면 그 값을, 없으면 null 을 전달한다.&lt;/li&gt;
&lt;li&gt;getPreAuthenticatedCredentials: 이 예시에서는 credential 을 사용하지 않는다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-3. SystemAuthenticationProvider.java&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Authentication 을 시큐리티 컨텍스트로 전달할 Provider 이다. 위의 SystemAuthenticationFilter 에서 이 Provider 를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647325708802&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample.config.auth;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;

@Component
public class SystemAuthenticationProvider implements AuthenticationProvider {

  private static final String SECRET_KEY = &quot;password&quot;;

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    String secretKey = (String) authentication.getPrincipal();

    if (secretKey == null || !secretKey.equals(SECRET_KEY)) {
      return null;
    }

    Map&amp;lt;String, Object&amp;gt; userInfo = Map.of(&quot;id&quot;, &quot;system&quot;);
    List&amp;lt;SimpleGrantedAuthority&amp;gt; roles = Collections.singletonList(
        new SimpleGrantedAuthority(&quot;ROLE_SYSTEM&quot;));
    return new PreAuthenticatedAuthenticationToken(userInfo, null, roles);
  }

  @Override
  public boolean supports(Class&amp;lt;?&amp;gt; authentication) {
    return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;authenticate:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AbstractPreAuthenticatedProcessingFilter 의 getPreAuthenticatedPrincipal() 메소드를 통해 전달되는 값을 여기서 getPrincipal() 를 통해 받는다.&lt;/li&gt;
&lt;li&gt;이 값을 미리 정해진 값 (위에서는 &quot;password&quot;) 과 동일한지 판단한다.&lt;/li&gt;
&lt;li&gt;사용자 정보를 map 으로 만든다. {id=system}&lt;/li&gt;
&lt;li&gt;사용자 권한(role) 을 List&amp;lt;SimpleGrantedAuthority&amp;gt; 로 만든다. [ROLE_SYSTEM]&lt;/li&gt;
&lt;li&gt;PreAuthenticatedAuthenticationToken 으로 만들어 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;supports:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;letter-spacing: 0px;&quot;&gt;AbstractPreAuthenticatedProcessingFilter 사용하는 경우, PreAuthenticatedAuthenticationToken 을 사용하고 있기 때문에, Provider 에서도 이것을 허용하도록 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-4. UserAuthenticationFilter.java&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증된 사용자를 위한 필터 UserAuthenticationFilter.java 를 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647326692314&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample.config.auth;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.springframework.core.annotation.Order;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;

@Order(2)
@Component
public class UserAuthenticationFilter extends AbstractPreAuthenticatedProcessingFilter {

  public static final String USER_ID_HEADER = &quot;USER-ID-HEADER&quot;;
  public static final String USER_ROLES_HEADER = &quot;USER-ROLES-HEADER&quot;;

  public UserAuthenticationFilter(UserAuthenticationProvider userAuthenticationProvider) {
    super.setCheckForPrincipalChanges(true);
    super.setAuthenticationManager(new ProviderManager(userAuthenticationProvider));
  }

  @Override
  protected Object getPreAuthenticatedPrincipal(HttpServletRequest request) {
    String userId = request.getHeader(USER_ID_HEADER);
    List&amp;lt;String&amp;gt; roles = Collections.list(request.getHeaders(USER_ROLES_HEADER));

    if (ObjectUtils.isEmpty(userId) || ObjectUtils.isEmpty(roles)) {
      return null;
    }

    return Map.of(
        &quot;userId&quot;, userId,
        &quot;roles&quot;, roles);
  }

  @Override
  protected Object getPreAuthenticatedCredentials(HttpServletRequest request) {
    return &quot;&quot;;
  }

  @Override
  protected boolean principalChanged(HttpServletRequest request,
      Authentication currentAuthentication) {

    if (currentAuthentication instanceof PreAuthenticatedAuthenticationToken) {
      return false;
    }

    return super.principalChanged(request, currentAuthentication);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;AbstractPreAuthenticatedProcessingFilter 를 상속하여 필터를 만든다. HttpServletRequest 에서 헤더 정보를 추출한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;@Order(2): 이 필터가 두번째로 나와야 하기 때문에 값을 주어 순서를 고정한다.&lt;/li&gt;
&lt;li&gt;Authentication 을 전달할 Provider 를 주입해준다. 이 Provider 는 3-5 에서 설명한다.&lt;/li&gt;
&lt;li&gt;setCheckForPrincipalChanges(true): true 로 해주어야 이 필터가 사용된다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;boot 에서 자동으로 추가하는 filter 를 타고 default Authentication 이 전달되는데, 여기서는 그것을 사용하지 않고, 서비스에 필요한 Authentication 로 갱신해야 하기 때문에 true 로 만든다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;getPreAuthenticatedPrincipal: 여기서 헤더에 있는 사용자 정보값을 추출한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;USER-ID-HEADER 헤더에서 사용자 ID 를, USER-ROLES-HEADER 에서 사용자 권한을 추출한다. (권한은 여러개가 가능하다.) 이 값을 Map 타입으로 반환하여 Provider 에서 받을 수 있도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;getPreAuthenticatedCredentials: 이 예시에서는 credential 을 사용하지 않는다.&lt;/li&gt;
&lt;li&gt;principalChanged: 이 필터 전에 SystemAuthenticationFilter 를 먼저 통과하는데, 앞 필터에서 적절한 system Authentication 이 할당되었다면 무시하고 통과시켜야 한다. 그래서 먼저 만들어진 PreAuthenticatedAuthenticationToken 이 있는지 확인하고 있다면 false 를 반환하여 처리하지 않도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3-5. UserAuthenticationProvider.java&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Authentication 을 시큐리티 컨텍스트로 전달할 Provider 이다. 위의 UserAuthenticationFilter 에서 이 Provider 를 사용한다.&lt;/p&gt;
&lt;pre id=&quot;code_1647327250272&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample.config.auth;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken;
import org.springframework.stereotype.Component;

@Component
public class UserAuthenticationProvider implements AuthenticationProvider {

  @Override
  public Authentication authenticate(Authentication authentication) throws AuthenticationException {
    Map&amp;lt;String, Object&amp;gt; principal = (Map&amp;lt;String, Object&amp;gt;) authentication.getPrincipal();

    String userId = (String) principal.get(&quot;userId&quot;);
    List&amp;lt;String&amp;gt; roles = (List&amp;lt;String&amp;gt;) principal.get(&quot;roles&quot;);

    Map&amp;lt;String, Object&amp;gt; userInfo = Map.of(&quot;id&quot;, userId);
    List&amp;lt;SimpleGrantedAuthority&amp;gt; authorities = roles.stream()
        .map(role -&amp;gt; new SimpleGrantedAuthority(&quot;ROLE_&quot; + role))
        .collect(Collectors.toList());
    return new PreAuthenticatedAuthenticationToken(userInfo, null, authorities);
  }

  @Override
  public boolean supports(Class&amp;lt;?&amp;gt; authentication) {
    return PreAuthenticatedAuthenticationToken.class.isAssignableFrom(authentication);
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;authenticate:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;AbstractPreAuthenticatedProcessingFilter 의 getPreAuthenticatedPrincipal() 메소드를 통해 전달되는 값을 여기서 authentication.getPrincipal() 를 통해 받는다. (필터에서 Map 타입으로 전달되어서 Map 으로 받는다.)&lt;/li&gt;
&lt;li&gt;사용자 ID 를 userId 로 받고, 권한을 roles 를 받는다.&lt;/li&gt;
&lt;li&gt;사용자 정보를 map 으로 만든다. {id=userId}&lt;/li&gt;
&lt;li&gt;사용자 권한(role) 을 List&amp;lt;SimpleGrantedAuthority&amp;gt; 로 만든다. [ROLE_*] (ex: ROLE_USER, ROLE_ADMIN ...)&lt;/li&gt;
&lt;li&gt;PreAuthenticatedAuthenticationToken 으로 만들어 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;supports:
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span&gt;AbstractPreAuthenticatedProcessingFilter 사용하는 경우,&lt;span&gt;&amp;nbsp;&lt;/span&gt;PreAuthenticatedAuthenticationToken 을 사용하고 있기 때문에, Provider 에서도 이것을 허용하도록 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;&lt;span&gt;4. Controller&lt;/span&gt;&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;span&gt;테스트를 할 controller 를 작성한다.&lt;/span&gt;&lt;/p&gt;
&lt;pre id=&quot;code_1647328840547&quot; class=&quot;java&quot; data-ke-language=&quot;java&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;package org.example.tistorysecuritypreauthsample.controller;

import java.util.Collection;
import java.util.Map;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class SampleController {

  @GetMapping(&quot;/sample&quot;)
  public String sample(Authentication authentication) {
    print(authentication);
    return &quot;SUCCESS&quot;;
  }

  @PreAuthorize(&quot;hasRole('USER')&quot;)
  @GetMapping(&quot;/sample-user&quot;)
  public String sampleUser(Authentication authentication) {
    print(authentication);
    return &quot;SUCCESS&quot;;
  }

  @PreAuthorize(&quot;hasRole('ADMIN')&quot;)
  @GetMapping(&quot;/sample-admin&quot;)
  public String sampleAdmin(Authentication authentication) {
    print(authentication);
    return &quot;SUCCESS&quot;;
  }

  @PreAuthorize(&quot;hasRole('SYSTEM')&quot;)
  @GetMapping(&quot;sample-system&quot;)
  public String sampleSystem(Authentication authentication) {
    print(authentication);
    return &quot;SUCCESS&quot;;
  }

  private void print(Authentication authentication) {
    if (authentication != null) {
      Map&amp;lt;?, ?&amp;gt; info = (Map&amp;lt;?, ?&amp;gt;) authentication.getPrincipal(); // @AuthenticationPrincipal
      Collection&amp;lt;? extends GrantedAuthority&amp;gt; authorities = authentication.getAuthorities();
      System.out.println(&quot;ID=&quot; + info.get(&quot;id&quot;) + &quot;, ROLE=&quot; + authorities.toString());
    } else {
      System.out.println(&quot;unauthenticated&quot;);
    }
  }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;시큐리티 컨텍스트에서 Authentication 을 가져오는 것은 여러 방법이 있지만 여기서는 메소드의 파라미터로 받아오는 방식을 택했다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Provider 에서 전달한 Principal 은 @AuthenticationPrincipal 어노테이션을 통해서도 받아올 수 있지만 여기서는 authentication 에서 가져온다. Provider 에서 Map 타입으로 전달되었기 때문에 Map 타입으로 받아온다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. 테스트&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;16 개의 경우의 수 가 있는데, 여기서는 일부만 테스트 해 본다. 전체 경우의 수에 대한 테스트는 &lt;a href=&quot;https://github.com/bitgadak/tistory-spring-security-preauth-sample/blob/master/src/test/java/org/example/tistorysecuritypreauthsample/ApplicationTest.java&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/bitgadak/tistory-spring-security-preauth-sample/blob/master/src/test/java/org/example/tistorysecuritypreauthsample/ApplicationTest.java&lt;/a&gt; 에서 확인 가능하다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-1. 권한 없는 요청 + 제한없는 메소드: 호출 가능&lt;/h4&gt;
&lt;pre id=&quot;code_1647329272368&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample --silent
SUCCESS&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647329353741&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:
unauthenticated&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-2. 권한 없는 요청 + USER 권한 메소드: 호출 불가&lt;/h4&gt;
&lt;pre id=&quot;code_1647329482776&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample-user --silent
{&quot;timestamp&quot;:&quot;2022-03-15T07:31:12.306+00:00&quot;,&quot;status&quot;:403,&quot;error&quot;:&quot;Forbidden&quot;,&quot;path&quot;:&quot;/sample-user&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647329490784&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:
unauthenticated&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-3. USER 권한 요청 + USER 권한 메소드: 호출 가능&lt;/h4&gt;
&lt;pre id=&quot;code_1647329589632&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample-user -H 'USER-ID-HEADER: tester' -H 'USER-ROLES-HEADER: USER' --silent
SUCCESS&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647329605264&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:
ID=tester, ROLE=[ROLE_USER]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-4. USER 권한 요청 + ADMIN 권한 메소드: 호출 불가&lt;/h4&gt;
&lt;pre id=&quot;code_1647329681223&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample-admin -H 'USER-ID-HEADER: tester' -H 'USER-ROLES-HEADER: USER' --silent
{&quot;timestamp&quot;:&quot;2022-03-15T07:34:30.166+00:00&quot;,&quot;status&quot;:403,&quot;error&quot;:&quot;Forbidden&quot;,&quot;path&quot;:&quot;/sample-admin&quot;}&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647329698206&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-5. ADMIN 권한 요청 + USER 권한 메소드: 호출 가능&lt;/h4&gt;
&lt;pre id=&quot;code_1647329969614&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample-user -H 'USER-ID-HEADER: admin' -H 'USER-ROLES-HEADER: USER' -H 'USER-ROLES-HEADER: ADMIN' --silent
SUCCESS&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647330002310&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:
ID=admin, ROLE=[ROLE_USER, ROLE_ADMIN]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-6. ADMIN 권한 요청 + ADMIN 권한 메소드: 호출 가능&lt;/h4&gt;
&lt;pre id=&quot;code_1647329904143&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample-admin -H 'USER-ID-HEADER: admin' -H 'USER-ROLES-HEADER: USER' -H 'USER-ROLES-HEADER: ADMIN' --silent
SUCCESS&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647329923151&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:
ID=admin, ROLE=[ROLE_USER, ROLE_ADMIN]&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;5-7. SYSTEM 권한 요청 + SYSTEM 권한 메소드: 호출 가능&lt;/h4&gt;
&lt;pre id=&quot;code_1647330059734&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ curl http://localhost:8080/sample-system -H 'SYSTEM-AUTH-HEADER: password' --silent
SUCCESS&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1647330077030&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;JAVA 로그:
ID=system, ROLE=[ROLE_SYSTEM]&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;모든 코드는 여기서 확인할 수 있다: &lt;a href=&quot;https://github.com/bitgadak/tistory-spring-security-preauth-sample&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://github.com/bitgadak/tistory-spring-security-preauth-sample&lt;/a&gt;&lt;/p&gt;</description>
      <category>spring</category>
      <category>AbstractPreAuthenticatedProcessingFilter</category>
      <category>AuthenticationProvider</category>
      <category>preauth</category>
      <category>PreAuthorize</category>
      <category>Spring</category>
      <category>Spring Security</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/9</guid>
      <comments>https://bitgadak.tistory.com/9#entry9comment</comments>
      <pubDate>Tue, 15 Mar 2022 16:57:43 +0900</pubDate>
    </item>
    <item>
      <title>nexus3 docker image 로 private docker repository 만들기</title>
      <link>https://bitgadak.tistory.com/8</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;docker 를 많이 사용하다보니 자연스럽게 개인용 private docker repository 의 필요가 생긴다. nexus3 를 이용하면 docker 환경 내에서 docker repository 를 정말 쉽게 만들 수 있다. 그 과정을 기록해 보려고 한다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;환경&lt;/h2&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nexus3 는 docker 로 띄울 것이고, ssl 인증이 필요하기 때문에 nginx 가 이 역할을 맡도록 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nexus 와 nginx 는 모두 docker 컨테이너로 띄울 것이다.&lt;/li&gt;
&lt;li&gt;nexus 컨테이너의 이름은 &lt;b&gt;nexus&lt;/b&gt;, nginx 컨테이너의 이름은 &lt;b&gt;nginx&lt;/b&gt; 로 할 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;도메인명: 이 글에서는 nexus 인터페이스는&amp;nbsp;&lt;b&gt;my-test-nexus.com&lt;/b&gt;, 그리고 새로 만들 private repository 는 &lt;b&gt;my-test-docker.com&lt;/b&gt; 라는 도메인명으로 접근한다고 가정하고 작업한다. (hosts 파일을 고쳐서 테스트한다.)&lt;/li&gt;
&lt;li&gt;서버 정보
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;OS: CentOS 7.9&lt;/li&gt;
&lt;li&gt;IP: 192.168.101.101&lt;/li&gt;
&lt;li&gt;Docker 20.10.12&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;nginx 1.20.2 (&lt;a href=&quot;https://hub.docker.com/_/nginx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/_/nginx&lt;/a&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;용도에 따라 라우팅을 한다. nexus 인터페이스를 위해서 my-test-nexus.com 으로 접근하면 nexus의 8081 로 연결하고, docker repository 를 위해 my-test-docker.com 으로 접근하면 nexus 의 8082 로 전달한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;nexus3 3.37.3 (&lt;a href=&quot;https://hub.docker.com/r/sonatype/nexus3/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/r/sonatype/nexus3/&lt;/a&gt;)
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;8081 포트로 인터페이스를 담당하고, 8082 로 docker repository 를 열 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ssl: repository 에 대해서는 nginx 에서 ssl 인증 처리를 해 줄 것이다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름은 test-chained.crt, test.key (repository 를 위한 것이기 때문에 my-docker.com 으로 도메인 이름이 들어가 있어햐 한다.)&lt;/li&gt;
&lt;li&gt;발급 방법은 아래 포스트를 참고한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;openssl 로 self-signed certificate 만들기(추가적인 작업 필요): &lt;a href=&quot;https://bitgadak.tistory.com/5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;certbot 으로 let's encrypt 인증서 발급받기: &lt;a href=&quot;https://bitgadak.tistory.com/6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/6&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 구조가 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;100.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;794&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/Sp4ad/btrtlrhEulz/umot26Uc2ebDqHi7yw74h0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/Sp4ad/btrtlrhEulz/umot26Uc2ebDqHi7yw74h0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/Sp4ad/btrtlrhEulz/umot26Uc2ebDqHi7yw74h0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FSp4ad%2FbtrtlrhEulz%2Fumot26Uc2ebDqHi7yw74h0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1576&quot; height=&quot;794&quot; data-filename=&quot;100.png&quot; data-origin-width=&quot;1576&quot; data-origin-height=&quot;794&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. nexus 실행&lt;/h2&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1 nexus 컨테이너 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커 이미지가 유지되어야 하기 때문에 도커 볼륨이 필요하다. nexus 라는 이름으로 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1644804362527&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker volume create nexus&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nexus3 3.37.3 이미지로 컨테이너를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1644804407206&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker run -d --name nexus -v nexus:/nexus-data -p 8081-8082:8081-8082 sonatype/nexus3:3.37.3&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;컨테이너 이름은 nexus&lt;/li&gt;
&lt;li&gt;위에서 만든 볼륨을 연결한다.&lt;/li&gt;
&lt;li&gt;nexus 의 기본 포트는 8081 이다. 추가로 8082 포트를 private docker 의 포트로 사용하도록 한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;만약 nexus 포트를 외부에 노출하고 싶지 않다면 -p 옵션은 없어도 된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2 nexus 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nexus 컨테이너가 떴는지 8081 로 접근하여 확인해 보자. 조금 시간이 걸린다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;777&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/tOk95/btrtieP5vc3/klul2ypjhKek5McUaOzvrK/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/tOk95/btrtieP5vc3/klul2ypjhKek5McUaOzvrK/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/tOk95/btrtieP5vc3/klul2ypjhKek5McUaOzvrK/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FtOk95%2FbtrtieP5vc3%2Fklul2ypjhKek5McUaOzvrK%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;777&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;777&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이하 hosts 를 수정하고 my-test-nexus.com 로 진행한다. 로그인을 해 본다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;102.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/O088n/btrtgsotBYN/uRWSJni7bbw3rzsR3Vbcq1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/O088n/btrtgsotBYN/uRWSJni7bbw3rzsR3Vbcq1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/O088n/btrtgsotBYN/uRWSJni7bbw3rzsR3Vbcq1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FO088n%2FbtrtgsotBYN%2FuRWSJni7bbw3rzsR3Vbcq1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;102.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;최초의 임시 비밀번호는 /nexus-data/admin.password 에 적혀있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;console1.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;34&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dAa6rM/btrs5yXoblO/iqRPJJaqRK9QDLHCn043gk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dAa6rM/btrs5yXoblO/iqRPJJaqRK9QDLHCn043gk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dAa6rM/btrs5yXoblO/iqRPJJaqRK9QDLHCn043gk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdAa6rM%2Fbtrs5yXoblO%2FiqRPJJaqRK9QDLHCn043gk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;968&quot; height=&quot;34&quot; data-filename=&quot;console1.png&quot; data-origin-width=&quot;968&quot; data-origin-height=&quot;34&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이걸로 로그인을 할 수 있다. 로그인을 하고 next 를 눌러 진행하다보면 새로운 패스워드를 요구한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;105.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bujCc9/btrs9Kv6XrV/4A4FBRYh5Vl90o2dZoWsY0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bujCc9/btrs9Kv6XrV/4A4FBRYh5Vl90o2dZoWsY0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bujCc9/btrs9Kv6XrV/4A4FBRYh5Vl90o2dZoWsY0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbujCc9%2Fbtrs9Kv6XrV%2F4A4FBRYh5Vl90o2dZoWsY0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;105.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로운 패스워드를 넣고 진행하자. Anonymous Access 에 대해서는 기본값(enable)으로 둔다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;106.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cVdOOS/btrtbZ0QuQq/5ugIOQsgNNHwq1748WBO10/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cVdOOS/btrtbZ0QuQq/5ugIOQsgNNHwq1748WBO10/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cVdOOS/btrtbZ0QuQq/5ugIOQsgNNHwq1748WBO10/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcVdOOS%2FbtrtbZ0QuQq%2F5ugIOQsgNNHwq1748WBO10%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;106.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료되었다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;107.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/b2mus5/btrtifCqDCl/ZTFF5G63knG5WPJA8uDta0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/b2mus5/btrtifCqDCl/ZTFF5G63knG5WPJA8uDta0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/b2mus5/btrtifCqDCl/ZTFF5G63knG5WPJA8uDta0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2Fb2mus5%2FbtrtifCqDCl%2FZTFF5G63knG5WPJA8uDta0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;107.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. Docker repository 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nexus 에서 docker repository 관련하여 직접 repository 를 만들 수도 있고, proxy 를 만들수도 있다. 그런데, 굳이 proxy 가 필요할 것 같진 않아서 직접 repository 를 만드는 것으로 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(repository 와 proxy 를 모두 만들고 group 으로 묶는 방법도 있긴 한데, 무료 라이센스에서는 지원하지 않는다.)&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.1 Blob Stores&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 image 를 저장할 blob stores 를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;108_1.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/JlwjJ/btrtnCiGS5f/sEGKn13IwRjfm1FGPMF4V0/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/JlwjJ/btrtnCiGS5f/sEGKn13IwRjfm1FGPMF4V0/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/JlwjJ/btrtnCiGS5f/sEGKn13IwRjfm1FGPMF4V0/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FJlwjJ%2FbtrtnCiGS5f%2FsEGKn13IwRjfm1FGPMF4V0%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;108_1.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Type 은 File (s3 는 클라우드 저장소) 으로 정하고 이름은 docker-hosted 로 정한다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;enable soft quota 옵션이 있는데, 체크하면 blob 를 모니터링 하다가 용량 초과시 알림을 주는 것 같다. 필요없는 것 같아서 체크하지 않는다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설정에 대한 자세한 설명은 여기서 참고한다: &lt;a href=&quot;https://help.sonatype.com/repomanager3/nexus-repository-administration/repository-management/configuring-blob-stores&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;Configuring&amp;nbsp;Blob&amp;nbsp;Stores&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;109.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bpMzEj/btrtnEgvxUj/j45xSpWDMF9FXviRBKjXDk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bpMzEj/btrtnEgvxUj/j45xSpWDMF9FXviRBKjXDk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bpMzEj/btrtnEgvxUj/j45xSpWDMF9FXviRBKjXDk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbpMzEj%2FbtrtnEgvxUj%2Fj45xSpWDMF9FXviRBKjXDk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;109.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;2.2 Repository&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repository 를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;110_1.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bFdOOs/btrtgr39tTS/2kgzuI2z0nZkNLU88bxxzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bFdOOs/btrtgr39tTS/2kgzuI2z0nZkNLU88bxxzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bFdOOs/btrtgr39tTS/2kgzuI2z0nZkNLU88bxxzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbFdOOs%2Fbtrtgr39tTS%2F2kgzuI2z0nZkNLU88bxxzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;110_1.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker (hosted) 를 선택한다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;111.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dttlVv/btrtfGUdqgx/7JN5wKGbfzi1fYQqlb7pzk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dttlVv/btrtfGUdqgx/7JN5wKGbfzi1fYQqlb7pzk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dttlVv/btrtfGUdqgx/7JN5wKGbfzi1fYQqlb7pzk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdttlVv%2FbtrtfGUdqgx%2F7JN5wKGbfzi1fYQqlb7pzk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;111.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;필요한 정보를 입력하고 저장소를 만든다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;112.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bymMcF/btrtb2YbBeN/3e2xm52M6yUwQZ7rs7BjQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bymMcF/btrtb2YbBeN/3e2xm52M6yUwQZ7rs7BjQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bymMcF/btrtb2YbBeN/3e2xm52M6yUwQZ7rs7BjQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbymMcF%2Fbtrtb2YbBeN%2F3e2xm52M6yUwQZ7rs7BjQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;112.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;113.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/UKmxM/btrtehU4XCu/g1VyeBSnDrkZQw0D1Coum1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/UKmxM/btrtehU4XCu/g1VyeBSnDrkZQw0D1Coum1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/UKmxM/btrtehU4XCu/g1VyeBSnDrkZQw0D1Coum1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FUKmxM%2FbtrtehU4XCu%2Fg1VyeBSnDrkZQw0D1Coum1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;113.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;정보는 다음과 같다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;Name: docker-hosted&lt;/li&gt;
&lt;li&gt;HTTP: 8082 포트. ssl 은 nginx 에서 처리할 것이기 때문에 여기서는 HTTP 로 열어준다.&lt;/li&gt;
&lt;li&gt;Blob store: docker-hosted (위에서 만든 blob store 이다.)&lt;/li&gt;
&lt;li&gt;나머지는 기본값으로 설정한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;repository 를 만들었다. 그러나 아직은 ssl 설정이 안되어서 docker api 로 접근은 안된다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. nginx 설정&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl 인증을 해 주고, 도메인 별 라우팅을 해 주기 위해 nginx 설정을 한다. 여기서는 docker 로 띄우지만, 호스트에 설치하는 것도 상관없다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx docker image 사용에 대해서는 docker hub 를 참고하였다: &lt;a href=&quot;https://hub.docker.com/_/nginx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/_/nginx&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1644815534657&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Nginx - Official Image | Docker Hub&quot; data-og-description=&quot;Quick reference Supported tags and respective Dockerfile links 1.21.6, mainline, 1, 1.21, latest 1.21.6-perl, mainline-perl, 1-perl, 1.21-perl, perl 1.21.6-alpine, mainline-alpine, 1-alpine, 1.21-alpine, alpine 1.21.6-alpine-perl, mainline-alpine-perl, 1-a&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/_/nginx&quot; data-og-url=&quot;https://hub.docker.com/_/nginx&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hub.docker.com/_/nginx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hub.docker.com/_/nginx&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Nginx - Official Image | Docker Hub&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Quick reference Supported tags and respective Dockerfile links 1.21.6, mainline, 1, 1.21, latest 1.21.6-perl, mainline-perl, 1-perl, 1.21-perl, perl 1.21.6-alpine, mainline-alpine, 1-alpine, 1.21-alpine, alpine 1.21.6-alpine-perl, mainline-alpine-perl, 1-a&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&amp;nbsp;nginx&amp;nbsp;설정을&amp;nbsp;하는&amp;nbsp;방법을&amp;nbsp;여러&amp;nbsp;가지&amp;nbsp;소개하고&amp;nbsp;있는데,&amp;nbsp;그&amp;nbsp;중에&amp;nbsp;하나는&amp;nbsp;컨테이너&amp;nbsp;내부의&amp;nbsp;설정파일을&amp;nbsp;직접&amp;nbsp;수정&amp;nbsp;추가&amp;nbsp;하는&amp;nbsp;것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.20.2&amp;nbsp;기준으로&amp;nbsp;Docker&amp;nbsp;run&amp;nbsp;을&amp;nbsp;통해&amp;nbsp;컨테이너를&amp;nbsp;시작하면&amp;nbsp;nginx&amp;nbsp;가&amp;nbsp;시작하면서,&amp;nbsp;/etc/nginx/nginx.conf&amp;nbsp;를&amp;nbsp;불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서&amp;nbsp;/etc/nginx/nginx.conf&amp;nbsp;를&amp;nbsp;직접&amp;nbsp;수정해도&amp;nbsp;되지만,&amp;nbsp;기본&amp;nbsp;nginx.conf&amp;nbsp;파일에서는&amp;nbsp;include&amp;nbsp;/etc/nginx/conf.d/*.conf;&amp;nbsp;라는&amp;nbsp;항목이&amp;nbsp;있어서&amp;nbsp;/etc/nginx/conf.d&amp;nbsp;디렉토리의&amp;nbsp;*.conf&amp;nbsp;의&amp;nbsp;설정&amp;nbsp;정보들을&amp;nbsp;불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서&amp;nbsp;해당&amp;nbsp;디렉토리에&amp;nbsp;필요한&amp;nbsp;설정파일을&amp;nbsp;*.conf&amp;nbsp;라는&amp;nbsp;이름으로&amp;nbsp;추가하는&amp;nbsp;방식을&amp;nbsp;통해&amp;nbsp;설정할&amp;nbsp;수&amp;nbsp;있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너&amp;nbsp;실행시&amp;nbsp;명령어로&amp;nbsp;-v&amp;nbsp;옵션을&amp;nbsp;주어&amp;nbsp;추가하는&amp;nbsp;방법도&amp;nbsp;있겠지만,&amp;nbsp;Dockerfile&amp;nbsp;을&amp;nbsp;통해&amp;nbsp;필요한&amp;nbsp;파일이&amp;nbsp;들어가&amp;nbsp;있는&amp;nbsp;새로운&amp;nbsp;이미지를&amp;nbsp;만드는&amp;nbsp;것이&amp;nbsp;깔끔할&amp;nbsp;것&amp;nbsp;같아서&amp;nbsp;그렇게&amp;nbsp;실행하려고&amp;nbsp;한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.1 nexus.conf&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nexus 컨테이너는 두 개의 포트가 열려있다. 8081 은 nexus 인터페이스용 포트이고, 8082 는 docker repository 로 사용하려고 연 포트이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;my-test-nexus.com 으로 들어오면 8081 로, my-test-docker.com 로 들어오면 8082 로 전달하도록 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;세부적인 설정 제외하고 간단하게 만들면 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1644815835679&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;upstream nexus {
    server nexus:8081;  # docker 로 띄운 nexus 컨테이너 이름
    keepalive 8;
}

upstream nexus-docker-hosted {
    server nexus:8082;  # docker 로 띄운 nexus 컨테이너 이름
    keepalive 8;
}

server {
    listen       80;
    server_name  my-test-nexus.com;

    location / {
        proxy_pass  http://nexus;
    }
}

server {
    listen       443 ssl;
    server_name  my-test-docker.com;

    ssl_certificate     /etc/nginx/test-chained.crt;
    ssl_certificate_key /etc/nginx/test.key;

    location / {
        proxy_pass           http://nexus-docker-hosted;
        client_max_body_size 2048m;  # docker image 업로드시 필요한 업로드 용량 제한 늘리기
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;기타 nginx 설정이 필요하면 추가하도록 하자.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.2 Dockerfile 및 이미지 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl 에 대한 파일과 위에서 만든 nexus.conf 설정파일을 넣어 새로운 이미지를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;디렉토리 구조는 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1644816389285&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/jenkins
+- Dockerfile
+- test-chained.crt
+- test.key
+- conf.d/
   +- nexus.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile 은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1644816415867&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM nginx:1.20.2

COPY *.crt /etc/nginx/
COPY *.key /etc/nginx/
COPY conf.d/ /etc/nginx/conf.d/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1644816728438&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker build -t my-nginx:0.1&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;3.3 nginx 컨테이너 실행&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만든 이미지로 컨테이너를 실행한다. 이 때, nginx 컨테이너 내부에서 nexus 컨테이너를 호출할 수 있어야 하기 때문에 link 옵션을 추가한다.&lt;/p&gt;
&lt;pre id=&quot;code_1644816713270&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d --name nginx -p 80:80 -p 443:443 --link nuxus:nuxus my-nginx:0.1&lt;/code&gt;&lt;/pre&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 확인&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosts 를 설정하고, 테스트 해 보자. https://my-test-docker.com (https!)로 브라우저로 접근하면 아래와 같이 접근이 가능하다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;test2.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bE0lXf/btrtaj6UYTM/UwdtaWKlRiicW3KVhwYGfk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bE0lXf/btrtaj6UYTM/UwdtaWKlRiicW3KVhwYGfk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bE0lXf/btrtaj6UYTM/UwdtaWKlRiicW3KVhwYGfk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbE0lXf%2Fbtrtaj6UYTM%2FUwdtaWKlRiicW3KVhwYGfk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;test2.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 이 때, self-signed certificate 로 만든 인증서를 사용했다면, docker login 을 해 보면 다음과 같이 나오며 접근이 불가하다.&lt;/p&gt;
&lt;pre id=&quot;code_1644817111229&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker login my-test-docker.com
Username: admin
Password: 
Error response from daemon: Get &quot;https://my-test-docker.com/v2/&quot;: x509: certificate signed by unknown authority&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;신뢰할 수 있는 인증서&lt;/b&gt; 로 등록되지 않았기 때문인데, CentOS 에서는 /etc/pki/ca-trust/source/anchors/ 에 추가하고 docker 를 재시작하면 되기는 한데, (&lt;a href=&quot;https://bitgadak.tistory.com/5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/5&lt;/a&gt; 의 4.2 참고) 이 경우 docker 를 재시작해야 하는 문제가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;다른 방법으로는 /etc/docker/certs.d/ 에 도메인명 디렉토리 이름을 정하고 그 안에 *.crt 로 crt 파일을 넣는 방법이 있다. 예를 들어 이 경우 도메인이 my-test-docker.com 이기 때문에 만들어둔 crt 를 /etc/docker/certs.d/my-test-docker.com/ 에 넣어주면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;(참고: &lt;a href=&quot;https://docs.docker.com/engine/security/certificates/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/engine/security/certificates/&lt;/a&gt;)&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;console3.png&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;121&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/COd1O/btrtk2Cytnk/Z0CvqGKbEtbcOcZLYqlZ71/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/COd1O/btrtk2Cytnk/Z0CvqGKbEtbcOcZLYqlZ71/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/COd1O/btrtk2Cytnk/Z0CvqGKbEtbcOcZLYqlZ71/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FCOd1O%2Fbtrtk2Cytnk%2FZ0CvqGKbEtbcOcZLYqlZ71%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;677&quot; height=&quot;121&quot; data-filename=&quot;console3.png&quot; data-origin-width=&quot;677&quot; data-origin-height=&quot;121&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;직접 push 를 해 보자. 위에서 만든 my-nginx:0.1 을 push 해 본다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 tag 명령어로 이미지 이름을 my-test-docker.com/ 으로 시작하도록 변경한다.&lt;/p&gt;
&lt;pre id=&quot;code_1644842722610&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker tag my-nginx:0.1 my-test-docker.com/my-nginx:0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;push 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1644842819370&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ docker push my-test-docker.com/my-nginx:0.1
The push refers to repository [my-test-docker.com/my-nginx]
27b7bb9a8f7f: Pushed 
27b1fc072fad: Pushed 
2318312b5335: Pushed 
c75c795b7d44: Pushed 
4e498ce5ae6a: Pushed 
35437a3771fc: Pushed 
108a6d6c3e60: Pushed 
9ccbab2746b8: Pushed 
2edcec3590a4: Pushed 
0.1: digest: sha256:fab48ce9f740e614056c1d308f54297a865ed91d25fbea5eab79e7389af1bee6 size: 2193&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 들어가 있음을 확인할 수 있다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;test3.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/EOBH3/btrtifbnbHT/ec19TpoWYVQiuj7xSYpy11/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/EOBH3/btrtifbnbHT/ec19TpoWYVQiuj7xSYpy11/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/EOBH3/btrtifbnbHT/ec19TpoWYVQiuj7xSYpy11/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FEOBH3%2FbtrtifbnbHT%2Fec19TpoWYVQiuj7xSYpy11%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1307&quot; height=&quot;875&quot; data-filename=&quot;test3.png&quot; data-origin-width=&quot;1307&quot; data-origin-height=&quot;875&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;</description>
      <category>docker</category>
      <category>docker</category>
      <category>docker private repository</category>
      <category>https</category>
      <category>Nexus</category>
      <category>nexus3</category>
      <category>nginx</category>
      <category>SSL</category>
      <category>넥서스</category>
      <category>도커</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/8</guid>
      <comments>https://bitgadak.tistory.com/8#entry8comment</comments>
      <pubDate>Mon, 14 Feb 2022 21:57:19 +0900</pubDate>
    </item>
    <item>
      <title>docker 환경에서 nginx 로 jenkins ssl 적용</title>
      <link>https://bitgadak.tistory.com/7</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;jenkins 를 외부에 공개해야 할 필요가 있다면 nginx 를 프록시로 사용하여 ssl 을 비롯한 각종 처리를 하게 하는 것도 괜찮은 방법이다. 그런 상황이 생겨서 한 번 적용해 보았다.&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CentOS 7.9&lt;/li&gt;
&lt;li&gt;docker 20.10.12
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;참고: nginx 컨테이너 내부에서 jenkins 컨테이너를 호출할 수 있어야 한다. run 시 --link 옵션을 사용하여 해결하도록 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;nginx&lt;span&gt;&amp;nbsp;&lt;/span&gt;&lt;span style=&quot;color: #000000;&quot;&gt;1.20.2&lt;/span&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;&lt;span style=&quot;color: #000000;&quot;&gt;직접 설치해서 사용해도 되지만 여기서는 docker 로 실행해보려고 한다.&lt;/span&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;jenkins 2.289.3
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;8080 포트로 열려있는 상태이다. 실습에서는 docker 로 띄웠는데, 도커를 이용하지 않아도 상관없다. jenkins 를 도커로 구동하는 것에 대해서는 &lt;a href=&quot;https://bitgadak.tistory.com/3&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/3&lt;/a&gt; 를 참고한다.&lt;/li&gt;
&lt;li&gt;nginx 에서 해당 컨테이너를 찾을 수 있어야 한다. 이름으로 찾을 수 있는데, jenkins 라는 이름으로 추가되어 있다고 한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;이 글에서는 임의로 젠킨스 서버 도메인을 my-jenkins.com 정하고, 해당 도메인으로 요청하면 젠킨스로 전달한다.&lt;/li&gt;
&lt;li&gt;ssl 인증서: nginx 로 ssl 를 적용할 것이기 때문에 필요하다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;이름은 jenkins-chained.crt, jenkins.key 로 준비&lt;/li&gt;
&lt;li&gt;발급 방법에 대해서는 아래를 참고한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;openssl 로 self-signed certificate 만들기: &lt;a href=&quot;https://bitgadak.tistory.com/5&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/5&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;certbot 으로 let's encrypt 인증서 발급받기: &lt;a href=&quot;https://bitgadak.tistory.com/6&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://bitgadak.tistory.com/6&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 형식이 될 것이다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;504&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cWCDpu/btrtifhU5Dd/n6OzKVaPdRVStyN5TjxJk1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cWCDpu/btrtifhU5Dd/n6OzKVaPdRVStyN5TjxJk1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cWCDpu/btrtifhU5Dd/n6OzKVaPdRVStyN5TjxJk1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcWCDpu%2FbtrtifhU5Dd%2Fn6OzKVaPdRVStyN5TjxJk1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1277&quot; height=&quot;504&quot; data-filename=&quot;2.png&quot; data-origin-width=&quot;1277&quot; data-origin-height=&quot;504&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Nginx docker image&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx docker image 사용에 대해서는 docker hub 를 참고하였다: &lt;a href=&quot;https://hub.docker.com/_/nginx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/_/nginx&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1644384313190&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;website&quot; data-og-title=&quot;Nginx - Official Image | Docker Hub&quot; data-og-description=&quot;Quick reference Supported tags and respective Dockerfile links 1.21.6, mainline, 1, 1.21, latest 1.21.6-perl, mainline-perl, 1-perl, 1.21-perl, perl 1.21.6-alpine, mainline-alpine, 1-alpine, 1.21-alpine, alpine 1.21.6-alpine-perl, mainline-alpine-perl, 1-a&quot; data-og-host=&quot;hub.docker.com&quot; data-og-source-url=&quot;https://hub.docker.com/_/nginx&quot; data-og-url=&quot;https://hub.docker.com/_/nginx&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://hub.docker.com/_/nginx&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://hub.docker.com/_/nginx&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Nginx - Official Image | Docker Hub&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;Quick reference Supported tags and respective Dockerfile links 1.21.6, mainline, 1, 1.21, latest 1.21.6-perl, mainline-perl, 1-perl, 1.21-perl, perl 1.21.6-alpine, mainline-alpine, 1-alpine, 1.21-alpine, alpine 1.21.6-alpine-perl, mainline-alpine-perl, 1-a&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;hub.docker.com&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 nginx 설정을 하는 방법을 여러 가지 소개하고 있는데, 그 중에 하나는 컨테이너 내부의 설정파일을 직접 수정 추가 하는 것이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;1.20.2 기준으로 Docker run 을 통해 컨테이너를 시작하면 nginx 가 시작하면서, /etc/nginx/nginx.conf 를 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 /etc/nginx/nginx.conf 를 직접 수정해도 되지만, 기본 nginx.conf 파일에서는 include /etc/nginx/conf.d/*.conf; 라는 항목이 있어서 /etc/nginx/conf.d 디렉토리의 *.conf 의 설정 정보들을 불러온다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그래서 해당 디렉토리에 필요한 설정파일을 *.conf 라는 이름으로 추가하는 방식을 통해 설정할 수 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 실행시 명령어로 -v 옵션을 주어 추가하는 방법도 있겠지만, Dockerfile 을 통해 필요한 파일이 들어가 있는 새로운 이미지를 만드는 것이 깔끔할 것 같아서 그렇게 실행하려고 한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.1 jenkins.conf&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 실행시 /etc/nginx/conf.d/ 디렉토리 내부에 들어갈 젠킨스를 위한 설정 파일이다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jenkins 를 위한 nginx 설정에 대해서는 참고할 만한 정보가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&lt;/a&gt;&lt;/p&gt;
&lt;figure id=&quot;og_1644384322821&quot; contenteditable=&quot;false&quot; data-ke-type=&quot;opengraph&quot; data-ke-align=&quot;alignCenter&quot; data-og-type=&quot;article&quot; data-og-title=&quot;Reverse proxy - Nginx&quot; data-og-description=&quot;In situations where you have existing web sites on your server, you may find it useful to run Jenkins (or the servlet container that Jenkins runs in) behind Nginx, so that you can bind Jenkins to the part of a bigger website that you may have. This section&quot; data-og-host=&quot;www.jenkins.io&quot; data-og-source-url=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&quot; data-og-url=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&quot; data-og-image=&quot;&quot;&gt;&lt;a href=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot; data-source-url=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&quot;&gt;
&lt;div class=&quot;og-image&quot; style=&quot;background-image: url();&quot;&gt;&amp;nbsp;&lt;/div&gt;
&lt;div class=&quot;og-text&quot;&gt;
&lt;p class=&quot;og-title&quot; data-ke-size=&quot;size16&quot;&gt;Reverse proxy - Nginx&lt;/p&gt;
&lt;p class=&quot;og-desc&quot; data-ke-size=&quot;size16&quot;&gt;In situations where you have existing web sites on your server, you may find it useful to run Jenkins (or the servlet container that Jenkins runs in) behind Nginx, so that you can bind Jenkins to the part of a bigger website that you may have. This section&lt;/p&gt;
&lt;p class=&quot;og-host&quot; data-ke-size=&quot;size16&quot;&gt;www.jenkins.io&lt;/p&gt;
&lt;/div&gt;
&lt;/a&gt;&lt;/figure&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기를 참고하지만 불필요한 부분을 제거하고 필요한 부분을 추가해서 수정한다.&lt;/p&gt;
&lt;pre id=&quot;code_1644383051333&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;upstream jenkins {
    server jenkins:8080; # docker 로 띄운 jenkins 컨테이너의 이름이다.
    keepalive 32;
}

# websocket 을 위한 설정
map $http_upgrade $connection_upgrade {
  default upgrade;
  '' close;
}

server {
    listen       80;
    listen       443 ssl;
    server_name  my-jenkins.com; # 요청이 my-jenkins.com 으로 온다면 처리

    ssl_certificate     /etc/nginx/jenkins-chained.crt;
    ssl_certificate_key /etc/nginx/jenkins.key;

    # Jenkins 가 추가로 필요한 header 가 있다면 허용
    ignore_invalid_headers off;

    location / {
        proxy_pass         http://jenkins;
        proxy_redirect     default;
        proxy_http_version 1.1;

        # websocket 을 위한 설정
        proxy_set_header   Connection        $connection_upgrade;
        proxy_set_header   Upgrade           $http_upgrade;

        proxy_set_header   Host              $host;
        proxy_set_header   X-Real-IP         $remote_addr;
        proxy_set_header   X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header   X-Forwarded-Proto $scheme;
        proxy_max_temp_file_size 0;

        #this is the maximum upload size
        client_max_body_size       10m;
        client_body_buffer_size    128k;

        proxy_connect_timeout      90;
        proxy_send_timeout         90;
        proxy_read_timeout         90;
        proxy_buffering            off;
        proxy_request_buffering    off; # HTTP CLI commands
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;서버 도메인은 my-jenkins.com 이다.&lt;/li&gt;
&lt;li&gt;인증서를 추가한다.
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;jenkins-chained.crt 와 jenkins.key 를 /etc/nginx 에 넣을 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;jenkins&amp;nbsp;에서&amp;nbsp;websocket&amp;nbsp;을&amp;nbsp;사용해서&amp;nbsp;관련&amp;nbsp;처리가&amp;nbsp;있다:&amp;nbsp;&lt;a href=&quot;http://nginx.org/en/docs/http/websocket.html&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;http://nginx.org/en/docs/http/websocket.html&lt;/a&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;websoket&amp;nbsp;을&amp;nbsp;사용하기&amp;nbsp;위해서는&amp;nbsp;Connection&amp;nbsp;헤더와&amp;nbsp;Upgrade&amp;nbsp;헤더가&amp;nbsp;젠킨스까지&amp;nbsp;잘&amp;nbsp;타고&amp;nbsp;가야&amp;nbsp;한다.&amp;nbsp;이를&amp;nbsp;위한&amp;nbsp;처리이다.&lt;/li&gt;
&lt;li&gt;map&amp;nbsp;블록:&amp;nbsp;Upgrade&amp;nbsp;가&amp;nbsp;헤더가&amp;nbsp;들어오면&amp;nbsp;기본적으로&amp;nbsp;`$connection_upgrade`&amp;nbsp;변수에&amp;nbsp;upgrade&amp;nbsp;를&amp;nbsp;넣어서&amp;nbsp;전달하지만,&amp;nbsp;Upgrade&amp;nbsp;헤더가&amp;nbsp;없으면&amp;nbsp;변수에&amp;nbsp;close&amp;nbsp;를&amp;nbsp;넣어서&amp;nbsp;전달한다.&lt;/li&gt;
&lt;li&gt;`$connection_upgrade`&amp;nbsp;변수는&amp;nbsp;젠킨스에&amp;nbsp;Connection&amp;nbsp;헤더를&amp;nbsp;통해&amp;nbsp;전달된다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;proxy_max_temp_file_size: 이걸 딱히 수정 안하면 1024MB(default) 이상 파일 다운로드가 안된다고 한다. 0 은 제한하지 않는다는 의미이다.&lt;/li&gt;
&lt;li&gt;$remote_addr: nginx 변수이다. 클라이언트 주소가 전달된다. &lt;a href=&quot;http://nginx.org/en/docs/http/ngx_http_core_module.html#var_remote_addr&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;$proxy_add_x_forwarded_for: nginx 변수이다. 들어오는 X-Forwarded-For 헤더에 $remote_addr 이 콤마로 추가된다. &lt;a href=&quot;https://nginx.org/en/docs/http/ngx_http_proxy_module.html#var_proxy_add_x_forwarded_for&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;참고&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;참고한&amp;nbsp;&lt;a href=&quot;https://www.jenkins.io/doc/book/system-administration/reverse-proxy-configuration-nginx/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;페이지&lt;/a&gt; 에는 있었지만 제거한 설정
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;sendfile 은 파일 전달 시 버퍼에 저장할지 말지에 대한 것인데 userContent 를 사용하지 않을 것이라 불필요하다.&lt;/li&gt;
&lt;li&gt;/static: 굳이 나눌 필요가 없을 것 같아서 제거&lt;/li&gt;
&lt;li&gt;/userContent: 젠킨스의 userContent 기능 역시 사용하지 않기 때문에 아예 제거 (userContent: &lt;a href=&quot;https://www.jenkins.io/doc/book/managing/user-content/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.jenkins.io/doc/book/managing/user-content/&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1.2 Dockerfile 및 이미지 생성&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;ssl 에 대한 파일과, 위에서 만든 jenkins.conf 설정파일을 넣어 새로운 이미지를 만든다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 구조이다.&lt;/p&gt;
&lt;pre id=&quot;code_1644385994185&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/jenkins
+- Dockerfile
+- jenkins-chained.crt
+- jenkins.key
+- conf.d/
   +- jenkins.conf&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile 은 다음과 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1644386120402&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;FROM nginx:1.20.2

COPY *.crt /etc/nginx/
COPY *.key /etc/nginx/
COPY conf.d/ /etc/nginx/conf.d/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1644386157793&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker build -t my-nginx:0.1 .&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. 실행&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;새로 만든 이미지로 컨테이너를 실행한다.&lt;/p&gt;
&lt;pre id=&quot;code_1644386290046&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;docker run -d --name nginx -p 80:80 -p 443:443 --link jenkins:jenkins my-nginx:0.1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 컨테이너 내부에서 jenkins 컨테이너에 접근이 가능해야 하기 때문에 link 옵션을 주었다. 아니면 docker network 를 따로 만들어서 두 컨테이너가 공유하는 방법도 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 https 로 jenkins 에 접근이 가능하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;hosts 를 고치고 https://my-jenkins.com 으로 접근해 보자.&lt;/p&gt;</description>
      <category>docker</category>
      <category>docker</category>
      <category>jenkins</category>
      <category>nginx</category>
      <category>SSL</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/7</guid>
      <comments>https://bitgadak.tistory.com/7#entry7comment</comments>
      <pubDate>Wed, 9 Feb 2022 15:00:08 +0900</pubDate>
    </item>
    <item>
      <title>certbot 으로 let's encrypt 인증서 발급받기</title>
      <link>https://bitgadak.tistory.com/6</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;certbot 을 통해 let's encrypt 인증서를 발급받아보자. 현재로서는 가장 간단한 방법인 것 같다. 공식 사이트 가이드를 따라 진행한다. 아래 페이지를 참고했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;certbot 가이드: &lt;a href=&quot;https://certbot.eff.org/instructions?ws=nginx&amp;amp;os=centosrhel7&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://certbot.eff.org/instructions?ws=nginx&amp;amp;os=centosrhel7&lt;/a&gt; (nginx, centos7)&lt;/li&gt;
&lt;li&gt;snapd 가이드: &lt;a href=&quot;https://snapcraft.io/docs/installing-snap-on-centos&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://snapcraft.io/docs/installing-snap-on-centos&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;준비&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li style=&quot;list-style-type: none;&quot;&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;외부에서 접근 가능한 ip&lt;/li&gt;
&lt;li&gt;도메인 (여기서는 필자가 소유하고 있는 crudewebtools.com 도메인을 사용한다.)&lt;/li&gt;
&lt;li&gt;서버&lt;/li&gt;
&lt;li&gt;열린 http(80) 포트 (인증서 발급에 필요), 열린 https(443) 포트 (인증서 발급 받고 사용할 것)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;환경
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CentOS 7.9&lt;/li&gt;
&lt;li&gt;Nginx 1.20.2&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;참고
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;본문의 명령들은 모두 root 권한으로 실행하였다. root 가 아닌 경우 일부 sudo 로 실행해야 하는 경우가 있다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style2&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;1. Nginx 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서를 nginx 를 통해 등록하려고 한다. 설치되어 있지 않아서 먼저 설치한다. 이미 설치되어 있다면 생략한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 설치는 &lt;a href=&quot;https://www.nginx.com/resources/wiki/start/topics/tutorials/install/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://www.nginx.com/resources/wiki/start/topics/tutorials/install/&lt;/a&gt; 를 참고하여 진행하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 repo 를 등록한다. /etc/yum.repos.d/ 디렉토리에 nginx.repo 를 만들고 아래와 같이 작성한다.&lt;/p&gt;
&lt;pre id=&quot;code_1643466772776&quot; class=&quot;bash&quot; data-ke-language=&quot;bash&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;[nginx]
name=nginx repo
baseurl=https://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 설치하자.&lt;/p&gt;
&lt;pre id=&quot;code_1643466856981&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# yum -y install nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;설치가 되었으면 확인해 보자. 1.20.2 로 설치가 되었다.&lt;/p&gt;
&lt;pre id=&quot;code_1643466940696&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# nginx -v
nginx version: nginx/1.20.2&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;2. nginx 구동&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;외부에서 도메인으로 접근 가능한 상태여야 한다. 서버의 ip 에 대한 도메인 등록은 이미 되어 있다고 가정하고 진행한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 를 시작해서 접근 가능한지 확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643467962228&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# systemctl status nginx&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;http 로 접근해 보자&amp;nbsp;&lt;a href=&quot;http://crudewebtools.com&quot;&gt;http://crudewebtools.com&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;접근해 보면 nginx 가 구동중임을 알 수 있다. 인증서가 없기 때문에 경고 표시가 나온다.&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nginx1.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/desujU/btrr1HUGAvm/wAcHU0KcRcUz28sbOzoLQk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/desujU/btrr1HUGAvm/wAcHU0KcRcUz28sbOzoLQk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/desujU/btrr1HUGAvm/wAcHU0KcRcUz28sbOzoLQk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdesujU%2Fbtrr1HUGAvm%2FwAcHU0KcRcUz28sbOzoLQk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1030&quot; height=&quot;650&quot; data-filename=&quot;nginx1.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. snapd 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;certbot 설치 페이지에서는 snapd 로 설치하는 것을 권장하고 있다. 먼저 snapd 를 설치해 보자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 snapd 가 centos 기본 저장소에 없기 때문에, epel-release 설치가 필요하다.&lt;/p&gt;
&lt;pre id=&quot;code_1643467074199&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# yum -y install epel-release&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 snapd 를 설치하자.&lt;/p&gt;
&lt;pre id=&quot;code_1643467121400&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# yum -y install snapd&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 systemd 에 등록하자.&lt;/p&gt;
&lt;pre id=&quot;code_1643467141447&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# systemctl enable --now snapd.socket
Created symlink from /etc/systemd/system/sockets.target.wants/snapd.socket to /usr/lib/systemd/system/snapd.socket.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 되었는지 확인. enabled 로 되어 있는 것을 볼 수 있다.&lt;/p&gt;
&lt;pre id=&quot;code_1643467156991&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# systemctl list-unit-files | grep snapd.socket
snapd.socket                                  enabled&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 페이지에는 classic snap support 를 위해서 아래와 같이 링크를 만들라고 하고 있다. 정확히 어떤 의미인지는 모르겠는데, 나중에 --classic 옵션을 사용하게 되기 때문에 추가해 두도록 하였다.&lt;/p&gt;
&lt;pre id=&quot;code_1643467274159&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ln -s /var/lib/snapd/snap /snap&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아직 snapd 데몬이 뜨지 않았기 때문에 띄워준다.&lt;/p&gt;
&lt;pre id=&quot;code_1643467334912&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# systemctl start snapd&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;3. certbot 설치&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 snapd 를 최신화하라고 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1643467524566&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# sudo snap install core; sudo snap refresh core&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료되면 확인해보자&lt;/p&gt;
&lt;pre id=&quot;code_1643467731669&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# snap version
snap    2.54.2-1.el7
snapd   2.54.2-1.el7
series  16
centos  7
kernel  3.10.0-1127.19.1.el7.x86_64&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 certbot 을 설치하자&lt;/p&gt;
&lt;pre id=&quot;code_1643468373936&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# sudo snap install --classic certbot
certbot 1.22.0 from Certbot Project (certbot-eff✓) installed&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;certbot 명령어를 사용하기 위해 심볼릭링크를 걸자.&lt;/p&gt;
&lt;pre id=&quot;code_1643468533433&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ln -s /snap/bin/certbot /usr/bin/certbot&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643468570842&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# certbot --version
certbot 1.22.0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 된다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;4. 인증서 발급&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;발급을 받아보자.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;certonly 옵션은 인증서만 발급받는다는 의미이다. 이 옵션이 없으면 자동으로 nginx 설정을 수정해 준다고 하는데, 어떻게 바꿔주는 지 정확하게 인지하고 있는 것이 아니라면 사용하지 않는 것이 나을 것 같다. 필자의 경우 nginx 설정을 직접 해 줄 것이기 때문에 certonly 옵션을 추가한다.&lt;/li&gt;
&lt;li&gt;nginx 에 설정할 것이기 때문에 --nginx 옵션을 추가한다.&lt;/li&gt;
&lt;li&gt;도메인이 crudewebtools.com 이니 이것도 정확하게 써 준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;pre id=&quot;code_1643468640209&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# certbot certonly --nginx -d crudewebtools.com&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;cerbot2.png&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;528&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cLywjk/btrr4wEEc4k/xr07QHJ7lF8HwHOezDaxlk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cLywjk/btrr4wEEc4k/xr07QHJ7lF8HwHOezDaxlk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cLywjk/btrr4wEEc4k/xr07QHJ7lF8HwHOezDaxlk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcLywjk%2Fbtrr4wEEc4k%2Fxr07QHJ7lF8HwHOezDaxlk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1037&quot; height=&quot;528&quot; data-filename=&quot;cerbot2.png&quot; data-origin-width=&quot;1037&quot; data-origin-height=&quot;528&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이메일을 요구한다. 적어준다.&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;약관 동의 요구에는 y 로 동의하고, 이메일을 통해 소식을 전달해 준다는 것에 대해서는 거절하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서는 /etc/letsencrypt/live 아래 위치한다. 정상적으로 발급되었는지 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1643469546838&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# ls -l /etc/letsencrypt/live/crudewebtools.com/
total 4
lrwxrwxrwx 1 root root  41 Jan 30 00:09 cert.pem -&amp;gt; ../../archive/crudewebtools.com/cert1.pem
lrwxrwxrwx 1 root root  42 Jan 30 00:09 chain.pem -&amp;gt; ../../archive/crudewebtools.com/chain1.pem
lrwxrwxrwx 1 root root  46 Jan 30 00:09 fullchain.pem -&amp;gt; ../../archive/crudewebtools.com/fullchain1.pem
lrwxrwxrwx 1 root root  44 Jan 30 00:09 privkey.pem -&amp;gt; ../../archive/crudewebtools.com/privkey1.pem
-rw-r--r-- 1 root root 692 Jan 30 00:09 README&lt;/code&gt;&lt;/pre&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;5. nginx 에 인증서 적용&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 발급 받은 인증서를 nginx 에 적용해 보자. 테스트를 위한 것이라 간단하게 /etc/nginx/conf.d/default.conf 를 고쳐보았다. 이 때, nginx 의 경우 꼭 fullchain.pem 을 사용해야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1643469797197&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen       80;
    listen       443 ssl;
    server_name  crudewebtools.com;

    ssl_certificate     /etc/letsencrypt/live/crudewebtools.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/crudewebtools.com/privkey.pem;

    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }

    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 를 reload 하자.&lt;/p&gt;
&lt;pre id=&quot;code_1643469869352&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# nginx -s reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 https 로 접근해 보자. &lt;a href=&quot;https://crudewebtools.com&quot;&gt;https://crudewebtools.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;nginx2.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;650&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/cfFa31/btrr2WX8w1D/33RtNe9clUiXsqQ5OJbvkk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/cfFa31/btrr2WX8w1D/33RtNe9clUiXsqQ5OJbvkk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/cfFa31/btrr2WX8w1D/33RtNe9clUiXsqQ5OJbvkk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FcfFa31%2Fbtrr2WX8w1D%2F33RtNe9clUiXsqQ5OJbvkk%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;1030&quot; height=&quot;650&quot; data-filename=&quot;nginx2.png&quot; data-origin-width=&quot;1030&quot; data-origin-height=&quot;650&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서가 적용되었다.&lt;/p&gt;
&lt;h2 data-ke-size=&quot;size26&quot;&gt;6. 인증서 갱신에 대한 정보&lt;/h2&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;원래 let's encrypt 에서 발급하는 인증서는 유효기간이 3개월이기 때문에 주기적으로 갱신을 해 주어야 한다. certbot 의 경우에도 갱신을 위한 명령어가 존재한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, 위 과정을 거쳐 certbot 을 통해 인증서를 발급 받은 경우, 자동으로 만료되기 20 일 전 쯤 갱신을 해준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;해당 예약 명령은 systemd.timer 에 등록되어 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해 보자.&lt;/p&gt;
&lt;pre id=&quot;code_1643470962881&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# systemctl list-timers
NEXT                         LEFT       LAST                         PASSED       UNIT                         ACTIVATES
Sun 2022-01-30 01:32:00 KST  49min left n/a                          n/a          snap.certbot.renew.timer     snap.certbot.renew.service
Sun 2022-01-30 22:55:40 KST  22h left   Sat 2022-01-29 22:55:40 KST  1h 46min ago systemd-tmpfiles-clean.timer systemd-tmpfiles-clean.service

2 timers listed.
Pass --all to see loaded but inactive timers, too.&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버에 별 문제가 생기지 않는다면 자동으로 알아서 계속 갱신해 줄 것이다.&lt;/p&gt;</description>
      <category>linux</category>
      <category>CentOS</category>
      <category>centos7</category>
      <category>certbot</category>
      <category>Let's Encrypt</category>
      <category>Lets Encrypt</category>
      <category>letsencrypt</category>
      <category>SSL</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/6</guid>
      <comments>https://bitgadak.tistory.com/6#entry6comment</comments>
      <pubDate>Sun, 30 Jan 2022 00:48:41 +0900</pubDate>
    </item>
    <item>
      <title>openssl 로 self-signed certificate 만들기</title>
      <link>https://bitgadak.tistory.com/5</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;서버에서 사용할 self-signed 인증서를 만드려고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;X.509 extensions&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;보통 브라우저에서는 상관없지만, Docker api 등에서는 인증서의 v3 extension 필드를 요구하는 경우가 있다. 이 경우를 위해 extension 의 일부 필드를 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- basicConstraints: root 인증서에서 사용할 필드이다. 다른 인증서(서버용 인증서) 에 서명을 하는 경우 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- subjectAltName: 서버 인증서에서 사용할 필드이다. 도메인과 일치하는 DNS 값이 있어야 한다. 이전에는 CN(Common Name) 만 도메인과 동일하면 문제가 되지 않았는데, 도커 20.10.8 에서는 api 사용 시 이 값이 도메인과 일치하는 것을 요구해서 추가하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;openssl 에서 extension 파라미터를 넘기는 방법이 여러개 있는데, 여기서는 파일을 전달하는 방식을 사용한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 파일 전달 형식에서도,&lt;code&gt;-extfile {{config 파일명}} -extensions {{section이름}}&lt;/code&gt; 으로 진행하는 방법이 있고, section 이름 없이 필요한 정보만 적은 파일을 &lt;code&gt;-extfile&lt;/code&gt; 로 전달하는 방식이 있는데 후자를 선택한다. (&lt;code&gt;-extensions&lt;/code&gt; 가 없으면 default section (파일 내에서 별도로 section 으로 정의하지 않은 영역) 을 사용하게 된다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extensions 의 종류와 파라미터 전달 방식은 아래를 참고:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- x509 명령어 옵션들: &lt;a href=&quot;https://www.openssl.org/docs/manmaster/man1/openssl-x509.html&quot;&gt;https://www.openssl.org/docs/manmaster/man1/openssl-x509.html&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- v3 conig 에 들어갈 수 있는 값, 포맷 정리: &lt;a href=&quot;https://www.openssl.org/docs/manmaster/man5/x509v3_config.html&quot;&gt;https://www.openssl.org/docs/manmaster/man5/x509v3_config.html&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;진행 환경:&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;동일한 환경을 유지 하기 위해서 docker 컨테이너를 이용하였지만, 굳이 docker 컨테이너 내부에서 진행할 필요는 없다. 혹시 docker 컨테이너를 사용하려고 한다면 결과물을 호스트 서버로 꺼내야 하니까 마운트 하는 것을 잊지 말자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Rocky Linux 8.5 (docker image)&lt;br /&gt;- root 로 진행&lt;br /&gt;- openssl 버전: 1.1.1k (yum install 로 설치한 경우)&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;인증서 내용:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-ke-mobileStyle=&quot;widthOrigin&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;616&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/bvXHfN/btrewg4h7mt/aU9XIAVDkkMJVSyRx3hKr1/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/bvXHfN/btrewg4h7mt/aU9XIAVDkkMJVSyRx3hKr1/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/bvXHfN/btrewg4h7mt/aU9XIAVDkkMJVSyRx3hKr1/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FbvXHfN%2Fbtrewg4h7mt%2FaU9XIAVDkkMJVSyRx3hKr1%2Fimg.png&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot; loading=&quot;lazy&quot; width=&quot;842&quot; height=&quot;616&quot; data-filename=&quot;1.png&quot; data-origin-width=&quot;842&quot; data-origin-height=&quot;616&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. openssl 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yum 으로 설치한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# yum -y install openssl&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전 확인&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# openssl version
OpenSSL 1.1.1k  FIPS 25 Mar 2021&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. root 인증서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;2-1. root 를 위한 키를 우선 만든다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# openssl genrsa -aes256 -out root.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
........+++++
..................................+++++
e is 65537 (0x010001)
Enter pass phrase for root.key:
Verifying - Enter pass phrase for root.key:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 때, pass phrase 를 요구한다. 나중에 서버 인증서 서명시 필요하니 기억해야 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고로 비밀번호가 4개 보다 작으면 아래처럼 에러가 발생한다.&lt;/p&gt;
&lt;pre id=&quot;code_1631241232260&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;UI routines:UI_set_result_ex:result too small:crypto/ui/ui_lib.c:905:You must type in 4 to 1023 characters&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, 일반적으로는 문제가 없지만, 혹시 권한 문제가 있다면 아래처럼 권한을 변경한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# chmod 600 root.key&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그 다음은 CSR(Certificate Signing Request) 을 만들자. 미리 정한 값을 넣는다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;# openssl req -new -key root.key -out root.csr
Enter pass phrase for root.key:
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:KR
State or Province Name (full name) []:
Locality Name (eg, city) [Default City]:Seoul
Organization Name (eg, company) [Default Company Ltd]:bitgadak
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:bitgadak root
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 들어갔는지 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1631240713949&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl req -in root.csr -noout -text
Certificate Request:
    Data:
        Version: 1 (0x0)
        Subject: C = KR, L = Seoul, O = bitgadak, CN = bitgadak root
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    (...생략...)
                Exponent: 65537 (0x10001)
        Attributes:
            a0:00
    Signature Algorithm: sha256WithRSAEncryption
         (...생략...)&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 인증서를 만들어야 하는데, 거기에 전달할 extensions 을 위한 파일을 만들자. 파일명은 root.ext 로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;root 에서 필요한 확장 필드는 basicConstraints 이다.&lt;/p&gt;
&lt;pre id=&quot;code_1631240789757&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# cat root.ext
basicConstraints = CA:TRUE&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 위에서 만든 CSR 과 extension 파일을 가지고 인증서를 만든다. (10년)&lt;/p&gt;
&lt;pre id=&quot;code_1631240854413&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl x509 -req -days 3650 -in root.csr -signkey root.key -extfile root.ext -out root.crt
Signature ok
subject=C = KR, L = Seoul, O = bitgadak, CN = bitgadak root
Getting Private key
Enter pass phrase for root.key:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;내용을 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1631240882965&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl x509 -text -in root.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            (...생략...)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = KR, L = Seoul, O = bitgadak, CN = bitgadak root
        Validity
            Not Before: Sep 10 01:48:21 2021 GMT
            Not After : Sep  8 01:48:21 2031 GMT
        Subject: C = KR, L = Seoul, O = bitgadak, CN = bitgadak root
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    (...생략...)
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Basic Constraints: 
                CA:TRUE
    Signature Algorithm: sha256WithRSAEncryption
         (...생략...)
-----BEGIN CERTIFICATE-----
(...생략...)
-----END CERTIFICATE-----&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;extensiosn 도 잘 들어가 있다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. 서버용 인증서&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;key 를 만들자. 일단은 비밀번호를 추가해서 만들어야 한다.&lt;/p&gt;
&lt;pre id=&quot;code_1631241414219&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl genrsa -aes256 -out server-with-password.key 2048
Generating RSA private key, 2048 bit long modulus (2 primes)
..............+++++
....................................+++++
e is 65537 (0x010001)
Enter pass phrase for server-with-password.key:
Verifying - Enter pass phrase for server-with-password.key:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;비밀번호를 제거한 버전을 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1631241449115&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl rsa -in server-with-password.key -out server.key
Enter pass phrase for server-with-password.key:
writing RSA key&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버용 CSR 생성&lt;/p&gt;
&lt;pre id=&quot;code_1631241479844&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl req -new -key server.key -out server.csr
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:KR
State or Province Name (full name) []: 
Locality Name (eg, city) [Default City]:Seoul
Organization Name (eg, company) [Default Company Ltd]:bitgadak
Organizational Unit Name (eg, section) []:
Common Name (eg, your name or your server's hostname) []:bitgadak.tistory.com
Email Address []:

Please enter the following 'extra' attributes
to be sent with your certificate request
A challenge password []:
An optional company name []:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;인증서 생성시 필요한 extensions 를 적은 파일을 만든다. 이름은 server.ext 로 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기서 필요한 확장 필드는 subjectAltName 이다. 여러 개가 필요한 경우 DNS.1, DNS.2 처럼 넣을 수 있는데, 일단 하나만 적었다. @alt_names 는 alt_names 섹션 ([alt_names] 이하 영역) 을 불러온다는 의미이다.&lt;/p&gt;
&lt;pre id=&quot;code_1631241563027&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# cat server.ext
subjectAltName = @alt_names

[alt_names]
DNS = bitgadak.tistory.com&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이제 인증서를 만들자 (1년)&lt;/p&gt;
&lt;pre id=&quot;code_1631241723330&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl x509 -req -days 365 -in server.csr -extfile server.ext \
  -CA root.crt -CAkey root.key -CAcreateserial -out server.crt
Signature ok
subject=C = KR, L = Seoul, O = bitgadak, CN = bitgadak.tistory.com
Getting CA Private Key
Enter pass phrase for root.key:&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1631241811825&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# openssl x509 -text -in server.crt
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            (...생략...)
        Signature Algorithm: sha256WithRSAEncryption
        Issuer: C = KR, L = Seoul, O = bitgadak, CN = bitgadak root
        Validity
            Not Before: Sep 10 01:58:59 2021 GMT
            Not After : Sep 10 01:58:59 2022 GMT
        Subject: C = KR, L = Seoul, O = bitgadak, CN = bitgadak.tistory.com
        Subject Public Key Info:
            Public Key Algorithm: rsaEncryption
                RSA Public-Key: (2048 bit)
                Modulus:
                    (...생략...)
                Exponent: 65537 (0x10001)
        X509v3 extensions:
            X509v3 Subject Alternative Name: 
                DNS:bitgadak.tistory.com
    Signature Algorithm: sha256WithRSAEncryption
         (...생략...)
-----BEGIN CERTIFICATE-----
(...생략...)
-----END CERTIFICATE-----&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;완료되었다. 이제 서버에 적용하면 된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. 기타&lt;/h3&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-1. nginx 에서 사용시&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 에서 사용하려면 root 인증서 정보가 포함된 crt 를 입력해야 한다고 한다. 순서가 중요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;nginx 에서 사용할 crt 만들기. nginx 입장에서는 이름이 중요하진 않은데, server-chained.crt 라고 정했다.&lt;/p&gt;
&lt;pre id=&quot;code_1631241993810&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;# cat server.crt root.crt &amp;gt; server-chained.crt&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 만든 파일을 nginx 의 설정 파일에서 server 영역 안에 넣어준다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;예시:&lt;/p&gt;
&lt;pre id=&quot;code_1631242086336&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;server {
    listen       443 ssl;
    (...생략...)

    ssl_certificate     /etc/nginx/server-chained.crt;
    ssl_certificate_key /etc/nginx/server.key;

    (...생략...)
}&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;4-2. CentOS 에서 신뢰할 수 있는 인정서 추가&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Self-Signed 의 경우 Docker api 사용시 에러가 나온다. CentOS 라면 root 인증서를 신뢰할 수 있는 인증서로 추가하면 해결된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사용자가 인증서를 추가하는 경우엔&lt;/p&gt;
&lt;pre id=&quot;code_1631242322528&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;/etc/pki/ca-trust/source/anchors/&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;에 추가하고&amp;nbsp;&lt;/p&gt;
&lt;pre id=&quot;code_1631242344368&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code&gt;$ sudo update-ca-trust&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 갱신하면 된다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker 가 인식하려면 Docker 의 재시작이 필요하다. 재시작 없이 리로딩 했으면 좋겠는데, 아직 방법을 모르겠다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;수정사항&lt;/h3&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;2022-02-13: 인증서를 만든 컨테이너 이미지를 CentOS 8 에서 Rocky Linux 로 변경 (CentOS 8 저장소 만료로 인해 yum install 이 동작하지 않음)&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>linux</category>
      <category>basic constraints</category>
      <category>CentOS</category>
      <category>docker</category>
      <category>extension</category>
      <category>https</category>
      <category>OpenSSL</category>
      <category>self-signed certificate</category>
      <category>SSL</category>
      <category>subject alt name</category>
      <category>X509</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/5</guid>
      <comments>https://bitgadak.tistory.com/5#entry5comment</comments>
      <pubDate>Fri, 10 Sep 2021 11:56:20 +0900</pubDate>
    </item>
    <item>
      <title>kubeadm 으로 local kubernetes 환경 구축</title>
      <link>https://bitgadak.tistory.com/4</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 를 제공해 주는 서비스가 많지만 때로는 직접 구축해야 할 필요가 있다. 그런데 막상 설치해보려니 신경 쓸 것도 많고, 선택을 해야 하는 것이 많아서 쉽지가 않았다. 과정을 기록해 보려고 한다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택1 - 컨테이너 런타임: docker&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 는 요구하는 스펙이 맞으면 굳이 특정 컨테이너 런타임을 사용할 필요는 없다. 다만 docker 가 가장 익숙하기 때문에 이를 사용하도록 하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/overview/components/#%EC%BB%A8%ED%85%8C%EC%9D%B4%EB%84%88-%EB%9F%B0%ED%83%80%EC%9E%84&quot;&gt;컨테이너 런타임&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택2 - 설치 도구: kubeadm&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 환경 설치를 위해 사용할 수 있는 도구들이 있다: kubeadm, kOps, kubespray 등. 이 중 공식 사이트에 가장 설명이 잘 되어 있는 것은 kubeadm 인 것 같아서 이를 선택했다.&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;선택3 - 네트워크 애드온: calico&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 의 네트워크를 사용하기 위해서는 kubernetes 네트워크 모델을 구현한 애드온을 실행할 필요가 있다. 많으 종류가 있는데, 그 중 calico 가 간단하고, 많이 사용하고 있어서 선택하였다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고: &lt;a href=&quot;https://kubernetes.io/ko/docs/concepts/cluster-administration/addons/#%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%82%B9%EA%B3%BC-%EB%84%A4%ED%8A%B8%EC%9B%8C%ED%81%AC-%ED%8F%B4%EB%A6%AC%EC%8B%9C&quot;&gt;네트워크 애드온&lt;/a&gt; &lt;a href=&quot;https://kubernetes.io/docs/concepts/cluster-administration/networking/#how-to-implement-the-kubernetes-networking-model&quot;&gt;kubernetes network model&lt;/a&gt;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;환경&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;585&quot; data-filename=&quot;1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/dpMMcM/btreqKwEjjA/D3EIudnKFbiK1tmt6Hlh9k/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/dpMMcM/btreqKwEjjA/D3EIudnKFbiK1tmt6Hlh9k/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/dpMMcM/btreqKwEjjA/D3EIudnKFbiK1tmt6Hlh9k/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FdpMMcM%2FbtreqKwEjjA%2FD3EIudnKFbiK1tmt6Hlh9k%2Fimg.png&quot; data-origin-width=&quot;667&quot; data-origin-height=&quot;585&quot; data-filename=&quot;1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버1: 컨트롤 플레인 (10.10.1.101)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CentOS 7.9&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Docker 20.10.8&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubernetes: 1.22.1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;서버2: 워커 (10.10.1.102)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- CentOS 7.9&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- Docker20.10.8&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubernetes: 1.22.1&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 네트워크:&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- 서버 네트워크 대역: 10.10.1.0/24&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubernetes 네트워크 서비스 대역: 172.22.0.0/16&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;- kubernetes 네트워크 파드 대역(calico): 192.168.0.0/16&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이하 내용에서 별도로 명시하지 않은 과정은 컨트롤플레인 서버와 워커 서버에 모두 적용해야 하는 과정이다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Docker 준비&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;사실 kubernetes 에서는 컨테이너 런타임이 필요한거지 반드시 Docker 를 사용해야 할 필요는 없다. 그러나 Docker 가 가장 익숙하기 때문에 그대로 사용하도록 했다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-1. enabled 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;docker 서비스가 enabled 상태가 아니면 kubeadm 실행시 warning 이 뜬다. 어차피 docker 는 늘 사용할 것이기 때문에, enabled 로 등록하여 부팅시 docker 가 실행되도록 하자.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo systemctl enable docker.service&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ systemctl list-unit-files | grep &quot;docker.service&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;1-2. Docker cgroup 드라이버 설정&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 에서는 도커의 cgroup 관리에 systemd 를 사용하는 것을 권장하고 있다. systemd 가 아니면 kubeadm 구동시 warning 이 뜬다. 무시해도 동작은 하는 것 같지만 맞춰주자.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 중지시키고 디렉토리를 만든다. (디렉토리가 이미 존재할 수 있는데 이 경우 생략)&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo mkdir /etc/docker&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기에 daemon.json 을 작성하자.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ cat &amp;lt;&amp;lt;EOF | sudo tee /etc/docker/daemon.json
{
  &quot;exec-opts&quot;: [&quot;native.cgroupdriver=systemd&quot;],
  &quot;log-driver&quot;: &quot;json-file&quot;,
  &quot;log-opts&quot;: {
    &quot;max-size&quot;: &quot;100m&quot;
  },
  &quot;storage-driver&quot;: &quot;overlay2&quot;
}
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;도커를 다시 실행하고 확인&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ docker info | grep Cgroup&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. iptables 설정&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;br_netfilter 모듈이 활성화 되어야 한다고 한다. CentOS 7.9 기준으로 이미 활성화 되어 있는데, 명시적으로 추가해 주자.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ cat &amp;lt;&amp;lt;EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;또한, iptables 관련 sysctl 설정을 수정해야 한다. 역시 CentOS 7.9 기준으로 활성화 되어 있긴 한데, 영구적용을 위해 sysctl.d 에 설정을 추가하자.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ cat &amp;lt;&amp;lt;EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;리로딩 하여 적용한다.&lt;/p&gt;
&lt;pre class=&quot;gams&quot;&gt;&lt;code&gt;$ sudo sysctl --system&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. swap 끄기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubelet 사용을 위해서는 swap 을 비활성화 하는 것을 권장한다고 한다. 켜 있어도 kubernetes 실행은 가능하지만, 실제 swap 하는 시점에 예상과 다른 동작이 발생할 수 있다고 한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo swapoff -a&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;결과 확인&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ free -h&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;재부팅시에도 반영하려면 &lt;code&gt;/etc/fstab&lt;/code&gt; 도 수정하자.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;4. SELinux 비활성화&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;현재 kublet 스펙에서는 꺼야 한다고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 비활성화&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo setenforce 0&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sestatus | grep &quot;Current mode&quot;&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Current mode 가 &lt;code&gt;enforcing&lt;/code&gt; 에서 &lt;code&gt;permissive&lt;/code&gt; 로 변경되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이후 재부팅 등의 상황에도 적용되도록 &lt;code&gt;/etc/selinux/config&lt;/code&gt; 를 고친다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo sed -i 's/^SELINUX=enforcing$/SELINUX=permissive/' /etc/selinux/config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;SELINUX=enforcing&lt;/code&gt; 로 되어 있던 라인이 &lt;code&gt;SELINUX=permissive&lt;/code&gt; 로 변경되었다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;5. firewalld 설정 (포트 열기)&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;kubernetes 에서 필요한 포트가 있고 calico 에서 필요한 포트가 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;kubernetes 필수 포트: &lt;a href=&quot;https://kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&quot;&gt;https://kubernetes.io/ko/docs/setup/production-environment/tools/kubeadm/install-kubeadm/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;calico 필수 포트: &lt;a href=&quot;https://docs.projectcalico.org/getting-started/kubernetes/requirements&quot;&gt;https://docs.projectcalico.org/getting-started/kubernetes/requirements&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(주의) 컨트롤 플레인 노드와 워커 노드에서 필요로 하는 포트가 다르다.&lt;/b&gt; 이하 각각 설명한다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-1. 컨트롤 플레인 (싱글 컨트롤 패널)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤플레인 서버에서 열지 않으면 문제가 생기는 포트가 있다. firewall-cmd 를 통해 열어준다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo firewall-cmd --permanent --add-port=179/tcp
$ sudo firewall-cmd --permanent --add-port=6443/tcp
$ sudo firewall-cmd --permanent --add-port=10250/tcp&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;179 포트가 안열리면 calico-node 관련 pod 가 ready 상태가 되지 않는다. ready 상태가 아니면, calico 가상 network (예를 들어 &lt;code&gt;192.168.62.129&lt;/code&gt;) 에 접근하려고 하면 접근되지 않는다. kubernetes 내부로 가야할 것이 외부 게이트웨이로 빠져버린다.&lt;/li&gt;
&lt;li&gt;6443 은 kubelete api 를 위한 포트이다.&lt;/li&gt;
&lt;li&gt;10250 을 안열어주면 해당 노드에 돌고 있는 pod 에 대한 &lt;code&gt;kubectl exec&lt;/code&gt; 가 동작하지 않는다. 컨트롤 플레인 에서도 필요한지는 모르겠는데, waring 에 나와서 그냥 열어줬다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 페이지에는 10251, 10252 포트도 필요하다고 하는데, 최소한 현재 구성중인 환경 기준으로는 열지 않아도 실행할 때는 문제가 없었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firewalld 를 재시작 하여 반영한다. (주의: permanent 로 설정하지 않은 설정값들이 있다면 사라진다.)&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo systemctl restart firewalld&lt;/code&gt;&lt;/pre&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;6-2. 워커&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;워커에서 열어야 하는 포트들이 있다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code style=&quot;white-space: pre&quot;;&gt;$ sudo firewall-cmd --permanent --add-port=179/tcp
$ sudo firewall-cmd --permanent --add-port=10250/tcp
$ sudo firewall-cmd --permanent --add-port=30000-32767/tcp
$ sudo firewall-cmd --permanent --add-masquerade&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;179 포트가 안열리면 calico-node 관련 pod 가 ready 상태가 되지 않는다. ready 상태가 아니면, calico 가상 network (예를 들어 &lt;code&gt;192.168.62.129&lt;/code&gt;) 에 접근하려고 하면 접근되지 않는다. kubernetes 내부로 가야할 것이 외부 게이트웨이로 빠져버린다.&lt;/li&gt;
&lt;li&gt;10250 을 안열어주면 해당 노드에 돌고 있는 pod 에 대한 &lt;code&gt;kubectl exec&lt;/code&gt; 가 동작하지 않는다.&lt;/li&gt;
&lt;li&gt;30000-32767 는 NodePort 서비스 실행시 사용된다.&lt;/li&gt;
&lt;li&gt;masquerade 를 안하면 calico 네트워크 대역(&lt;code&gt;192.168&lt;/code&gt;) ip 주소로 들어오는 요청을 처리하지 못한다. (물리 네트워크 대역 &lt;code&gt;10.10&lt;/code&gt; 이 아닌 &lt;code&gt;192.168&lt;/code&gt; 로 들어온다.)&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;firewalld 를 재시작 하여 반영한다. (주의: permanent 로 설정하지 않은 설정값들이 있다면 사라진다.)&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo systemctl restart firewalld&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;7. 설치&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 레포지토리를 추가한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code style=&quot;white-space: pre&quot;;&gt;$ cat &amp;lt;&amp;lt;EOF | sudo tee /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://packages.cloud.google.com/yum/repos/kubernetes-el7-\$basearch
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://packages.cloud.google.com/yum/doc/yum-key.gpg https://packages.cloud.google.com/yum/doc/rpm-package-key.gpg
exclude=kubelet kubeadm kubectl
EOF&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;yum 을 통해 설치한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo yum install -y kubelet kubeadm kubectl --disableexcludes=kubernetes&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 kubelet 을 띄워놓자. 우선 systemctl 에 등록한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo systemctl enable --now kubelet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;버전에 따라서 enable 명령시 --now 옵션이 안먹을 때가 있다. 그럴 때는 그냥 아래처럼 리로드 해준다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo systemctl daemon-reload&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo systemctl restart kubelet&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;code&gt;systemctl&lt;/code&gt; 로 확인해 보면 서비스 실행이 실패했다고 뜨는데, 나중에 kubeadm 이 다 구성되면 자동으로 해결된다.&lt;/p&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;8. 초기화 및 조인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;b&gt;(주의) 이하 진행은 컨트롤 패널 서버와 노드가 다르다.&lt;/b&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 컨트롤 플레인에서 kubernetes 를 초기화하고 워커를 추가하는 개념이다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;8-1. 컨트롤 플레인 노드 (싱글 컨트롤 플레인 초기화)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/&quot;&gt;https://kubernetes.io/docs/setup/production-environment/tools/kubeadm/create-cluster-kubeadm/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;여기 보면 옵션이 많은데 하나를 제외하고는 모두 디폴트 값을 사용한다. &lt;code&gt;--service-cidr&lt;/code&gt; 값만 수정해 주자. 이것은 서비스에 대하여 kubernetes 에서 할당할 ip 범위이다. 기본적으로 &lt;code&gt;10.96.0.0/16&lt;/code&gt; 을 사용하고 있다. 그런데 현재 사설망이 &lt;code&gt;10.&lt;/code&gt; 으로 시작해서 헷갈릴 것 같아서 &lt;code&gt;172.22.0.0/16&lt;/code&gt; 으로 수정한다. (주의: 비슷한 옵션 &lt;code&gt;--pod-network-cidr&lt;/code&gt; 이 있는데, 이건 다른 옵션이다.)&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ sudo kubeadm init --service-cidr=172.22.0.0/16&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;실행하면 아래와 같은 로그가 나오는데, 워커 노드에서 사용해야 하니 기억해 두자&lt;/p&gt;
&lt;pre class=&quot;sql&quot;&gt;&lt;code style=&quot;white-space: pre&quot;;&gt;    (...)
Your Kubernetes control-plane has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

Alternatively, if you are the root user, you can run:

  export KUBECONFIG=/etc/kubernetes/admin.conf

You should now deploy a pod network to the cluster.
Run &quot;kubectl apply -f [podnetwork].yaml&quot; with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 10.10.1.101:6443 --token cmhczt.dqkpcn0fnytprotj \
    --discovery-token-ca-cert-hash sha256:733ec3943111f0bb91e5f37c9afeb878e638af6767e1c9b70e0689e10fec9d10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 현재 사용자 기준으로 위 안내에 나온 걸 실행해 준다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ mkdir -p $HOME/.kube
$ sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
$ sudo chown $(id -u):$(id -g) $HOME/.kube/config&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이러면 root 가 아니더라도 &lt;code&gt;kubectl&lt;/code&gt; 명령어를 사용할 수 있다. (원래도 단순 호출을 할 수는 있었는데, 실행을 하려면 막혔다.)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이렇게 kubeadm 을 통한 컨트롤플레인 초기화가 완료되었다. 다만 컨트롤플레인에는 calico 설치도 필요하다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;calico 의 경우 노드 서버 수 50개 기준으로 다른 가이드를 주고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&lt;a href=&quot;https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises&quot;&gt;https://docs.projectcalico.org/getting-started/kubernetes/self-managed-onprem/onpremises&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이 경우는 50 개 미만의 가이드를 따른다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;먼저 설정 파일을 받는다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ curl https://docs.projectcalico.org/manifests/calico.yaml -O&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그리고 적용한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ kubectl apply -f calico.yaml&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;적용 후 시간이 지나면 pending 상태였던 coredns 도 Running 을 바뀌고 calico 관련 서비스가 돌고 있는 것을 확인할 수 있다.&lt;/p&gt;
&lt;h4 data-ke-size=&quot;size20&quot;&gt;8-2. 워커 노드 (조인하기)&lt;/h4&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;위에서 컨트롤플레인 초기화시 나온 로그를 참고하여 적용한다.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code style=&quot;white-space: pre&quot;;&gt;$ sudo kubeadm join 10.10.1.101:6443 --token cmhczt.dqkpcn0fnytprotj \
    --discovery-token-ca-cert-hash sha256:733ec3943111f0bb91e5f37c9afeb878e638af6767e1c9b70e0689e10fec9d10&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;아래와 같은 로그가 나오며 완료된다.&lt;/p&gt;
&lt;pre class=&quot;routeros&quot;&gt;&lt;code&gt;This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the control-plane to see this node join the cluster.&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;9. 확인&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨트롤 플레인 서버에서 확인해 보자.&lt;/p&gt;
&lt;pre class=&quot;shell&quot; data-ke-language=&quot;shell&quot;&gt;&lt;code&gt;$ kubectl get node -o wide&lt;/code&gt;&lt;/pre&gt;
&lt;pre id=&quot;code_1631079642651&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre&quot;;&gt;NAME      STATUS   ROLES                  AGE   VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION           CONTAINER-RUNTIME
server1   Ready    control-plane,master   12h   v1.22.1   10.10.1.101     &amp;lt;none&amp;gt;        CentOS Linux 7 (Core)   3.10.0-1160.el7.x86_64   docker://20.10.8
server2   Ready    &amp;lt;none&amp;gt;                 12h   v1.22.1   10.10.1.102     &amp;lt;none&amp;gt;        CentOS Linux 7 (Core)   3.10.0-1160.el7.x86_64   docker://20.10.8&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;잘 적용되었다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;참고:&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;현 구조상, 컨트롤 플레인에는 pod 가 뜨지 않는다. pod 를 뜨게 설정할 수 있지만 생략한다.&lt;/li&gt;
&lt;/ul&gt;</description>
      <category>kubernetes</category>
      <category>calico</category>
      <category>docker</category>
      <category>kubeadm</category>
      <category>kubernetes</category>
      <category>도커</category>
      <category>칼리코</category>
      <category>쿠버네티스</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/4</guid>
      <comments>https://bitgadak.tistory.com/4#entry4comment</comments>
      <pubDate>Wed, 8 Sep 2021 14:44:19 +0900</pubDate>
    </item>
    <item>
      <title>DooD (docker-outside-of-docker) 를 통해 Jenkins 컨테이너에서 docker 사용하기</title>
      <link>https://bitgadak.tistory.com/3</link>
      <description>&lt;p data-ke-size=&quot;size16&quot;&gt;Jenkins 를 docker 를 통해 컨테이너로 띄우는 것이 아주 편해져서 이제는 docker 로 운영하고 있다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;그런데, Jenkins 상에서 Docker&amp;nbsp;관련 작업이 필요하여 단순하게 컨테이너 내부에서 설치를 하려고 했는데, 내부에서 docker 를 띄우는 것과 관련하여 이것저것 이슈가 있다고 한다. (&lt;a href=&quot;https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://jpetazzo.github.io/2015/09/03/do-not-use-docker-in-docker-for-ci/&lt;/a&gt;)&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;관련해서 좀 더 찾아보았는데, 내부에서 Docker 를 전부 띄우지 말고 외부(호스트 환경) 의 Docker 와 소켓을 공유하여 사용하는 것이 그나만 나은 방법이라고 한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이를 기존의 방식인 Docker-in-docker 와 대비하여 Docker-outside-of-docker 라고 부르는 것 같다. 완전히 정착된 이름은 아닌 것 같지만 이 글에서는 이 이름을 사용하겠다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 Jenkins 이미지: &lt;a href=&quot;https://hub.docker.com/r/jenkins/jenkins&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/r/jenkins/jenkins&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 Docker 이미지: &lt;a href=&quot;https://hub.docker.com/_/docker&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://hub.docker.com/_/docker&lt;/a&gt; (여기도 Docker-in-docker 를 추천하지 않는다고 쓰여있다.)&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;환경&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;호스트 서버
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;CentOS 7.9&lt;/li&gt;
&lt;li&gt;Docker server: 20.10.8&lt;/li&gt;
&lt;li&gt;사용자는 docker 그룹에 포함되어 있다. (sudo 없이 docker 명령어 입력 가능)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Jenkins 컨테이너
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;기본 이미지: 2.289.3-jdk11
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;내부에서 Docker 를 사용해야 하기 때문에 이를 베이스로 새로 이미지를 만들 것이다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Docker client: 20.10.8&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;figure class=&quot;imageblock alignCenter&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;365&quot; data-filename=&quot;1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot;&gt;&lt;span data-url=&quot;https://blog.kakaocdn.net/dn/voGEb/btreiPSCr7V/aWJNcoNxKbqYtuGOkw2ihk/img.png&quot; data-phocus=&quot;https://blog.kakaocdn.net/dn/voGEb/btreiPSCr7V/aWJNcoNxKbqYtuGOkw2ihk/img.png&quot;&gt;&lt;img src=&quot;https://blog.kakaocdn.net/dn/voGEb/btreiPSCr7V/aWJNcoNxKbqYtuGOkw2ihk/img.png&quot; srcset=&quot;https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&amp;fname=https%3A%2F%2Fblog.kakaocdn.net%2Fdn%2FvoGEb%2FbtreiPSCr7V%2FaWJNcoNxKbqYtuGOkw2ihk%2Fimg.png&quot; data-origin-width=&quot;767&quot; data-origin-height=&quot;365&quot; data-filename=&quot;1.png&quot; data-ke-mobilestyle=&quot;widthOrigin&quot; onerror=&quot;this.onerror=null; this.src='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png'; this.srcset='//t1.daumcdn.net/tistory_admin/static/images/no-image-v1.png';&quot;/&gt;&lt;/span&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;hr contenteditable=&quot;false&quot; data-ke-type=&quot;horizontalRule&quot; data-ke-style=&quot;style6&quot; /&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;1. Jenkins 이미지 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너 내부에서 외부(호스트) 도커와 통신해야 하기 때문에 추가로 설치할 것이 있다. 기본 이미지는 현시점 (2021-09-06) 에서 가장 최신의 lts 버전이고, jdk11 이 들어 있는 버전인 2.289.3-jdk11 을 택한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;젠킨스 이미지: &lt;a href=&quot;https://hub.docker.com/r/jenkins/jenkins/tags?page=1&amp;amp;ordering=last_updated&amp;amp;name=2.289.3-jdk11&quot;&gt;https://hub.docker.com/r/jenkins/jenkins/tags?page=1&amp;amp;ordering=last_updated&amp;amp;name=2.289.3-jdk11&lt;/a&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Docker client 가 필요하기 때문에 Dockerfile 를 통해 설치하고 이미지를 만든다.&amp;nbsp;베이스 이미지가 Debian 기반이기 때문에 Debian 환경에 Docker 설치를 위한 가이드를 참고한다.&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;공식 가이드: &lt;a href=&quot;https://docs.docker.com/engine/install/debian/&quot; target=&quot;_blank&quot; rel=&quot;noopener&quot;&gt;https://docs.docker.com/engine/install/debian/&lt;/a&gt;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;Dockerfile 은 아래와 같다.&lt;/p&gt;
&lt;pre id=&quot;code_1630927270938&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;FROM jenkins/jenkins:2.289.3-jdk11

USER root

RUN apt-get update \
 &amp;amp;&amp;amp; apt-get -y install lsb-release \
 &amp;amp;&amp;amp; curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg \
 &amp;amp;&amp;amp; echo &quot;deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/debian $(lsb_release -cs) stable&quot; | tee /etc/apt/sources.list.d/docker.list &amp;gt; /dev/null \
 &amp;amp;&amp;amp; apt-get update \
 &amp;amp;&amp;amp; apt-get -y install docker-ce docker-ce-cli containerd.io
RUN usermod -u {{호스트의사용자아이디}} jenkins &amp;amp;&amp;amp; \
    groupmod -g {{호스트의도커그룹아이디}} docker &amp;amp;&amp;amp; \
    usermod -aG docker jenkins

USER jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;&amp;nbsp;&lt;/p&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사이트에서 요구하는 패키지들이 있는데, 대부분 이미 설치되어 있고, lsb-release 만 추가로 설치하면 된다.&lt;/li&gt;
&lt;li&gt;외부(호스트) 도커 서버를 사용할거라서 docker-ce-cli 만 설치해도 되기는 하지만, docker-ce 를 설치해야 docker 그룹이 생긴다. 괜히 건드렸다가 꼬일 것 같아서 그냥 가이드대로 다 설치했다.&lt;/li&gt;
&lt;li&gt;컨테이너 내부 사용자 (jenkins) 가 호스트의 파일에 접근이 가능 가능하도록 호스트 사용자 아이디와 동일하도록 맞춰준다. (위 Dockerfile 에서 {{호스트의사용자아이디}})
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;사용자 아이디는 호스트의 /etc/passwd 에서 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;컨테이너의 도커 그룹 아이디를 호스트의 도커 그룹 아이디와 동일하게 맞춘다. (위 Dockerfile 에서 {{호스트의도커그룹아이디}})
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;도커 그룹 아이디는 호스트의 /etc/group 에서 확인한다.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;이미지를 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1630927746251&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;docker build -t my-jenkins:0.1 .&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인&lt;/p&gt;
&lt;pre id=&quot;code_1630928218614&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;[user@host ~]$ docker image ls
REPOSITORY                 TAG             IMAGE ID       CREATED         SIZE
my-jenkins                 0.1             df9c3ec43a7c   4 minutes ago   1.15GB
jenkins/jenkins            2.289.3-jdk11   ea470c80c15d   5 weeks ago     680MB&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;2. volume 만들기&lt;/h3&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jenkins 설정 및 히스토리를 저장하고 재시작시에도 유지되도록 volume 을 만든다.&lt;/p&gt;
&lt;pre id=&quot;code_1630928155846&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;docker volume create jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;확인&lt;/p&gt;
&lt;pre id=&quot;code_1630928207918&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;[user@host ~]$ docker volume ls
DRIVER    VOLUME NAME
local     jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;h3 data-ke-size=&quot;size23&quot;&gt;3. Jenkins 컨테이너 실행&lt;/h3&gt;
&lt;pre id=&quot;code_1630928571446&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;docker run -d --name jenkins \
    -v /var/run/docker.sock:/var/run/docker.sock \
    -v jenkins:/var/jenkins_home \
    -p 8080:8080 my-jenkins:0.1&lt;/code&gt;&lt;/pre&gt;
&lt;ul style=&quot;list-style-type: disc;&quot; data-ke-list-type=&quot;disc&quot;&gt;
&lt;li&gt;핵심은 docker.sock 을 마운트 하는 것이다. 이를 통해 컨테니어 내부에서 외부(호스트) 의 docker 서버에 접근할 수 있다.&lt;/li&gt;
&lt;li&gt;volume 을 마운트하고, 접근 포트를 열어준다.&lt;/li&gt;
&lt;/ul&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;컨테이너가 잘 떴는지 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1630928933611&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;[user@host ~]$ docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS        PORTS                               NAMES
d57073d5fec2   my-jenkins:0.1   &quot;/sbin/tini -- /usr/&amp;hellip;&quot;   3 seconds ago   Up 1 second   0.0.0.0:8080-&amp;gt;8080/tcp, 50000/tcp   jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;jenkins 컨테이너 내부에서도 호스트의 docker 에 접근이 가능한지 내부에서 호출하여 확인해 보자&lt;/p&gt;
&lt;pre id=&quot;code_1630929170195&quot; class=&quot;shell&quot; data-ke-language=&quot;shell&quot; data-ke-type=&quot;codeblock&quot;&gt;&lt;code style=&quot;white-space: pre;&quot;&gt;[user@host ~]$ docker exec jenkins docker ps
CONTAINER ID   IMAGE            COMMAND                  CREATED         STATUS         PORTS                               NAMES
d57073d5fec2   my-jenkins:0.1   &quot;/sbin/tini -- /usr/&amp;hellip;&quot;   2 minutes ago   Up 2 minutes   0.0.0.0:8080-&amp;gt;8080/tcp, 50000/tcp   jenkins&lt;/code&gt;&lt;/pre&gt;
&lt;p data-ke-size=&quot;size16&quot;&gt;호스트의 도커 서버를 공유하고 있기 때문에 같은 결과가 나온다.&lt;/p&gt;</description>
      <category>docker</category>
      <category>docker</category>
      <category>docker-in-docker</category>
      <category>docker-outside-of-docker</category>
      <category>jenkins</category>
      <category>도커</category>
      <category>이미지</category>
      <category>젠킨스</category>
      <category>컨테이너</category>
      <author>빛가닥</author>
      <guid isPermaLink="true">https://bitgadak.tistory.com/3</guid>
      <comments>https://bitgadak.tistory.com/3#entry3comment</comments>
      <pubDate>Mon, 6 Sep 2021 20:55:44 +0900</pubDate>
    </item>
  </channel>
</rss>