Published on

自家製ngrokの作り方

Authors

Ngrokと同じことをするのに何が必要かというとこれだけでした。

  • リモートポートフォワードでSSHする
  • Nginxでフォワードされたポートをインターネットに公開するよう設定する

EC2の起動テンプレートをterraformで作り、起動テンプレから立ち上がったサーバーはあらかじめ仕込まれたユーザーデータのスクリプトで自動でNginxや必要なセットアップを行うようにしました。

Terraform

resource "aws_key_pair" "umihico" {
  # 2人目以降はuser_data.shから直接~/.ssh/authorized_keysに足す
  key_name   = "umihico"
  public_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFUMIHICoCb3Sy2n1qPXOxc2mFBqW9Hg0dRigxl2F3nW"
}


resource "aws_security_group" "ssh" {
  name   = "self-hosted-ngrok-sg"
  vpc_id = var.vpc_id

  ingress {
    description      = "allow only SSH"
    from_port        = 1234
    to_port          = 1234
    protocol         = "tcp"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }

  egress {
    from_port        = 0
    to_port          = 0
    protocol         = "-1"
    cidr_blocks      = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}

resource "aws_launch_template" "this" {
  name          = "self-hosted-ngrok"
  instance_type = "t2.micro"
  key_name      = aws_key_pair.umihico.id
  image_id      = "ami-0bcc04d20228d0cf6"
  user_data     = filebase64("${path.module}/user_data.sh")
  network_interfaces {
    subnet_id       = var.public_subnets[0]
    security_groups = [aws_security_group.ssh.id]
  }
  tag_specifications {
    resource_type = "instance"
    tags = {
      Name = "self-hosted-ngrok"
    }
  }
}

user_data.sh

#!/bin/bash

# サーバー費削減のために毎朝3時に自動でstopするようにcronを追加
sudo timedatectl set-timezone Asia/Tokyo
sudo sh -c "echo '0 3 * * * root /usr/sbin/shutdown -h now' > /etc/cron.d/auto-shutdown"
sudo systemctl crond restart

# SSHポートをパブリックに公開するならデフォの22から変更して攻撃量を減らしておく
sudo sed -i -e "s/#Port 22/Port 1234/" /etc/ssh/sshd_config 
sudo systemctl restart sshd

sudo yum update -y

sudo amazon-linux-extras install nginx1 -y
sudo tee /etc/nginx/conf.d/server.conf << END
upstream tunnel {
  server 127.0.0.1:8080;
}
server {
  location = /nginx-healthcheck {
    add_header Content-Type text/html;
    return 200 '<html><body><h1>OK</h1></body></html>';
  }
  location / {
    proxy_set_header X-Real-IP \$remote_addr;
    proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
    proxy_set_header Host \$http_host;
    proxy_redirect off;
    
    proxy_pass http://tunnel;
  }
}
END
sudo systemctl start nginx.service

Nginxサーバーが立ち上がったどうかだけ/nginx-healthcheckから確認できます。 無事にNginxが動いていれば、次に以下のSSHコマンドです。ローカルでポート3000で見えるものがEC2のパブリックDNSにも見えたら成功です。

ssh -N -p 1234 -R 8080:localhost:3000 -oStrictHostKeyChecking=no ec2-user@$DnsName

これのほか起動時に前回使ったstoppedなサーバーをterminateしたり、https使ったり証明書を発行したりする必要がありますが、この辺のルーティングまわりやスクリプトは各自の環境次第なので省略