[남용 금지] AWS Lambda에서 Tor 사용

AWS의 람바다를 통해 복제/제거를 하려면 Headless Chrome(chromium)을 사용하면 됩니다.
※ 설치는 타입 스크립트에 따라 진행됩니다.

계층형 Tor


'브라우저'이 있어 오해받기 쉽지만, 타워는 타워 브라우저에만 국한된 것이 아니라 평소 사용하던 크롬과 같은 일반적인 브라우저도'타워화'할 수 있는 자체 실행tor 명령이다.tor 명령은 여기.의 Tor 프로젝트에서 버전 관리를 하고 다운로드와 컴파일을 통해 누구나 쉽게 사용할 수 있다.또한 자신이 컴파일하지 않아도 예를 들어 MacOS는 Homebrewbrew install tor를 통해 간단하게 설치할 수 있다.
Lambda에서 사용하면 Linux 환경에서 컴파일된 파일을 사용하지 않으면 오류가 발생할 수 있으므로 Docker 컨테이너에서 다운로드하고 컴파일하는 것이 좋습니다.이번에는 그 절차를 생략하지만, zip화된 물건을 미리 컴파일해서 여기. 창고에 준비했기 때문에, zip 파일을 람바다 도면층으로 업로드하기만 하면 간단하게 참조할 수 있습니다.실행 파일의 경로는 /opt/bin/tor입니다.
또 창고 안serverless.yml에도 같이 쓰여 있기 때문에 실행sls deploy만 하면 업로드를 완료할 수 있다.tor 파일 자체는 15MB 미만이며 Lambda로 처리하기에 충분한 크기입니다.

Headless Chrome


※ 최종 결과는 GiitHub 웨어하우스 올라왔습니다.우리도 서버리스를 지원합니다.
Tor의 도면화가 끝났기 때문에 다음에는 Headless Chrome에서 호출하기만 하면 됩니다.예전에는 헤드리스화를 위해 애를 썼던 기억이 나는데, 지금은 간단하게 설치해서 사용할 수 있게 되었다.
$ yarn add chrome-aws-lambda
에 모듈을 설치한 후 아래의 chromium만 가져오면 준비가 완료됩니다.
import chromium from 'chrome-aws-lambda'
다음
await chromium.puppeteer.launch({ options })
Headless Chrome을 시작할 수 있습니다.Lambda에서 Tor 사용하기
  • Headless Chrome이 시작되기 전에 실행tor
  • 상기options에서 Tor를 사용하는 의미
  • 를 설명합니다.
    이런 흐름.

    Tor의 모듈식(클래스)


    간단하게 가동tor할 수 있도록 분류부터 할게요.tor 설정 파일로 본 .torrc 파일로 다음 2개를 써야 한다.
  • SOCKSPort: SOCKS 프록시 통신 포트
  • DataDirectory: 데이터 송수신 디렉터리 경로
  • Lambda는 임시 디스크 용량으로 tmp 파일 디렉터리를 만들 수 있으며 .torrcDataDirectory에 대응하는 디렉터리를 만들 수 있습니다.
    그 결과 주로 이루어져야 할 부분은 다음과 같은 세 가지가 있다.
  • 임시 파일 디렉터리 만들기
  • tor의 실행
  • 종료 시 정리
  • 이상의 내용을 바탕으로 Tor의 등급화된 실복을 살펴봅시다.평론을 가지고 전체를 써 보았다.
    /* tor.ts */
    import { spawn, execSync, ChildProcessWithoutNullStreams } from 'child_process'
    import tempfile from 'tempfile'
    import { IS_LOCAL } from './env' // ローカル環境かどうかの判別。ファイルの中身は後述
    
    export default class Tor {
      port: number       // SOCKSPort に対応
      dataDir: string    // DataDirectory に対応
      torrcPath: string  // 一時ファイルとしての .torrc のパス
      torPath: string = `/opt/bin/tor`
      proc: ChildProcessWithoutNullStreams
    
      constructor(port: number) {
        this.port = port
      }
    
      async launch() {
        if (IS_LOCAL) { // ローカルでは tor は起動済という前提
          return
        }
    
        this.createTempfiles() // 一時ファイル・ディレクトリを作成
    
        // tor の起動には時間がかかるため Promise を返しておく
        return new Promise((resolve, reject) => {
          // tor の起動
          this.proc = spawn(this.torPath, ['-f', this.torrcPath], {
            cwd: this.dataDir,
          })
          
          // 起動完了まで待機
          this.proc.stdout.on('data', (data: Buffer) => {
            if (data.toString().match(/100%/)) { // 起動完了
              resolve()
            }
          })
    
          this.proc.stderr.on('data', (data) => {
            reject(`Failed tor initialization: ${data}`)
          })
    
          this.proc.on('close', (code) => {
            if (code) {
              reject()
            } else {
              resolve()
            }
          })
        })
      }
    
      createTempfiles() {
        // 一時ファイル・ディレクトリのパス生成
        this.dataDir = tempfile('.data')
        this.torrcPath = tempfile('.torrc')
    
        // .data および .torrc の作成
        const cmds = [
          `mkdir ${this.dataDir}`,
          `touch ${this.torrcPath}`,
          `echo 'SOCKSPort ${this.port}\n' > ${this.torrcPath} && echo 'DataDirectory ${this.dataDir}\n' >> ${this.torrcPath}`,
        ]
    
        for (let cmd of cmds) {
          execSync(cmd)
        }
      }
    
      close() {
        if (IS_LOCAL) {
          return
        }
    
        // tor の終了
        this.proc.kill('SIGINT')
    
        const cmds = [`rm -rf ${this.dataDir}`, `rm -rf ${this.torrcPath}`]
        for (let cmd of cmds) {
          execSync(cmd)
        }
      }
    }
    
    상기 코드 내의 env 파일은 다음과 같다.
    /* env.ts */
    export const IS_LOCAL = process.env.IS_LOCAL === 'true' ? true : false
    
    로컬에서 Lambda를 시도할 수 있는 경우 serverless-offline
    $ sls invoke local -f <function_name>
    
    로 실행할 수 있는 함수로서 이 설정IS_LOCAL=true 환경 변수입니다.boolean으로 잘 평가할 수 있도록 기술한 것이다.

    Headless Chromew/Tor의 모듈식(분류)


    그럼 여기까지는 헤드리스 크롬과 토어를 활용할 준비가 됐으니 둘을 조합해 사용할 수 있도록 실장해 주세요.
    위에서 설명한 대로 Tor는 chromium.puppeteer.launch({ options })의 매개변수에서만 사용됩니다.
    /* browser.ts */
    import 'source-map-support/register'
    
    import chromium from 'chrome-aws-lambda'
    import { Browser } from 'puppeteer-core'
    import { IS_LOCAL } from './env'
    import os from './os' // ローカルで Win/Mac の判別用。ファイルの中身は後述
    import Tor from './tor'
    
    // Tor のポート
    const PORT = 9050
    
    // Chromium のデフォルトのユーザーエージェントは `webdriver`
    export const UA =
      'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_5)' +
      'AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.125 Safari/537.36'
    
    // ローカルの場合は、インストールしてある Chrome を起動
    let path: string = ''
    switch (os) {
      case 'win':
        path = 'C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe'
        break
      case 'mac':
        path = '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome'
        break
    }
    
    export interface LaunchOptions {
      headless?: boolean
      useTor?: boolean
    }
    
    export class Chrome {
      browser?: Browser // Headless Chrome 本体
      tor: Tor | undefined
    
      async launch({ headless, useTor }: LaunchOptions) {
        headless = headless === false ? false : true
        let { args } = chromium
        if (useTor) {
          // 事前に tor を実行
          this.tor = new Tor(PORT)
          await this.tor.launch()
          
          // Tor を利用するために、引数にSOCKSプロキシの設定を追加
          args.push(`--proxy-server=socks5://127.0.0.1:${PORT}`)
        }
    
        this.browser = await chromium.puppeteer.launch({
          args,
          defaultViewport: chromium.defaultViewport,
          executablePath: IS_LOCAL ? path : await chromium.executablePath,
          headless,
          ignoreHTTPSErrors: true,
        })
      }
    
      async close() {
        if (this.tor) {
          this.tor.close()
        }
        if (this.browser != null) {
          await this.browser.close()
        }
      }
    }
    
    코드 내의 OS 판별은 아래 설치를 통해 진행된다.
    /* os.ts */
    export const platform = process.platform
    
    export let os: 'win' | 'mac' | 'linux' | 'unknown' = 'unknown'
    
    switch (platform) {
      case 'win32':
        os = 'win'
        break
      case 'darwin':
        os = 'mac'
        break
      case 'linux':
        os = 'linux'
        break
    }
    
    export default os
    
    다음
    const chrome = new Chrome({ headless: true, useTor: true })
    
    면 Tor 브라우저화가 완료됩니다.

    Let's Try!


    실제로 해보세요!
    /* main.ts */
    import 'source-map-support/register'
    
    import { Handler } from 'aws-lambda'
    import { Chrome } from './browser'
    
    export const handler: Handler = async (_event, _context, callback) => {
      const chrome = new Chrome()
      const url = 'https://ipinfo.io/'
    
      let content: string[]
      try {
        await chrome.launch({ headless: true, useTor: true })
        const page = await chrome.browser.newPage()
        await page.goto(url, { waitUntil: 'networkidle2' })
        await page.waitForSelector('.json-widget-entry')
        content = await page.$$eval('.json-widget-entry', (elems: any) => {
          return elems.map((el: any) =>
            (el.textContent as string).replace(/\n|\s/g, '')
          )
        })
      } catch (e) {
        console.error(e, e.stack)
        return callback(e, null)
      } finally {
        await chrome.close()
      }
    
      return callback(null, content)
    }
    
    위에서 디버깅 및 실행
    결과
    [
      ...
      "city:\"Wiesbaden\"",
      "region:\"Hesse\"",
      "country:\"DE\"",
      ...
      "tor:true",
    ]
    
    로 해외 방문이 확인됐다.tor:true라는 표시가 있어'Tor화'가 가능하다는 것도 안다.
    지금까지 설치였습니다!람다에서 Tor화하는 것은 사실 매우 간단하다는 것을 여러분이 알고 싶습니다.이렇게 되면 자신의 신분을 숨길 수 있지만 절대 남용하지 마세요.
    (또한, 위에서 말한 바와 같이tor:true Tor인지 아닌지를 판단할 수 있기 때문에 서비스에 따라 탄주 방문도 있습니다.)
    창고. 내에 Chrome류에서도 preventBotDetection의 코드를 기술했다.크롬을 통한 방문을 숨기기 위해 실시된 것으로, 이 내용에 대해서는 다른 기회에 소개하고 싶다.

    좋은 웹페이지 즐겨찾기