Skip to content

Commit

Permalink
Add fallback DNS (#414)
Browse files Browse the repository at this point in the history
  • Loading branch information
varunsh-coder authored Jun 15, 2023
1 parent de3a03e commit 2a612fe
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 54 deletions.
56 changes: 23 additions & 33 deletions .github/workflows/int.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,37 +12,27 @@ jobs:
integration-test:
permissions:
contents: read
runs-on: ubuntu-20.04
runs-on: ubuntu-latest
steps:
- uses: step-security/harden-runner@v2
with:
allowed-endpoints:
api.github.com:443
github.com:443
golang.org:443
int.api.stepsecurity.io:443
objects.githubusercontent.com:443
pipelines.actions.githubusercontent.com:443
proxy.golang.org:443
step-security-agent.s3.us-west-2.amazonaws.com:443
storage.googleapis.com:443
sts.us-west-2.amazonaws.com:443
- name: Checkout
uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5
- name: Set up Go
uses: actions/setup-go@424fc82d43fa5a37540bae62709ddcc23d9520d4
with:
go-version: 1.19
- run: sudo go test -v
- run: go build -ldflags="-s -w" -o ./agent
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- run: aws s3 cp ./agent s3://step-security-agent/refs/heads/int/agent --acl public-read
- name: Integration test
uses: docker://ghcr.io/step-security/integration-test/int:latest
env:
PAT: ${{ secrets.PAT }}
- uses: step-security/harden-runner@v2
with:
egress-policy: audit
- name: Checkout
uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5
- name: Set up Go
uses: actions/setup-go@424fc82d43fa5a37540bae62709ddcc23d9520d4
with:
go-version: 1.19
- run: sudo go test -v
- run: go build -ldflags="-s -w" -o ./agent
- name: Configure aws credentials
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: us-west-2
- run: aws s3 cp ./agent s3://step-security-agent/refs/heads/int/agent --acl public-read
- name: Integration test
uses: docker://ghcr.io/step-security/integration-test/int:latest
env:
PAT: ${{ secrets.PAT }}
20 changes: 19 additions & 1 deletion agent_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,25 @@ func TestRun(t *testing.T) {
},
t.Log))

httpmock.RegisterResponder("GET", "https://dns.google/resolve", // no query params to match all other requests
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=actions-results-receiver-production.githubapp.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=artifactcache.actions.githubusercontent.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=pipelines.actions.githubusercontent.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=token.actions.githubusercontent.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=codeload.github.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=vstsmms.actions.githubusercontent.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=vstoken.actions.githubusercontent.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"requesteddomain.com.","type":1}],"Answer":[{"name":"requesteddomain.com.","type":1,"TTL":300,"data":"69.69.69.69"}]}`))

httpmock.RegisterResponder("GET", "https://apiurl/v1/github/owner/repo/actions/subscription",
Expand Down
20 changes: 15 additions & 5 deletions dnsproxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,18 @@ func (proxy *DNSProxy) isAllowedDomain(domain string) bool {

func (proxy *DNSProxy) ResolveDomain(domain string) (*Answer, error) {
url := fmt.Sprintf("https://dns.google/resolve?name=%s&type=a", domain)

fallbackUrl := fmt.Sprintf("https://cloudflare-dns.com/dns-query?name=%s&type=A", domain)
retryCounter := 0
var httpError error
var resp *http.Response
for retryCounter < 2 {
resp, httpError = proxy.ApiClient.Client.Get(url)
requestUrl := url
if retryCounter == 1 {
requestUrl = fallbackUrl
}
req, _ := http.NewRequest("GET", requestUrl, nil)
req.Header.Add("accept", "application/dns-json")
resp, httpError = proxy.ApiClient.Client.Do(req)
if httpError != nil {
retryCounter++
} else {
Expand Down Expand Up @@ -251,15 +257,19 @@ func (proxy *DNSProxy) processTypeA(q *dns.Question, requestMsg *dns.Msg) (*dns.
queryMsg := new(dns.Msg)
requestMsg.CopyTo(queryMsg)
queryMsg.Question = []dns.Question{*q}
domains := map[string]string{
"dns.google.": "8.8.8.8",
"cloudflare-dns.com.": "1.1.1.1",
}

if q.Name == "dns.google." {
rr, err := dns.NewRR("dns.google. IN A 8.8.8.8")
if ip, ok := domains[q.Name]; ok {
rr, err := dns.NewRR(fmt.Sprintf("%s IN A %s", q.Name, ip))

if err != nil {
return nil, err
}

proxy.Cache.Set(q.Name, &Answer{Name: q.Name, TTL: math.MaxInt32, Data: "8.8.8.8"}, false)
proxy.Cache.Set(q.Name, &Answer{Name: q.Name, TTL: math.MaxInt32, Data: ip}, false)

return &rr, nil
}
Expand Down
17 changes: 17 additions & 0 deletions dnsproxy_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@ func TestDNSProxy_getResponse(t *testing.T) {
auditCache := InitCache(EgressPolicyAudit)
blockCache := InitCache(EgressPolicyBlock)
rrDnsGoogle, _ := dns.NewRR("dns.google. IN A 8.8.8.8")
rrDnsCloudflare, _ := dns.NewRR("cloudflare-dns.com. IN A 1.1.1.1")
rrDnsTest, _ := dns.NewRR("test.com. IN A 67.225.146.248")
rrDnsNotAllowed, _ := dns.NewRR(fmt.Sprintf("notallowed.com. IN A %s", StepSecuritySinkHoleIPAddress))
rrDnsAllowed, _ := dns.NewRR("allowed.com. IN A 67.225.146.248")
rrDnsMcr, _ := dns.NewRR("westus.data.mcr.microsoft.com. IN A 67.225.146.248")
rrDnsFallback, _ := dns.NewRR("testfallback.com. IN A 67.225.146.248")
allowedEndpoints := make(map[string][]Endpoint)
allowedEndpoints["allowed.com."] = append(allowedEndpoints["allowed.com."], Endpoint{domainName: "allowed.com"})
allowedEndpointsTest := make(map[string][]Endpoint)
Expand All @@ -51,13 +53,28 @@ func TestDNSProxy_getResponse(t *testing.T) {
httpmock.RegisterResponder("GET", "https://dns.google/resolve?name=notfound.com.&type=a",
httpmock.NewStringResponder(200, `{"Status":3,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"notfound.com.","type":1}],"Authority":[{"name":"com.","type":6,"TTL":900,"data":"a.gtld-servers.net. nstld.verisign-grs.com. 1640040308 1800 900 604800 86400"}],"Comment":"Response from 2001:503:231d::2:30."}`))

httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=testfallback.com.&type=A",
httpmock.NewStringResponder(200, `{"Status":0,"TC":false,"RD":true,"RA":true,"AD":false,"CD":false,"Question":[{"name":"testfallback.com.","type":1}],"Answer":[{"name":"testfallback.com.","type":1,"TTL":3080,"data":"67.225.146.248"}]}`))

tests := []struct {
name string
fields fields
args args
want *dns.Msg
wantErr bool
}{
{name: "test fallback",
fields: fields{Cache: &auditCache},
args: args{requestMsg: &dns.Msg{Question: []dns.Question{{Name: "testfallback.com.", Qtype: dns.TypeA}}}},
want: &dns.Msg{Answer: []dns.RR{rrDnsFallback}},
wantErr: false,
},
{name: "type A cloudflare-dns.com.",
fields: fields{Cache: &auditCache},
args: args{requestMsg: &dns.Msg{Question: []dns.Question{{Name: "cloudflare-dns.com.", Qtype: dns.TypeA}}}},
want: &dns.Msg{Answer: []dns.RR{rrDnsCloudflare}},
wantErr: false,
},
{name: "type A dns.google",
fields: fields{Cache: &auditCache},
args: args{requestMsg: &dns.Msg{Question: []dns.Question{{Name: "dns.google.", Qtype: dns.TypeA}}}},
Expand Down
23 changes: 8 additions & 15 deletions firewall.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ const (
target = "-j"
accept = "ACCEPT"
reject = "REJECT"
dnsServerIP = "8.8.8.8"
dnsServerIP2 = "8.8.4.4"
classAPrivateAddressRange = "10.0.0.0/8"
classBPrivateAddressRange = "172.16.0.0/12"
classCPrivateAddressRange = "192.168.0.0/16"
Expand Down Expand Up @@ -58,6 +56,8 @@ func addBlockRulesForGitHubHostedRunner(firewall *Firewall, endpoints []ipAddres
func addBlockRules(firewall *Firewall, endpoints []ipAddressEndpoint, chain, netInterface, direction string) error {
var ipt IPTables
var err error
dnsServers := []string{"8.8.8.8", "8.8.4.4", "1.1.1.1"}

if firewall == nil {

ipt, err = iptables.New()
Expand Down Expand Up @@ -88,20 +88,13 @@ func addBlockRules(firewall *Firewall, endpoints []ipAddressEndpoint, chain, net
}

// Agent uses HTTPs to resolve domain names
// Allow 8.8.8.8 for dns
err = ipt.Append(filterTable, chain, direction, netInterface, protocol, tcp,
destination, dnsServerIP, target, accept)

if err != nil {
return errors.Wrap(err, "failed to add rule")
}

// Allow 8.8.4.4 for dns
err = ipt.Append(filterTable, chain, direction, netInterface, protocol, tcp,
destination, dnsServerIP2, target, accept)
for _, dnsServer := range dnsServers {
err = ipt.Append(filterTable, chain, direction, netInterface, protocol, tcp,
destination, dnsServer, target, accept)

if err != nil {
return errors.Wrap(err, "failed to add rule")
if err != nil {
return errors.Wrapf(err, "failed to add rule for DNS server %s", dnsServer)
}
}

// Allow AzureIPAddress
Expand Down

0 comments on commit 2a612fe

Please sign in to comment.