Starting trying out nsupdate calls from Go
This commit is contained in:
parent
3cd72d6e74
commit
2614cd2e90
5
go.mod
5
go.mod
@ -37,6 +37,8 @@ require (
|
||||
github.com/googleapis/gax-go/v2 v2.12.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/jcmturner/gofork v1.7.6 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
@ -47,6 +49,8 @@ require (
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
google.golang.org/api v0.154.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 // indirect
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
@ -82,6 +86,7 @@ require (
|
||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.18.1 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jcmturner/gokrb5 v8.4.4+incompatible
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
|
||||
10
go.sum
10
go.sum
@ -171,10 +171,16 @@ github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxC
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5 h1:bJj+Pj19UZMIweq/iie+1u5YCdGrnxCT9yvm0e+Nd5M=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.5/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
|
||||
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jcmturner/gofork v1.7.6 h1:QH0l3hzAU1tfT3rZCnW5zXl+orbkNMMRGJfdJjHVETg=
|
||||
github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo=
|
||||
github.com/jcmturner/gokrb5 v8.4.4+incompatible h1:aX4yX9Lwq0U7yurW6pzRH5JJYDwK0hWIPBTTWfWBOLQ=
|
||||
github.com/jcmturner/gokrb5 v8.4.4+incompatible/go.mod h1:0Q5eFyVvYsEsZ8xl1A/jUqhXvxUp/X9ELrJm+zieq5E=
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=
|
||||
github.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||
@ -442,6 +448,10 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0 h1:1duIyWiTaYvVx3YX2CYtpJbUFd7/UuPYCfgXtQ3VTbI=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
|
||||
gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
||||
126
pkg/auth/kerberos.go
Normal file
126
pkg/auth/kerberos.go
Normal file
@ -0,0 +1,126 @@
|
||||
package auth
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/user"
|
||||
"sync"
|
||||
|
||||
cred "github.com/jcmturner/gokrb5/credentials"
|
||||
)
|
||||
|
||||
const (
|
||||
kinitCmd = "kinit"
|
||||
kvnoCmd = "kvno"
|
||||
klistCmd = "klist"
|
||||
spn = "DNS/upd.sthome.net@STHOME.LAN"
|
||||
)
|
||||
|
||||
type output struct {
|
||||
buf *bytes.Buffer
|
||||
lines []string
|
||||
*sync.Mutex
|
||||
}
|
||||
|
||||
func newOutput() *output {
|
||||
return &output{
|
||||
buf: &bytes.Buffer{},
|
||||
lines: []string{},
|
||||
Mutex: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *output) Write(p []byte) (int, error) {
|
||||
rw.Lock()
|
||||
defer rw.Unlock()
|
||||
return rw.buf.Write(p)
|
||||
}
|
||||
|
||||
func (rw *output) Lines() []string {
|
||||
rw.Lock()
|
||||
defer rw.Unlock()
|
||||
s := bufio.NewScanner(rw.buf)
|
||||
for s.Scan() {
|
||||
rw.lines = append(rw.lines, s.Text())
|
||||
}
|
||||
return rw.lines
|
||||
}
|
||||
|
||||
func login() error {
|
||||
file, err := os.Create("/etc/krb5.conf")
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot open krb5.conf: %v", err)
|
||||
}
|
||||
defer file.Close()
|
||||
fmt.Fprintf(file, krb5conf)
|
||||
|
||||
cmd := exec.Command(kinitCmd, dnsupdateuser)
|
||||
|
||||
stdinR, stdinW := io.Pipe()
|
||||
stderrR, stderrW := io.Pipe()
|
||||
cmd.Stdin = stdinR
|
||||
cmd.Stderr = stderrW
|
||||
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start %s command: %v", kinitCmd, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
io.WriteString(stdinW, "passwordvalue")
|
||||
stdinW.Close()
|
||||
}()
|
||||
errBuf := new(bytes.Buffer)
|
||||
go func() {
|
||||
io.Copy(errBuf, stderrR)
|
||||
stderrR.Close()
|
||||
}()
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s did not run successfully: %v stderr: %s", kinitCmd, err, string(errBuf.Bytes()))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getServiceTkt() error {
|
||||
cmd := exec.Command(kvnoCmd, spn)
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not start %s command: %v", kvnoCmd, err)
|
||||
}
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return fmt.Errorf("%s did not run successfully: %v", kvnoCmd, err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func klist() ([]string, error) {
|
||||
cmd := exec.Command(klistCmd, "-Aef")
|
||||
|
||||
stdout := newOutput()
|
||||
cmd.Stdout = stdout
|
||||
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("could not start %s command: %v", klistCmd, err)
|
||||
}
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return []string{}, fmt.Errorf("%s did not run successfully: %v", klistCmd, err)
|
||||
}
|
||||
|
||||
return stdout.Lines(), nil
|
||||
}
|
||||
|
||||
func loadCCache() (*cred.CCache, error) {
|
||||
usr, _ := user.Current()
|
||||
cpath := "/tmp/krb5cc_" + usr.Uid
|
||||
return cred.LoadCCache(cpath)
|
||||
}
|
||||
43
pkg/auth/krbconf.go
Normal file
43
pkg/auth/krbconf.go
Normal file
@ -0,0 +1,43 @@
|
||||
package auth
|
||||
|
||||
const (
|
||||
dnsupdateuser = "dns_updater@STHOME.LAN"
|
||||
krb5conf = `[logging]
|
||||
default = FILE:/var/log/krb5libs.log
|
||||
kdc = FILE:/var/log/krb5kdc.log
|
||||
admin_server = FILE:/var/log/kadmind.log
|
||||
|
||||
[libdefaults]
|
||||
# specifies the default realm that needs to be picked up for authentication
|
||||
default_realm = STHOME.LAN
|
||||
# set following to true, if you're specified dns' instead of IP addresses under [realms]
|
||||
dns_lookup_realm = false
|
||||
# specifies whether DNS SRV records should be used to locate the KDCs and other servers for a realm
|
||||
dns_lookup_kdc = false
|
||||
# this is a mandatory flag as we need to obtain forwardable tickets from the KDC
|
||||
forward = true
|
||||
# specifies if initial tickets will be forwardable by default, if allowed by the KDC
|
||||
forwardable = true
|
||||
ticket_lifetime = 24h
|
||||
renew_lifetime = 7d
|
||||
rdns = false
|
||||
default_ccache_name = KEYRING:persistent:%{uid}
|
||||
pkinit_dh_min_bits = 1024
|
||||
# specifies that short hostnames should be canonicalized to fully-qualified hostnames
|
||||
dns_canonicalize_hostname = true
|
||||
|
||||
[realms]
|
||||
# Realm configuration with different possible way to be resolved
|
||||
STHOME.LAN = {
|
||||
admin_server = 192.168.2.1:749
|
||||
kdc = 192.168.2.1:88
|
||||
kdc = 192.168.2.4:88
|
||||
}
|
||||
|
||||
[domain_realm]
|
||||
sthome.lan = STHOME.LAN
|
||||
.sthome.lan = STHOME.LAN
|
||||
sthome.net = STHOME.LAN
|
||||
.sthome.net = STHOME.LAN
|
||||
`
|
||||
)
|
||||
101
pkg/dns/update.go
Normal file
101
pkg/dns/update.go
Normal file
@ -0,0 +1,101 @@
|
||||
package dns
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os/exec"
|
||||
"sync"
|
||||
|
||||
"github.com/stuurmcp/cert-manager-webhook-sthome/pkg/util"
|
||||
"k8s.io/klog/v2"
|
||||
)
|
||||
|
||||
const (
|
||||
nsupdateCmd = "nsupdate"
|
||||
ttl = "7200"
|
||||
)
|
||||
|
||||
type output struct {
|
||||
buf *bytes.Buffer
|
||||
lines []string
|
||||
*sync.Mutex
|
||||
}
|
||||
|
||||
func AddTxtRecord(nameservers []string, domain string, fulldomain string, recordvalue string) error {
|
||||
return processTXT("add", nameservers, domain, fulldomain, recordvalue)
|
||||
}
|
||||
|
||||
func DeleteTxtRecord(nameservers []string, domain string, fulldomain string, recordvalue string) error {
|
||||
return processTXT("delete", nameservers, domain, fulldomain, recordvalue)
|
||||
}
|
||||
|
||||
func processTXT(action string, nameservers []string, domain string, fulldomain string, recordvalue string) error {
|
||||
for _, ns := range nameservers {
|
||||
klog.Infof("======= ns: %s =======", ns)
|
||||
zone := extractDomainName(domain)
|
||||
nsucmd := " gsstsig\n server " + ns + "\n zone " + zone + "\n class IN\n update " + action + " " + fulldomain + " " + ttl + " TXT " + recordvalue + "\n send\n"
|
||||
out, err := nsupdate(nsucmd)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, line := range out.lines {
|
||||
klog.Infof(line)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func newOutput() *output {
|
||||
return &output{
|
||||
buf: &bytes.Buffer{},
|
||||
lines: []string{},
|
||||
Mutex: &sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (rw *output) Write(p []byte) (int, error) {
|
||||
rw.Lock()
|
||||
defer rw.Unlock()
|
||||
return rw.buf.Write(p)
|
||||
}
|
||||
|
||||
func nsupdate(nsucmd string) (*output, error) {
|
||||
klog.Infof("nsupdate\n%s", nsucmd) // for debugging
|
||||
cmd := exec.Command(nsupdateCmd)
|
||||
stdout := newOutput()
|
||||
cmd.Stdout = stdout
|
||||
stdinR, stdinW := io.Pipe()
|
||||
stderrR, stderrW := io.Pipe()
|
||||
cmd.Stdin = stdinR
|
||||
cmd.Stderr = stderrW
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
return stdout, fmt.Errorf("could not start %s command: %v", nsupdateCmd, err)
|
||||
}
|
||||
|
||||
go func() {
|
||||
io.WriteString(stdinW, nsucmd)
|
||||
stdinW.Close()
|
||||
}()
|
||||
errBuf := new(bytes.Buffer)
|
||||
go func() {
|
||||
io.Copy(errBuf, stderrR)
|
||||
stderrR.Close()
|
||||
}()
|
||||
|
||||
err = cmd.Wait()
|
||||
if err != nil {
|
||||
return stdout, fmt.Errorf("%s did not run successfully: %v stderr: %s", nsupdateCmd, err, errBuf.String())
|
||||
}
|
||||
return stdout, nil
|
||||
}
|
||||
|
||||
func extractDomainName(zone string) string {
|
||||
authZone, err := util.FindZoneByFqdn(zone, util.RecursiveNameservers)
|
||||
if err != nil {
|
||||
klog.Errorf("could not get zone by fqdn %v", err)
|
||||
return zone
|
||||
}
|
||||
return util.UnFqdn(authZone)
|
||||
}
|
||||
@ -1,3 +1,3 @@
|
||||
0.0.5-alpha.113
|
||||
0.0.6-alpha.0
|
||||
20240422-0918
|
||||
113
|
||||
0
|
||||
Loading…
Reference in New Issue
Block a user