在 10 分鐘內實現安全的 React + Docker

做者:Matt Raible

翻譯:瘋狂的技術宅html

原文:https://scotch.io/tutorials/r...前端

未經容許嚴禁轉載node

假如你已經構建了一個 React 應用,可是如今須要部署它。應該怎麼作?首先,最好選擇一個雲提供商,由於它們通常成本低並且部署容易。react

大多數雲提供商都提供了一種部署靜態站點的方法。用 React 構建應用只是 JavaScript、HTML 和 CSS。它們是靜態文件,幾乎能夠在任何 Web 服務器上使用。但實際上,若是你使用了 JSX(JS 中的 HTML)和樣式化組件,那麼這些能夠說只有 JavaScriptnginx

Docker 是用於構建和共享容器化應用的事實標準。你可使用它打包你的應用程序,幷包含多種開源 Web 服務器來爲你的應用程序提供服務。另外,你還能夠經過配置網絡服務器來發送安全標頭,這樣使你的程序更安全。git

前提條件:程序員

建立 React 應用

爲了集中精力,我用了一位同事已經構建的程序。首先克隆存儲庫。github

git clone https://github.com/oktadeveloper/okta-react-styled-components-example.git react-docker
cd react-docker
npm install

這是一個使用樣式化組件的 React 應用,並由 OpenID Connect(aka OIDC)保護。你能夠在使用樣式化組件構建 React 應用 一文中瞭解其建立方式。web

登陸你的 Okta 開發者賬戶(你已經建立了一個,對嗎?)註冊此應用並啓用 OIDC 身份驗證。面試

  1. 轉到頂部菜單中的 Applications
  2. 選擇 Add Application > Single-Page App ,而後單擊 Next
  3. 在設置屏幕上,爲你的應用命名,例如 React Docker
  4. 確保端口設置爲 3000,而且 Login redirect URIhttp://localhost:3000/callback
  5. 點擊 Done

出現的界面將爲你提供一個客戶端 ID。

image.png

將客戶端 ID 複製並粘貼到應用程序的 src/App.js 中。 <yourIssuerURI> 的值能夠在 Okta 儀表板的 API > Authorization Servers 下找到。例如個人是 https://dev-133320.okta.com/oauth2/default

function App() {
  return (
    <Router>
      <Security issuer='<yourIssuerURI>'
                clientId='<yourClientId>'
                redirectUri={window.location.origin + '/callback'}
                pkce={true}>
        <SecureRoute path='/' exact={true} component={Calendar}/>
        <Route path='/callback' component={LoginCallback}/>
      </Security>
    </Router>
  );
}

<> 括號只是佔位符,請確保將其刪除!

npm start 啓動你的應用。你將被重定向到 Okta 進行身份驗證,而後返你的應用。若是你沒有重定向,那是由於你已經登陸。請在 private 窗口中重試來查看登陸過程。

你會看到一個簡單、乾淨的日曆,並選擇了今天的日期。

image.png

我認可這是一個很是簡單的應用,但咱們會用它來演示如何用 Docker 進行容器化。

爲何要使用Docker?

你可能會問:「爲何要用 Docker?這不會使事情複雜化嗎?」

是的我贊成。用 Docker 進行操做比用 Heroku 進行 firebase deploygit push 處理更爲複雜。可是若是你真的要使事情複雜化,並用 Kubernetes 去管理你的應用,那麼它能夠給你更多的控制權。 😛

建立Dockerfile和Nginx配置

在你的根目錄中建立一個 Dockerfile

FROM node:14.1-alpine AS builder

WORKDIR /opt/web
COPY package.json package-lock.json ./
RUN npm install

ENV PATH="./node_modules/.bin:$PATH"

COPY . ./
RUN npm run build

FROM nginx:1.17-alpine
RUN apk --no-cache add curl
RUN curl -L https://github.com/a8m/envsubst/releases/download/v1.1.0/envsubst-`uname -s`-`uname -m` -o envsubst && \
    chmod +x envsubst && \
    mv envsubst /usr/local/bin
COPY ./nginx.config /etc/nginx/nginx.template
CMD ["/bin/sh", "-c", "envsubst < /etc/nginx/nginx.template > /etc/nginx/conf.d/default.conf && nginx -g 'daemon off;'"]
COPY --from=builder /opt/web/build /usr/share/nginx/html

這將會構建你的項目並把 Nginx 添加爲 Web服務器。它還將安裝 envsubst 版本,該版本容許你用環境變量去替換變量,並設置默認值。

在同一目錄中建立一個 nginx.config

server {
    listen       ${PORT:-80};
    server_name  _;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $$uri /index.html;
    }
}

這個文件把 Nginx 配置爲將你的 React 應用做爲 SPA(其中全部路由都轉到 index.html)並在 80 端口上運行。在 uri 前面有兩個 $$,以防止 $uri 被替換爲空白值。

用 React 應用構建 Docker 鏡像

先執行 docker ps 確保你的 Docker 守護進程正在運行。而後運行如下命令來構建你的 Docker 鏡像。 命令中的 react-docker 能夠是你想要爲鏡像命名的任何名字。

docker build -t react-docker .

該過程完成後,你將會看到如下消息的內容:

Successfully built 3211a1255527
Successfully tagged react-docker:latest

運行你的 Docker + React 應用

如今,你能夠用 docker run 命令經過 Docker 在端口 3000 上運行 React 應用。

docker run -p 3000:80 react-docker

若是你發現這些 docker 命令很難記住,也能夠在 package.json文件中添加幾個腳本 。

"docker": "docker build -t react-docker .",
"react-docker": "docker run -p 3000:80 react-docker"

而後就能夠用 npm run dockernpm run react-docker 運行了。

很漂亮吧?在短短几分鐘內就把你的 React 應用作了 docker 化。 🎉

把將你的 React App 部署到 Heroku

你的應用要直到正式投入生產時纔會真正的存在,因此讓咱們把它部署到 Heroku。首先我將向你展現怎樣不用 Docker 作到這一點。

首先,你須要 一個 Heroku 賬戶。而後,安裝 Heroku CLI

打開終端,登陸你的 Heroku 賬戶,而後建立一個新應用。

heroku login
heroku create

如今,你應該有了一個新的 heroku Git 遠程存儲庫。能夠用 git remote -v 來確認。

在帶有安全標頭的根目錄中建立一個 static.json 文件,並把全部 HTTP 請求重定向到 HTTPS。

{
  "headers": {
    "/**": {
      "Content-Security-Policy": "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' https://*.okta.com;",
      "Referrer-Policy": "no-referrer, strict-origin-when-cross-origin",
      "Strict-Transport-Security": "max-age=63072000; includeSubDomains",
      "X-Content-Type-Options": "nosniff",
      "X-Frame-Options": "DENY",
      "X-XSS-Protection": "1; mode=block",
      "Feature-Policy": "accelerometer 'none'; camera 'none'; microphone 'none'"
    }
  },
  "https_only": true,
  "root": "build/",
  "routes": {
    "/**": "index.html"
  }
}

要讀取 「static.json」,你必須用 Heroku static buildpack

把你的更改提交到 Git,添加 Node.js + static buildpack,而後部署 React 應用。

git commit -am "Configure secure headers and static buildpacks"
heroku buildpacks:set heroku/nodejs
heroku buildpacks:add https://github.com/heroku/heroku-buildpack-static.git
git push heroku master

該過程完成後,使用如下方法在瀏覽器中打開你的應用程序:

heroku open

你將會被重定向到 Okta,可能會看到如下錯誤:

The 'redirect_uri' parameter must be an absolute URI that is whitelisted in the client app settings.

要解決這個問題,須要修改 Okta 應用,以將你的 Heroku URL 添加爲「登陸重定向 URI」。例如https://gentle-peak-37809.herokuapp.com/callback

如今,你應該能夠登陸並看到你的應用在 Heroku 上運行了!你能夠在 https://securityheaders.com 上驗證其安全標頭是否正確。

image.png

在這個部署示例中,buildpacks 爲你完成了全部工做。可是並不是每一個雲提供商都提供 buildpack。這就是須要 Docker 的地方。

把 Docker + React App 部署到 Heroku

當涉及到 Docker 鏡像時,Heroku 具備一些出色的功能。若是你的項目有一個 Dockerfile,則能夠用 Heroku Container Registry直接部署你的應用。

首先,登陸到Container Registry。

heroku container:login

而後,建立一個新的應用。

heroku create

把 Git URL 做爲新的 remote 添加到你的應用。

git remote add docker https://git.heroku.com/<your-app-name>.git

而後,把將你的 Docker 鏡像 push 到 Heroku 的 Container Registry。

heroku container:push web --remote docker

該過程完成後,release 你的應用程序鏡像:

heroku container:release web --remote docker

而後,在瀏覽器中打開該應用:

heroku open --remote docker

你須要先在 Okta 中添加應用的 URI,而後才能登陸。

改善 Docker 中 Nginx 的安全標頭

若是在 securityheaders.com 上的 Docker 站點中測試新的 Nginx,你的得分應該是 F

爲了解決這個問題,修改你的 nginx.config 添加安全頭。

server {
    listen       ${PORT:-80};
    server_name  _;

    root /usr/share/nginx/html;
    index index.html;

    location / {
        try_files $$uri /index.html;
    }

    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:; connect-src 'self' https://*.okta.com;";
    add_header Referrer-Policy "no-referrer, strict-origin-when-cross-origin";
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains";
    add_header X-Content-Type-Options nosniff;
    add_header X-Frame-Options DENY;
    add_header X-XSS-Protection "1; mode=block";
    add_header Feature-Policy "accelerometer 'none'; camera 'none'; microphone 'none'";
}

更新文件後,運行如下命令:

heroku container:push web --remote docker
heroku container:release web --remote docker

如今你應該獲得 A

image.png

用 Cloud Native Buildpacks 建立你的 React + Docker 鏡像

在本文中,咱們學習了把 React 應用部署到 Heroku 的兩種方法。首先是利用 buildpack 和 git push。第二個是使用 Heroku 的 Container Registry 和 heroku container:push + heroku push:release

Cloud Native Buildpacks 是 Pivotal 和 Heroku 在 2018 年初發起的一項舉措。它具備 pack CLI,可以讓你用 buildpacks 構建 Docker 映像。

個人好朋友 Joe Kutner是 Heroku 的一名軟件架構師,在實現 Cloud Native Buildpacks 中發揮了重要的做用。 Joe 是 JHipster 項目的積極提交者,其做者 The Healthy Programmer 是 Cloud Native Buildpacks 核心團隊的創始成員 。他對 Docker 的建議是:「若是不須要,請不要使用 Dockerfile」。

Joe 對我在弄清楚如何使用 buildpacks 建立 Docker 映像的技術上提供了很大的幫助,因此下面的說明應該歸功於他。

首先,請 installpack。若是你使用的是 Mac 或 Linux,可使用 Homebrew。若是用的是 Windows,能夠安裝其可執行文件

brew tap buildpack/tap
brew install pack

在前面的 buildpacks 示例中,我用了 Heroku 的 Node.js 和靜態 buildpacks。

Heroku 靜態構建包不是 「Cloud Native」 構建包。它使用舊的(原生雲)API。這意味着它與開箱即用的 pack 不兼容。

幸運的是,Heroku 確實提供了 cnb-shim,你能夠用它來使其工做。在用 cnb-shim 轉換後,Joe 爲 Heroku 的靜態 buildpack 建立了一個 URL (https://cnb-shim.herokuapp.com/v1/heroku-community/static) 。

在本地構建和運行 Docker 鏡像以前,必須先進行一項更改。 從 static.json 中刪除 "https_only":true 這一行。

而後用如下命令經過 Node.js 和靜態 buildpack(也就是你在 Heroku 上使用的相同 buildpack)構建 Docker 鏡像。

pack build react-pack --builder heroku/buildpacks --buildpack \
  heroku/nodejs,https://cnb-shim.herokuapp.com/v1/heroku-community/static

提示:若是你想擺脫 --builder 參數,能夠用 pack set-default-builder heroku/buildpacks

該過程完成後,你應該能夠運行它。

docker run --rm -it --init -p 3000:3000 --env PORT=3000 okta

若是你發現這些 pack 命令很難被記住,那麼能夠把它們添加到 package.json 中。

"pack": "pack build react-pack --builder heroku/buildpacks --buildpack heroku/nodejs,https://cnb-shim.herokuapp.com/v1/heroku-community/static",
"react-pack": "docker run --rm -it --init -p 3000:3000 --env PORT=3000 react-pack"

而後可使用 npm run packnpm run react-pack 來運行它們。

把將你的 React + Docker 鏡像部署到 Docker Hub

經過把它們部署到 Docker Hub 等註冊表中,能夠輕鬆共享 Docker 容器。若是你尚未 Docker Hub 賬戶,那就先建立一個

擁有賬戶以後,登陸並 push 你的鏡像。在下面的示例中,我正在使用 react-docker,但你也可使用 react-pack 來部署 buildpacks 版本。

docker login
docker image tag react-docker <your-username>/react-docker
docker push <your-username>/react-docker

默認狀況下,這會將其標記爲 latest。若是要標記和推送特定版本,能夠用:

docker image tag react-docker <your-username>/react-docker:1.0
docker push <your-username>/react-docker

而後其餘人就能夠用如下命令 pull 並運行:

docker run -p 3000:80 <your-username>/react-docker

把 React + Docker 鏡像部署到 Heroku

要把現有映像部署到 Heroku,能夠用 docker push。你必須用如下命名約定來標記和推送鏡像。

docker tag <image> registry.heroku.com/<app>/<process-type>
docker push registry.heroku.com/<app>/<process-type>

要部署 react-pack 鏡像,你能夠執行如下操做:

docker tag react-pack registry.heroku.com/fierce-eyrie-08414/web
docker push registry.heroku.com/fierce-eyrie-08414/web
heroku container:release web --remote docker

我嘗試了一下,發現沒有強制使用 HTTPS。必須將 "https_only":true 添加到 static.json 中,而後從新push。

瞭解有關 React 和 Docker 的更多信息

在本教程中,咱們學習瞭如何用 Docker 容器化你的 React 應用。你能夠用 docker build 手動進行這項操做,也能夠用 Heroku 的 Container Registry 經過 Dockerfile 推送和發佈項目。在構建容器時,還能夠用 pack 命令來利用 Cloud-Native + Heroku 構建包。

若是你用的是 Heroku,它的 buildpack 比 Docker 更容易使用。經過簡單的 git push,你能夠在 Heroku 的服務器上部署代碼並構建。

能夠在 GitHub上 的 oktadeveloper/okta-react-docker-example 上找到本示例的源代碼。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索