GithubHelp home page GithubHelp logo

bodgit / tsig Goto Github PK

View Code? Open in Web Editor NEW
10.0 3.0 9.0 439 KB

Golang library to support additional TSIG methods for DNS queries

Home Page: https://godoc.org/github.com/bodgit/tsig

License: BSD 3-Clause "New" or "Revised" License

Go 100.00%
golang golang-library dns tsig gssapi sspi rfc-3645 rfc-2930 diffie-hellman rfc-2845

tsig's Introduction

GitHub release Build Status Coverage Status Go Report Card GoDoc Go version Go version

Additional TSIG methods

The github.com/bodgit/tsig package adds support for additional TSIG methods used in DNS queries. It is designed to be used alongside the github.com/miekg/dns package which is used to construct and parse DNS queries and responses.

This is most useful for allowing RFC 3645 GSS-TSIG which is necessary for dealing with Windows DNS servers that require 'Secure only' updates or BIND if it has been configured to use Kerberos.

โš ๏ธ Windows DNS servers don't accept wildcard resource names in dynamic updates.

Here is an example client, it is necessary that your Kerberos or Active Directory environment is configured and functional:

package main

import (
        "fmt"
        "time"

        "github.com/bodgit/tsig"
        "github.com/bodgit/tsig/gss"
        "github.com/miekg/dns"
)

func main() {
        dnsClient := new(dns.Client)
        dnsClient.Net = "tcp"

        gssClient, err := gss.NewClient(dnsClient)
        if err != nil {
                panic(err)
        }
        defer gssClient.Close()

        host := "ns.example.com:53"

        // Negotiate a context with the chosen server using the
        // current user. See also gssClient.NegotiateContextWithCredentials()
        // and gssClient.NegotiateContextWithKeytab() for alternatives
        keyname, _, err := gssClient.NegotiateContext(host)
        if err != nil {
                panic(err)
        }

        dnsClient.TsigProvider = gssClient

        // Use the DNS client as normal

        msg := new(dns.Msg)
        msg.SetUpdate(dns.Fqdn("example.com"))

        insert, err := dns.NewRR("test.example.com. 300 A 192.0.2.1")
        if err != nil {
                panic(err)
        }
        msg.Insert([]dns.RR{insert})

        msg.SetTsig(keyname, tsig.GSS, 300, time.Now().Unix())

        rr, _, err := dnsClient.Exchange(msg, host)
        if err != nil {
                panic(err)
        }

        if rr.Rcode != dns.RcodeSuccess {
                fmt.Printf("DNS error: %s (%d)\n", dns.RcodeToString[rr.Rcode], rr.Rcode)
        }

        // Cleanup the context
        err = gssClient.DeleteContext(keyname)
        if err != nil {
                panic(err)
        }
}

If you need to deal with both regular TSIG and GSS-TSIG together then this package also exports an HMAC TSIG implementation. To use both together set your client up something like this:

package main

import (
        "github.com/bodgit/tsig"
        "github.com/bodgit/tsig/gss"
        "github.com/miekg/dns"
)

func main() {
        dnsClient := new(dns.Client)
        dnsClient.Net = "tcp"

        // Create HMAC TSIG provider
        hmac := tsig.HMAC{"axfr.": "so6ZGir4GPAqINNh9U5c3A=="}

        // Create GSS-TSIG provider
        gssClient, err := gss.NewClient(dnsClient)
        if err != nil {
                panic(err)
        }
        defer gssClient.Close()

        // Configure DNS client with both providers
        dnsClient.TsigProvider = tsig.MultiProvider(hmac, gssClient)

        // Use the DNS client as normal
}

tsig's People

Contributors

bodgit avatar branden-blackline avatar dependabot[bot] avatar ethanmoffat avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

tsig's Issues

Concurrent access

Establishing and destroying multiple contexts can lead to concurrent map access within the main GSS struct which causes a panic.

Adding a mutex around map writes should fix it.

Relax set of GSSAPI flags

gssapi.GSS_C_DELEG_FLAG|gssapi.GSS_C_MUTUAL_FLAG|gssapi.GSS_C_REPLAY_FLAG|gssapi.GSS_C_SEQUENCE_FLAG|gssapi.GSS_C_INTEG_FLAG,

nsupdate uses just GSS_C_REPLAY_FLAG | GSS_C_MUTUAL_FLAG | GSS_C_INTEG_FLAG. Certainly having GSS_C_SEQUENCE_FLAG set may break when responses are received out of order, which can happen.

Return correct expiration

The negotiated context attempts to indicate the expiration time of the context however it never seems to match reality. The TKEY record has an expiry field which will indicate validity for an hour yet after five minutes the negotiated context will cease to work.

Perhaps the expiry field in the TKEY record needs to be ignored and the expiration is buried in the negotiated GSSAPI/Kerberos/SSPI context.

Need to check for trailing dot

If a DNS query is used to locate the nameserver(s) then it could be returned with a trailing ., i.e. ns.example.com.. This needs to be stripped off for generating the SPN and passing to Exchange() when joined with a port argument.

Incorrect flags when creating context using gokrb5

The apcera code uses the following flags when creating the context:

gssapi.GSS_C_MUTUAL_FLAG|gssapi.GSS_C_REPLAY_FLAG|gssapi.GSS_C_INTEG_FLAG,

However the gokrb5 version omits the mutual and replay flags:

apreq, err := spnego.NewKRB5TokenAPREQ(cl, tkt, key, []int{gssapi.ContextFlagInteg}, []int{gssapi.ContextFlagMutual})

Even though the mutual flag is there, it's added as the APReq options rather than the GSSAPI flags which seems wrong.

Use gokrb5.v8 instead gokrb5.v7

Based on changelog from gokrb5, they solved a "Potential leak of the SPNEGO client session due to reuse of the same cookie jar" and other minor fixes.

Make Apcera GSSAPI optional

Hide the Apcera GSSAPI implementation behind a build tag so we always prefer the native Gokrb5 implementation on !windows.

Fix tests on Ubuntu 22.04

Tests seem to break when ubuntu-latest switched to 22.04, suspect something to do with loopback networking.

Adding a wildcard record

Is it possible to add a wildcard record?
If I try to add "*.example.com. 300 A 192.0.2.1", I get this error:
"unexpected acceptor flag is not set: expecting a token from the acceptor, not in the initiator"

Error updating DNS record: unexpected acceptor flag is not set: expecting a token from the acceptor, not in the initiator

It appears that the terraform dns provider is throwing the error "Error updating DNS record: unexpected acceptor flag is not set: expecting a token from the acceptor, not in the initiator" from this package. See issue hashicorp/terraform-provider-dns#160

I have traced the issue to the parameters passed to https://github.com/bodgit/tsig/blob/v1.1.1/gss/gokrb5.go#L243

When changing the parameters to match those passed in the ns1 fork https://github.com/ns1/tsig/blob/master/gss/gokrb5.go#L150 the issue does not present itself. I am not sure why this resolves the issue and really have no insight into what the parameters do.

The issue is reproducible on an active directory dns server.

Add more detail to error returned from `loadConfig`

Hello ๐Ÿ‘‹๐Ÿป ,

Background

We recently had some questions about a confusing error that bubbled up from this library in the DNS terraform provider: hashicorp/terraform-provider-dns#128

Error updating DNS record: Error negotiating GSS context: configuration file could not be opened:  open : no such file or directory

This error is caused by not setting the KRB5_CONFIG environment variable and not having a valid config in one of the default locations defined here:

try := []string{"/etc/krb5.conf"}

Looking a little deeper it seems in this situation that the blank file path is passed down to the jcmturner/gokrb5 library and then fails in the os.Open method: https://github.com/jcmturner/gokrb5/blob/2a265cd1f9a5e200336857e55e38ef3c06815891/config/krb5conf.go#L522

Proposal

  • We could add more detail to the error message describing that no valid path was found in environment variable or default paths

Implement replay protection when using gokrb5

Even though we now negotiate replay protection with the server when using gokrb5, we don't actually keep track of the sequence numbers used by the server; gokrb5 doesn't track this for us automatically unlike the other GSSAPI implementations.

Not sure if it's as simple as keeping a map[uint64]struct{}{} handy and store each received sequence number.

Unable to specify port

Can't currently pass a port. Highly unlikely a non-standard port would ever be used however it should be supported. Either extend the host argument to allow passing hostname:port which means we need to split it and get the hostname back out to be able to generate the SPN, etc. or add a separate port argument.

Acceptor subkey flag is always set regardless of key used

#86 allowed the acceptor subkey to be optional and to fall back to using the session key, however it seems that the subkey flag is always set, i.e.

tsig/gss/gokrb5.go

Lines 186 to 190 in 0fe7e6c

token := gssapi.MICToken{
Flags: gssapi.MICTokenFlagAcceptorSubkey,
SndSeqNum: ctx.seq,
Payload: msg,
}

Not sure how this isn't breaking things in the no subkey scenario, but that flag should be unset when no subkey is used.

Make DNS client optional in GSS-TSIG constructor

Now there's a variadic parameter on the constructor, I could drop the required first parameter and add a gss.WithDNSClient(client) option function, and make the default a new dns.Client with the transport set to tcp.

This would be a v2 change.

Add minimal copy of `dns.Client`

Due to the upstream maintainer not willing to participate on the PR to add support for GSS-TSIG I'm left with the only solution being to copy enough of the dns.Client code in order to be able to add non-HMAC functionality.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.