Skip to content

Commit 2a612fe

Browse files
Add fallback DNS (#414)
1 parent de3a03e commit 2a612fe

File tree

5 files changed

+82
-54
lines changed

5 files changed

+82
-54
lines changed

.github/workflows/int.yml

Lines changed: 23 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -12,37 +12,27 @@ jobs:
1212
integration-test:
1313
permissions:
1414
contents: read
15-
runs-on: ubuntu-20.04
15+
runs-on: ubuntu-latest
1616
steps:
17-
- uses: step-security/harden-runner@v2
18-
with:
19-
allowed-endpoints:
20-
api.github.com:443
21-
github.com:443
22-
golang.org:443
23-
int.api.stepsecurity.io:443
24-
objects.githubusercontent.com:443
25-
pipelines.actions.githubusercontent.com:443
26-
proxy.golang.org:443
27-
step-security-agent.s3.us-west-2.amazonaws.com:443
28-
storage.googleapis.com:443
29-
sts.us-west-2.amazonaws.com:443
30-
- name: Checkout
31-
uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5
32-
- name: Set up Go
33-
uses: actions/setup-go@424fc82d43fa5a37540bae62709ddcc23d9520d4
34-
with:
35-
go-version: 1.19
36-
- run: sudo go test -v
37-
- run: go build -ldflags="-s -w" -o ./agent
38-
- name: Configure aws credentials
39-
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
40-
with:
41-
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
42-
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
43-
aws-region: us-west-2
44-
- run: aws s3 cp ./agent s3://step-security-agent/refs/heads/int/agent --acl public-read
45-
- name: Integration test
46-
uses: docker://ghcr.io/step-security/integration-test/int:latest
47-
env:
48-
PAT: ${{ secrets.PAT }}
17+
- uses: step-security/harden-runner@v2
18+
with:
19+
egress-policy: audit
20+
- name: Checkout
21+
uses: actions/checkout@629c2de402a417ea7690ca6ce3f33229e27606a5
22+
- name: Set up Go
23+
uses: actions/setup-go@424fc82d43fa5a37540bae62709ddcc23d9520d4
24+
with:
25+
go-version: 1.19
26+
- run: sudo go test -v
27+
- run: go build -ldflags="-s -w" -o ./agent
28+
- name: Configure aws credentials
29+
uses: aws-actions/configure-aws-credentials@ea7b857d8a33dc2fb4ef5a724500044281b49a5e
30+
with:
31+
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
32+
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
33+
aws-region: us-west-2
34+
- run: aws s3 cp ./agent s3://step-security-agent/refs/heads/int/agent --acl public-read
35+
- name: Integration test
36+
uses: docker://ghcr.io/step-security/integration-test/int:latest
37+
env:
38+
PAT: ${{ secrets.PAT }}

agent_test.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,25 @@ func TestRun(t *testing.T) {
135135
},
136136
t.Log))
137137

138-
httpmock.RegisterResponder("GET", "https://dns.google/resolve", // no query params to match all other requests
138+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=actions-results-receiver-production.githubapp.com.&type=A",
139+
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"}]}`))
140+
141+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=artifactcache.actions.githubusercontent.com.&type=A",
142+
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"}]}`))
143+
144+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=pipelines.actions.githubusercontent.com.&type=A",
145+
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"}]}`))
146+
147+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=token.actions.githubusercontent.com.&type=A",
148+
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"}]}`))
149+
150+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=codeload.github.com.&type=A",
151+
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"}]}`))
152+
153+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=vstsmms.actions.githubusercontent.com.&type=A",
154+
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"}]}`))
155+
156+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=vstoken.actions.githubusercontent.com.&type=A",
139157
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"}]}`))
140158

141159
httpmock.RegisterResponder("GET", "https://apiurl/v1/github/owner/repo/actions/subscription",

dnsproxy.go

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -116,12 +116,18 @@ func (proxy *DNSProxy) isAllowedDomain(domain string) bool {
116116

117117
func (proxy *DNSProxy) ResolveDomain(domain string) (*Answer, error) {
118118
url := fmt.Sprintf("https://dns.google/resolve?name=%s&type=a", domain)
119-
119+
fallbackUrl := fmt.Sprintf("https://cloudflare-dns.com/dns-query?name=%s&type=A", domain)
120120
retryCounter := 0
121121
var httpError error
122122
var resp *http.Response
123123
for retryCounter < 2 {
124-
resp, httpError = proxy.ApiClient.Client.Get(url)
124+
requestUrl := url
125+
if retryCounter == 1 {
126+
requestUrl = fallbackUrl
127+
}
128+
req, _ := http.NewRequest("GET", requestUrl, nil)
129+
req.Header.Add("accept", "application/dns-json")
130+
resp, httpError = proxy.ApiClient.Client.Do(req)
125131
if httpError != nil {
126132
retryCounter++
127133
} else {
@@ -251,15 +257,19 @@ func (proxy *DNSProxy) processTypeA(q *dns.Question, requestMsg *dns.Msg) (*dns.
251257
queryMsg := new(dns.Msg)
252258
requestMsg.CopyTo(queryMsg)
253259
queryMsg.Question = []dns.Question{*q}
260+
domains := map[string]string{
261+
"dns.google.": "8.8.8.8",
262+
"cloudflare-dns.com.": "1.1.1.1",
263+
}
254264

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

258268
if err != nil {
259269
return nil, err
260270
}
261271

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

264274
return &rr, nil
265275
}

dnsproxy_test.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@ func TestDNSProxy_getResponse(t *testing.T) {
2424
auditCache := InitCache(EgressPolicyAudit)
2525
blockCache := InitCache(EgressPolicyBlock)
2626
rrDnsGoogle, _ := dns.NewRR("dns.google. IN A 8.8.8.8")
27+
rrDnsCloudflare, _ := dns.NewRR("cloudflare-dns.com. IN A 1.1.1.1")
2728
rrDnsTest, _ := dns.NewRR("test.com. IN A 67.225.146.248")
2829
rrDnsNotAllowed, _ := dns.NewRR(fmt.Sprintf("notallowed.com. IN A %s", StepSecuritySinkHoleIPAddress))
2930
rrDnsAllowed, _ := dns.NewRR("allowed.com. IN A 67.225.146.248")
3031
rrDnsMcr, _ := dns.NewRR("westus.data.mcr.microsoft.com. IN A 67.225.146.248")
32+
rrDnsFallback, _ := dns.NewRR("testfallback.com. IN A 67.225.146.248")
3133
allowedEndpoints := make(map[string][]Endpoint)
3234
allowedEndpoints["allowed.com."] = append(allowedEndpoints["allowed.com."], Endpoint{domainName: "allowed.com"})
3335
allowedEndpointsTest := make(map[string][]Endpoint)
@@ -51,13 +53,28 @@ func TestDNSProxy_getResponse(t *testing.T) {
5153
httpmock.RegisterResponder("GET", "https://dns.google/resolve?name=notfound.com.&type=a",
5254
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."}`))
5355

56+
httpmock.RegisterResponder("GET", "https://cloudflare-dns.com/dns-query?name=testfallback.com.&type=A",
57+
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"}]}`))
58+
5459
tests := []struct {
5560
name string
5661
fields fields
5762
args args
5863
want *dns.Msg
5964
wantErr bool
6065
}{
66+
{name: "test fallback",
67+
fields: fields{Cache: &auditCache},
68+
args: args{requestMsg: &dns.Msg{Question: []dns.Question{{Name: "testfallback.com.", Qtype: dns.TypeA}}}},
69+
want: &dns.Msg{Answer: []dns.RR{rrDnsFallback}},
70+
wantErr: false,
71+
},
72+
{name: "type A cloudflare-dns.com.",
73+
fields: fields{Cache: &auditCache},
74+
args: args{requestMsg: &dns.Msg{Question: []dns.Question{{Name: "cloudflare-dns.com.", Qtype: dns.TypeA}}}},
75+
want: &dns.Msg{Answer: []dns.RR{rrDnsCloudflare}},
76+
wantErr: false,
77+
},
6178
{name: "type A dns.google",
6279
fields: fields{Cache: &auditCache},
6380
args: args{requestMsg: &dns.Msg{Question: []dns.Question{{Name: "dns.google.", Qtype: dns.TypeA}}}},

firewall.go

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,6 @@ const (
2323
target = "-j"
2424
accept = "ACCEPT"
2525
reject = "REJECT"
26-
dnsServerIP = "8.8.8.8"
27-
dnsServerIP2 = "8.8.4.4"
2826
classAPrivateAddressRange = "10.0.0.0/8"
2927
classBPrivateAddressRange = "172.16.0.0/12"
3028
classCPrivateAddressRange = "192.168.0.0/16"
@@ -58,6 +56,8 @@ func addBlockRulesForGitHubHostedRunner(firewall *Firewall, endpoints []ipAddres
5856
func addBlockRules(firewall *Firewall, endpoints []ipAddressEndpoint, chain, netInterface, direction string) error {
5957
var ipt IPTables
6058
var err error
59+
dnsServers := []string{"8.8.8.8", "8.8.4.4", "1.1.1.1"}
60+
6161
if firewall == nil {
6262

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

9090
// Agent uses HTTPs to resolve domain names
91-
// Allow 8.8.8.8 for dns
92-
err = ipt.Append(filterTable, chain, direction, netInterface, protocol, tcp,
93-
destination, dnsServerIP, target, accept)
94-
95-
if err != nil {
96-
return errors.Wrap(err, "failed to add rule")
97-
}
98-
99-
// Allow 8.8.4.4 for dns
100-
err = ipt.Append(filterTable, chain, direction, netInterface, protocol, tcp,
101-
destination, dnsServerIP2, target, accept)
91+
for _, dnsServer := range dnsServers {
92+
err = ipt.Append(filterTable, chain, direction, netInterface, protocol, tcp,
93+
destination, dnsServer, target, accept)
10294

103-
if err != nil {
104-
return errors.Wrap(err, "failed to add rule")
95+
if err != nil {
96+
return errors.Wrapf(err, "failed to add rule for DNS server %s", dnsServer)
97+
}
10598
}
10699

107100
// Allow AzureIPAddress

0 commit comments

Comments
 (0)