シナリオ
例えばオンプレミスネットワークとAWS側のVPCが接続してリーチャビリティはあるとします。
そしてVPC内にあるインスタンスA(client)から同じサブネットのインスタンスB(web)に通信しようとしています。
しかし、インスタンスAはインスタンスBにネットワーク的に問題はなくても名前解決ができないので通信できないとします。
そしてインスタンスBの名前はオンプレミス側のDNSが知っているとします。
図示するとこうです。
d2 ソース
network: {
onpre: オンプレミス環境 {
dns: DNS server
}
vpc: AWS VPC {
client: client
web: web
client -> web: ここにアクセスしたいけど名前解決できない {
style: {
stroke-dash: 3
}
}
}
onpre.dns -> vpc.web: オンプレミスのDNSは名前を知っている
}
今回は検証用にすべて同じサブネットにインスタンスを立てますが、実際のシナリオでも流れは同じです。
実際にやってみる
環境の再現
検証用にterraformコードを用意しました。
まずはこれでterraform applyします。
variable "aws_access_key" {}
variable "aws_secret_key" {}
variable "region" {
default = "ap-northeast-1"
}
provider "aws" {
access_key = "${var.aws_access_key}"
secret_key = "${var.aws_secret_key}"
region = "${var.region}"
}
variable "images" {
default = {
us-east-1 = "ami-02396cdd13e9a1257"
us-east-2 = "ami-0578f2b35d0328762"
us-west-1 = "ami-0127b2e6f3b9b94d5"
us-west-2 = "ami-009c5f630e96948cb"
ap-south-1 = "ami-0c768662cc797cd75"
ap-northeast-3 = "ami-08334eeab9e50ade9"
ap-northeast-2 = "ami-0a306845f8cfbd41a"
ap-southeast-1 = "ami-03e312c9b09e29831"
ap-southeast-2 = "ami-0f6ad051716c81af1"
ap-northeast-1 = "ami-01b32aa8589df6208"
ca-central-1 = "ami-07e17b58812e0c7c2"
eu-central-1 = "ami-0b7fd829e7758b06d"
eu-west-1 = "ami-04f1014c8adcfa670"
eu-west-2 = "ami-0d76271a8a1525c1a"
eu-west-3 = "ami-05eb678ed1ab021c7"
eu-north-1 = "ami-0577c11149d377ab7"
sa-east-1 = "ami-0668aa31594136bd1"
}
}
variable "cidr" {
default = {
vpc = "192.168.0.0/24"
public_subnet = "192.168.0.0/25"
second_subnet = "192.168.0.128/25"
}
}
variable "ip_address" {
default = {
web = "192.168.0.10"
dns = "192.168.0.11"
client = "192.168.0.100"
}
}
resource "aws_vpc" "test-vpc" {
cidr_block = var.cidr.vpc
enable_dns_support = "true"
enable_dns_hostnames = "true"
tags = {
Name = "test-vpc"
}
}
resource "aws_internet_gateway" "test-igw" {
vpc_id = aws_vpc.test-vpc.id
tags = {
Name = "test-igw"
}
}
resource "aws_subnet" "test-public-subnet" {
vpc_id = aws_vpc.test-vpc.id
cidr_block = var.cidr.public_subnet
tags = {
Name = "test-public-subnet"
}
}
resource "aws_route_table" "test-public-subnet-route" {
vpc_id = aws_vpc.test-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.test-igw.id
}
}
resource "aws_route_table_association" "test-public-subnet-association" {
subnet_id = aws_subnet.test-public-subnet.id
route_table_id = aws_route_table.test-public-subnet-route.id
}
resource "aws_subnet" "test-second-subnet" {
vpc_id = aws_vpc.test-vpc.id
cidr_block = var.cidr.second_subnet
tags = {
Name = "test-second-subnet"
}
}
resource "aws_route_table" "test-second-subnet-route" {
vpc_id = aws_vpc.test-vpc.id
route {
cidr_block = "0.0.0.0/0"
gateway_id = aws_internet_gateway.test-igw.id
}
}
resource "aws_route_table_association" "test-second-subnet-association" {
subnet_id = aws_subnet.test-second-subnet.id
route_table_id = aws_route_table.test-second-subnet-route.id
}
resource "aws_security_group" "web-sg" {
name = "web-sg"
description = "Security group to pass HTTP traffics"
vpc_id = aws_vpc.test-vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 80
to_port = 80
protocol = "tcp"
cidr_blocks = [var.cidr.vpc]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "dns-sg" {
name = "dns-sg"
description = "Security group to pass DNS traffics"
vpc_id = aws_vpc.test-vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
ingress {
from_port = 53
to_port = 53
protocol = "tcp"
cidr_blocks = [var.cidr.vpc]
}
ingress {
from_port = 53
to_port = 53
protocol = "udp"
cidr_blocks = [var.cidr.vpc]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_security_group" "client-sg" {
name = "client-sg"
description = "Security group with ssh rule"
vpc_id = aws_vpc.test-vpc.id
ingress {
from_port = 22
to_port = 22
protocol = "tcp"
cidr_blocks = ["0.0.0.0/0"]
}
egress {
from_port = 0
to_port = 0
protocol = "-1"
cidr_blocks = ["0.0.0.0/0"]
}
}
resource "aws_instance" "web-instance" {
ami = var.images[var.region]
instance_type = "t2.micro"
subnet_id = aws_subnet.test-public-subnet.id
private_ip = var.ip_address.web
vpc_security_group_ids = [
"${aws_security_group.web-sg.id}"
]
associate_public_ip_address = "true"
user_data = <<EOF
#!/bin/bash
sudo yum update -y
sudo yum install nginx -y
sudo systemctl start nginx
EOF
tags = {
Name = "web-instance"
}
}
resource "aws_instance" "dns-instance" {
ami = var.images[var.region]
instance_type = "t2.micro"
subnet_id = aws_subnet.test-public-subnet.id
private_ip = var.ip_address.dns
vpc_security_group_ids = [
"${aws_security_group.dns-sg.id}"
]
associate_public_ip_address = "true"
user_data = file("dns_init.sh")
tags = {
Name = "dns-instance"
}
}
resource "aws_instance" "client-instance" {
ami = var.images[var.region]
instance_type = "t2.micro"
subnet_id = aws_subnet.test-public-subnet.id
private_ip = var.ip_address.client
vpc_security_group_ids = [
"${aws_security_group.client-sg.id}"
]
associate_public_ip_address = "true"
tags = {
Name = "client-instance"
}
}
#!/bin/bash
sudo yum update -y
sudo yum install bind -y
cat << 'eof' | sudo tee /etc/named/local
; BIND data file for local loopback interface;
$TTL 604800
@ IN SOA web.local. web.local. (
2 ; Serial
604800 ; Refresh
86400 ; Retry
2419200 ; Expire
604800 ) ; Negative Cache TTL
;
@ IN NS web.local.
web IN A 192.168.0.10
eof
cat << 'eof' | sudo tee /etc/named.conf
//
// named.conf
//
// Provided by Red Hat bind package to configure the ISC BIND named(8) DNS
// server as a caching only nameserver (as a localhost DNS resolver only).
//
// See /usr/share/doc/bind*/sample/ for example named configuration files.
//
options {
listen-on port 53 { any; };
listen-on-v6 port 53 { ::1; };
directory "/var/named";
dump-file "/var/named/data/cache_dump.db";
statistics-file "/var/named/data/named_stats.txt";
memstatistics-file "/var/named/data/named_mem_stats.txt";
secroots-file "/var/named/data/named.secroots";
recursing-file "/var/named/data/named.recursing";
allow-query { any; };
/*
- If you are building an AUTHORITATIVE DNS server, do NOT enable recursion.
- If you are building a RECURSIVE (caching) DNS server, you need to enable
recursion.
- If your recursive DNS server has a public IP address, you MUST enable access
control to limit queries to your legitimate users. Failing to do so will
cause your server to become part of large scale DNS amplification
attacks. Implementing BCP38 within your network would greatly
reduce such attack surface
*/
recursion yes;
dnssec-validation yes;
managed-keys-directory "/var/named/dynamic";
geoip-directory "/usr/share/GeoIP";
pid-file "/run/named/named.pid";
session-keyfile "/run/named/session.key";
/* https://fedoraproject.org/wiki/Changes/CryptoPolicy */
include "/etc/crypto-policies/back-ends/bind.config";
};
logging {
channel default_debug {
file "data/named.run";
severity dynamic;
};
};
zone "." IN {
type hint;
file "named.ca";
};
zone "local" {
type master;
file "/etc/named/local";
};
include "/etc/named.rfc1912.zones";
include "/etc/named.root.key";
eof
sudo systemctl start named
再現
この状態では名前解決ができません。
[ec2-user@ip-192-168-0-100 ~]$ nslookup web.local
Server: 192.168.0.2
Address: 192.168.0.2#53
** server can't find web.local: NXDOMAIN
一応DNSサーバーを直接指定すれば名前解決できますが、もしclientが各個人のラップトップPCであったりめったに触れられないものである場合は大変です。
[ec2-user@ip-192-168-0-100 ~]$ nslookup web.local 192.168.0.11
Server: 192.168.0.11
Address: 192.168.0.11#53
Name: web.local
Address: 192.168.0.10
そこでRoute 53 Resolverを使います。
Route 53 Resolverの立ち上げ
コンソールやCLIでも構いませんが、以下のコードを試してみてください。Route 53 ResolverがVPCに反映されるはずです。
resource "aws_route53_resolver_endpoint" "test-rslvr" {
name = "test-rslvr"
direction = "OUTBOUND"
security_group_ids = [
aws_security_group.dns-sg.id,
]
ip_address {
subnet_id = aws_subnet.test-public-subnet.id
}
ip_address {
subnet_id = aws_subnet.test-second-subnet.id
}
}
resource "aws_route53_resolver_rule" "test-rslvr-rule" {
domain_name = "local"
name = "test-rslvr-rule"
rule_type = "FORWARD"
resolver_endpoint_id = aws_route53_resolver_endpoint.test-rslvr.id
target_ip {
ip = var.ip_address.dns
}
}
resource "aws_route53_resolver_rule_association" "test-rslvr-rule-association" {
resolver_rule_id = aws_route53_resolver_rule.test-rslvr-rule.id
vpc_id = aws_vpc.test-vpc.id
}
結果
DNSサーバーを指定しなくても名前解決ができるようになります。
[ec2-user@ip-192-168-0-100 ~]$ nslookup web.local
Server: 192.168.0.2
Address: 192.168.0.2#53
Non-authoritative answer:
Name: web.local
Address: 192.168.0.10
もちろんcurlも通ります。
[ec2-user@ip-192-168-0-100 ~]$ curl web.local
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
最後に
terraform destroyでリソースを消すことを忘れないでください
