[Go] API 활용해 DNS 아이피 고정하기
집에서 서비스 배포 후 도메인에 연결시키기 위해서는 레코드에 아이피를 입력해줘야 하는데 가정집의 경우 유동 아이피이기 때문에 업데이를 계속 해주거나 DDNS 서비스를 활용해서 연결해야한다. 그래서 DDNS 서비스를 사용하던 중 Namesilo 사에서 api로 레코드 정보를 조회하고 업데이트할 수 있는 방법이 있어서 이 방법으로 하려고 한다.
순서
도메인의 레코드 조회 -> A레코드에 해당하는 값들 저장 -> 아이피 조회 -> A레코드에 아이피 업데이트 -> 빌드 후 crontab에 등록
Namesilo의 API키 발급
발급은 여기에서 할 수 있다. 내가 이용할 API는 조회와 업데이트이다.
도메인의 레코드 조회
도메인의 레코드를 조회하는 함수이다
func aDomain() []ResourceRecord {
url := "https://www.namesilo.com/api/dnsListRecords?version=1&type=xml&key=" + apiKey + "&domain=" + myDomain
res, err := http.Get(url)
checkErr(err)
defer res.Body.Close()
data, err := ioutil.ReadAll(res.Body)
checkErr(err)
var name NameSilo
err = xml.Unmarshal(data, &name)
checkErr(err)
var aDomains []ResourceRecord
for _, resource := range name.Reply.ResourceRecord {
if resource.Typee == "A" {
aDomains = append(aDomains, resource)
}
}
return aDomains
}
url을 만들고 get으로 요청을 보내서 응답을 받는다. 이때 응답은 xml로 이루어지기 때문에 xml 구조체를 정의해야 한다.
type NameSilo struct {
XMLName xml.Name `xml:"namesilo"`
Request Request `xml:"request"`
Reply Reply `xml:"reply"`
}
type Request struct {
XMLName xml.Name `xml:"request"`
Operation string `xml:"operation"`
Ip string `xml:"ip"`
}
type Reply struct {
XMLName xml.Name `xml:"reply"`
Code string `xml:"code"`
Detail string `xml:"detail"`
ResourceRecord []ResourceRecord `xml:"resource_record"`
}
type ResourceRecord struct {
XMLName xml.Name `xml:"resource_record"`
RecordId string `xml:"record_id"`
Typee string `xml:"type"`
Host string `xml:"host"`
Value string `xml:"value"`
Ttl string `xml:"ttl"`
Distance string `xml:"distance"`
}
아까 링크에서 볼 수 있는 예시응답을 참조하여서 만들었다. A레코드만을 가져와서 저장하게끔 함수를 만들어주었다. 사실 만약 탑레벨 도메인이 A레코드로 연결돼있고, 서브도메인에 와일드카드를 사용했다면 그냥 바로 아이피 조회 후 업데이트를 할 수 있다.
ip 조회
ip 조회는 ifconfig.me에 요청하면 받을 수 있는 ip주소의 값을 사용했다.
func ipAddr() string {
resp, err := http.Get("https://ifconfig.me")
checkErr(err)
defer resp.Body.Close()
data, err := ioutil.ReadAll(resp.Body)
checkErr(err)
return string(data)
}
레코드 업데이트
이제 레코드를 업데이트하기 위한 함수를 만든다.
func update(aDomains []ResourceRecord, ip string) {
for _, resource := range aDomains {
var urlString string
if resource.Host == myDomain {
urlString = "https://www.namesilo.com/api/dnsUpdateRecord?version=1&type=xml&key=" + apiKey + "&domain=" + myDomain + "&rrid=" + resource.RecordId + "&rrvalue=" + ip
} else {
urlString = "https://www.namesilo.com/api/dnsUpdateRecord?version=1&type=xml&key=" + apiKey + "&domain=" + myDomain + "&rrid=" + resource.RecordId + "&rrhost=" + strings.Split(resource.Host, "."+myDomain)[0] + "&rrvalue=" + ip
}
respo, err := http.Get(urlString)
checkErr(err)
defer respo.Body.Close()
dataaa, err := ioutil.ReadAll(respo.Body)
checkErr(err)
var updatesilo NamesiloUpdate
err = xml.Unmarshal(dataaa, &updatesilo)
checkErr(err)
log.Println(updatesilo)
}
}
아까의 함수들을 이용해 생성한 값들을 인자로 넘겨받을 수 있게 하였다. 응답받을 때의 host 값은 서브.도메인.컴 이런 식으로 받는다. 요청할 때는 host값을 서브도>메인 혹은 탑레벨일 경우 값 없이 보내줘야 하기 때문에 if 구문을 추가하여서 확인을 한 번 거치게끔 하였다.
그 후 응답이 어떻게 왔는지 확인하기 위해서 xml응답을 저장 후 출력하게끔 만들었다. 이를 위해 또 xml 구조체를 만들어야 하는데 이는 다음과 같다.
type NamesiloUpdate struct {
XMLNAME xml.Name `xml:"namesilo"`
Request RequestUpdate `xml:"request"`
Reply ReplyUpdate `xml:"reply"`
}
type RequestUpdate struct {
Operation string `xml:"operation"`
Ip string `xml:"ip"`
}
type ReplyUpdate struct {
Code string `xml:"code"`
Detail string `xml:"detail"`
RecordId string `xml:"record_id"`
}