Skip to content

Commit dd8bc8d

Browse files
authored
Forward proxy: add /etc/hosts file entry to point to localhost (#14)
* initial commit * add check to avoid overwriting
1 parent e8f78f2 commit dd8bc8d

File tree

1 file changed

+68
-0
lines changed

1 file changed

+68
-0
lines changed

forward/forwardproxy.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"net"
2525
"net/http"
2626
"net/textproto"
27+
"os"
2728
"strconv"
2829
"strings"
2930
"sync"
@@ -39,6 +40,14 @@ import (
3940
"github.com/scionproto-contrib/http-proxy/forward/utils"
4041
)
4142

43+
const (
44+
hostsFile = "/etc/hosts"
45+
hostsComment = " # Line added by the SCION HTTP Forward Proxy"
46+
hostName = "forward-proxy.scion"
47+
// TODO: make the address injectable via configuration
48+
hostsEntry = "127.0.0.1\t" + hostName
49+
)
50+
4251
// ResolveHandler defines an interface for handling HTTP requests related to
4352
// host resolution and redirection. Implementations of this interface should
4453
// provide mechanisms to handle redirection back or errors, as well as host
@@ -89,11 +98,18 @@ func (cp *CoreProxy) Initialize() error {
8998
}
9099
cp.metricsHandler = panpolicy.NewMetricsHandler(cp.policyManager, cp.logger.With(zap.String("component", "metrics-handler")))
91100
cp.resolver = resolver.NewPANResolver(cp.logger.With(zap.String("component", "resolver")), cp.resolveTimeout)
101+
102+
if err := cp.addHostsEntry(); err != nil {
103+
cp.logger.Warn("Failed to add entry to /etc/hosts file", zap.Error(err))
104+
}
92105
return nil
93106
}
94107

95108
// Cleanup cleans up the core proxy logic.
96109
func (cp *CoreProxy) Cleanup() error {
110+
if err := cp.removeHostsEntry(); err != nil {
111+
cp.logger.Warn("Failed to remove entry from /etc/hosts file", zap.Error(err))
112+
}
97113
return cp.policyManager.Stop()
98114
}
99115

@@ -158,6 +174,58 @@ func (cp *CoreProxy) HandleTunnelRequest(w http.ResponseWriter, r *http.Request)
158174
return cp.forwardRequest(w, r, dialer)
159175
}
160176

177+
func (cp *CoreProxy) addHostsEntry() error {
178+
content, err := os.ReadFile(hostsFile)
179+
if err != nil {
180+
return fmt.Errorf("failed to read hosts file: %w", err)
181+
}
182+
183+
lines := strings.Split(string(content), "\n")
184+
for _, line := range lines {
185+
if !strings.HasPrefix(strings.TrimSpace(line), "#") && strings.Contains(line, hostName) {
186+
cp.logger.Debug("Entry for host name already exists", zap.String("entry", line))
187+
return nil
188+
}
189+
}
190+
191+
file, err := os.OpenFile(hostsFile, os.O_APPEND|os.O_WRONLY, 0644)
192+
if err != nil {
193+
return fmt.Errorf("failed to open hosts file for writing: %w", err)
194+
}
195+
defer file.Close()
196+
197+
entry := hostsComment + "\n" + hostsEntry + "\n"
198+
if _, err := file.WriteString(entry); err != nil {
199+
return fmt.Errorf("failed to write to hosts file: %w", err)
200+
}
201+
202+
cp.logger.Info("Added entry to hosts file", zap.String("entry", hostsEntry))
203+
return nil
204+
}
205+
206+
func (cp *CoreProxy) removeHostsEntry() error {
207+
content, err := os.ReadFile(hostsFile)
208+
if err != nil {
209+
return fmt.Errorf("failed to read hosts file: %w", err)
210+
}
211+
212+
var newLines []string
213+
lines := strings.Split(string(content), "\n")
214+
for _, line := range lines {
215+
if !strings.Contains(line, hostsEntry) && !strings.Contains(line, hostsComment) {
216+
newLines = append(newLines, line)
217+
}
218+
}
219+
220+
err = os.WriteFile(hostsFile, []byte(strings.Join(newLines, "\n")), 0644)
221+
if err != nil {
222+
return fmt.Errorf("failed to write to hosts file: %w", err)
223+
}
224+
225+
cp.logger.Info("Removed entry from hosts file", zap.String("entry", hostsEntry))
226+
return nil
227+
}
228+
161229
func (cp *CoreProxy) parseCookieFromProxyAuth(w http.ResponseWriter, r *http.Request) error {
162230
// the path policy cookie is passed in the proxy-authorization header as the cookie
163231
username, cookie, err := proxyBasicAuth(r)

0 commit comments

Comments
 (0)