package main import ( "bufio" "crypto/tls" "encoding/json" "flag" "fmt" "io/ioutil" "log" "net/http" "os" "strings" "gopkg.in/ldap.v2" ) var ( configFile string domain string host string ldap_uri string ldap_default_bind_dn string ldap_default_authtok string ldap_search_base string ) type IP struct { Query string } func init() { test, err := os.Hostname() if err == nil { host = test } flag.StringVar(&configFile, "c", "/etc/sssd/sssd.conf", "Path to SSSD Config.") flag.StringVar(&host, "h", "", "Host ID for DNS (relativeDomainName)") flag.StringVar(&domain, "d", "", "Domain for DNS (zoneName)") flag.Parse() } func main() { log.Printf("Using %+v as config file path", configFile) file, err := os.Open(configFile) if err != nil { log.Fatal(err) } defer file.Close() scanner := bufio.NewScanner(file) // optionally, resize scanner's capacity for lines over 64K, see next example for scanner.Scan() { line := scanner.Text() mod := &ldap_uri switch { case strings.Contains(line, "ldap_uri"): mod = &ldap_uri case strings.Contains(line, "ldap_default_bind_dn"): mod = &ldap_default_bind_dn case strings.Contains(line, "ldap_default_authtok"): mod = &ldap_default_authtok case strings.Contains(line, "ldap_search_base"): mod = &ldap_search_base default: continue } *mod = strings.Split(line, "= ")[1] } if err := scanner.Err(); err != nil { log.Fatal(err) } log.Printf("Connecting to %s", ldap_uri) ldap_uri = strings.ReplaceAll(ldap_uri, "ldap://", "") l, err := ldap.Dial("tcp", ldap_uri) if err != nil { log.Fatal(err) } defer l.Close() log.Printf("Connected to %s, reconnecting with TLS", ldap_uri) // Reconnect with TLS err = l.StartTLS(&tls.Config{InsecureSkipVerify: true}) if err != nil { log.Fatal(err) } log.Printf("Binding as %s", ldap_default_bind_dn) // First bind with a read only user err = l.Bind(ldap_default_bind_dn, ldap_default_authtok) if err != nil { log.Fatal(err) } log.Printf("Connected as read-only user, searching for DNS Record") // Search for the given username searchRequest := ldap.NewSearchRequest( fmt.Sprintf("zoneName=%s,cn=dns,%s", ldap.EscapeFilter(domain), ldap.EscapeFilter(ldap_search_base)), ldap.ScopeWholeSubtree, ldap.NeverDerefAliases, 0, 0, false, fmt.Sprintf("(relativeDomainName=%s)", ldap.EscapeFilter(host)), []string{"dn","aRecord"}, nil, ) sr, err := l.Search(searchRequest) if err != nil { log.Fatal(err) } if len(sr.Entries) != 1 { log.Fatalf("Host does not exist or too many entries returned (%+v)", len(sr.Entries)) } log.Printf("Record found for %s.%s: %+v", host, domain, sr.Entries[0]) newIp := getip2() if sr.Entries[0].GetAttributeValue("aRecord") == newIp { log.Println("New IP is same as old IP, exiting gracefully.") return } req := ldap.NewModifyRequest(sr.Entries[0].DN) req.Replace("aRecord", []string{newIp}) if err = l.Modify(req); err != nil { log.Fatalf("Failed to modify DN: %s\n", err) } log.Printf("Updated record successfully.") } func getip2() string { req, err := http.Get("http://ip-api.com/json/") if err != nil { return err.Error() } defer req.Body.Close() body, err := ioutil.ReadAll(req.Body) if err != nil { return err.Error() } var ip IP json.Unmarshal(body, &ip) log.Printf("Detected IP as %s", ip.Query) return ip.Query }