Using MITM to get around old TLS for APT

Are you like me and have to wrangle old systems with outdated TLS that no internet server wants to talk to anymore? Seems like most services no longer support the TLS version included in say, Debian 6. But you still need to serve updated business logic to that dinosaur from a service like PackageCloud.

You can do this with the magic of man-in-the-middle attacks.

First we need a server to run our MITM proxy on. Lets call it 192.168.1.200. This server should have new enough TLS that other internet servers want to talk to it.

Coding the proxy

Now we need a MITM proxy, that can terminate the TLS connections on. We can use the reverse proxy in the golang stdlib to help us do this:

rp := &httputil.ReverseProxy{
	Director: func(req *http.Request) {
		// turn the incoming traffic from our dinosaur from http to https traffic
		req.URL.Scheme = "https"
		log.Println("Processing request for", req.URL)
	},
	Transport: &http.Transport{
		TLSClientConfig: &tls.Config{
			// Use a TLS session cache to minimize TLS connection establishment
			// Requires Go 1.3+
			ClientSessionCache: tls.NewLRUClientSessionCache(10000),
		},
	},
	ModifyResponse: modifier,
}

So we’re converting simply switching the scheme for the outgoing traffic. The request coming back is a bit trickier but the modifier function shows how to do that:

func modifier(r *http.Response) error {
	// change any redirects that go to https, to http so the client
	// will come back via this proxy
	redirect := r.Header.Get("Location")

	if redirect != "" {
		redirect = strings.Replace(redirect, "https", "http", 1)
		r.Header.Set("Location", redirect)
		log.Println("Downgrading HTTPS redirect to HTTP", redirect)
	}

	// delete these headers cos they break stuff
	r.Header.Del("Content-Security-Policy")
	r.Header.Del("Content-Security-Policy-Report-Only")

	return nil
}

With PackageCloud the package URL is redirected to the real file location in AWS S3. So we need to make we modify any incoming redirects (in the Location header) to an HTTP scheme so that apt will go through the proxy for everything (this will be shown in the apt config).

Now simply make a server and tell it to listen:

server := &http.Server{
	Addr:         "192.168.1.200:5000",
	Handler:      rp,
	ReadTimeout:  10 * time.Hour,
	WriteTimeout: 10 * time.Hour,
}

log.Printf("About to start HTTP proxy at %s", addr)
if err := server.ListenAndServe(); err != nil {
	log.Fatalf("Unable to start HTTP proxy: %s", err)
}

Now just package that code into a binary, perhaps with your own Addr value and run it on your proxy server.

Setting up apt to talk to the proxy

We need to configure apt to send all http traffic to our proxy, unless it’s going to the official repos:

cat <<EOF > /etc/apt/apt.conf.d/00-proxy
Acquire::http::Proxy "http://192.168.1.200:5000/";
Acquire::http::Proxy::archive.debian.org DIRECT;
EOF

Now the last step is to change your PackageCloud sources to use http instead of https:

sed 's/https/http/g' -i /etc/apt/sources.list.d/*

Now you can update your dinosaur of an operating system from newer TLS servers.

2 Likes