Compare commits
No commits in common. "8cf7b02e1b9d8a1528bdfe4ce16f9eee30da5a30" and "3e8d9675955f7d7bea76771079fc09a349b2e2e1" have entirely different histories.
8cf7b02e1b
...
3e8d967595
|
@ -2,10 +2,10 @@
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Start by installing Go and Git. On Debian, run:
|
Start by installing Go, Git, GCC and glibc (the program is written in Go, but a tiny bit of C code is used to drop privileges when running as root). On Debian, run:
|
||||||
|
|
||||||
```
|
```
|
||||||
apt install golang git
|
apt install golang git gcc libc6-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
Then fetch and build the program:
|
Then fetch and build the program:
|
||||||
|
@ -66,7 +66,7 @@ systemctl start client-hello-mirror.service
|
||||||
|
|
||||||
## Drop root
|
## Drop root
|
||||||
|
|
||||||
A standard web-facing setup involves using a CA-signed certificate and binding to privileged port 443. For security reasons, the program will drop root privileges imediately after loading the certificate and binding to the specified port. Use the `-u` option to select a user to switch to. If you really want to run as root, set `-u root` (not recommended).
|
A standard web-facing setup involves using a CA-signed certificate and binding to privileged port 443. For security reasons, the program will drop root privileges imediately after loading the certificate and binding to the specified port. Use the `-u` option to select a user to switch to; the default is `www-data`.
|
||||||
|
|
||||||
## Redirect http:// to https://
|
## Redirect http:// to https://
|
||||||
|
|
||||||
|
|
5
go.mod
5
go.mod
|
@ -1,5 +0,0 @@
|
||||||
module tildegit.org/nervuri/client-hello-mirror
|
|
||||||
|
|
||||||
go 1.19
|
|
||||||
|
|
||||||
require golang.org/x/crypto v0.5.0
|
|
2
go.sum
2
go.sum
|
@ -1,2 +0,0 @@
|
||||||
golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
|
|
||||||
golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
|
|
70
server.go
70
server.go
|
@ -14,7 +14,6 @@ import (
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
|
||||||
"os/user"
|
"os/user"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -22,6 +21,12 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
import (
|
||||||
|
//#include <unistd.h>
|
||||||
|
//#include <errno.h>
|
||||||
|
"C"
|
||||||
|
)
|
||||||
|
|
||||||
type tlsConnectionInfo struct {
|
type tlsConnectionInfo struct {
|
||||||
TlsVersion uint16 `json:"tls_version"`
|
TlsVersion uint16 `json:"tls_version"`
|
||||||
CipherSuite uint16 `json:"cipher_suite"`
|
CipherSuite uint16 `json:"cipher_suite"`
|
||||||
|
@ -124,24 +129,21 @@ _____________________
|
||||||
=> https://tildegit.org/nervuri/client-hello-mirror Source (contributions welcome)
|
=> https://tildegit.org/nervuri/client-hello-mirror Source (contributions welcome)
|
||||||
=> https://www.gnu.org/licenses/agpl-3.0.en.html License: AGPL-3.0-or-later`
|
=> https://www.gnu.org/licenses/agpl-3.0.en.html License: AGPL-3.0-or-later`
|
||||||
|
|
||||||
// Copy the Client Hello message before starting the TLS handshake.
|
|
||||||
func peek(conn net.Conn, tlsConfig *tls.Config) {
|
func peek(conn net.Conn, tlsConfig *tls.Config) {
|
||||||
|
// Copy the Client Hello before starting the TLS handshake.
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
var buf bytes.Buffer
|
var buf bytes.Buffer
|
||||||
// Copy TLS record header.
|
_, err := io.CopyN(&buf, conn, 5) // TLS record header
|
||||||
_, err := io.CopyN(&buf, conn, 5)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Check if this is a TLS handshake record.
|
|
||||||
if buf.Bytes()[0] != 0x16 {
|
if buf.Bytes()[0] != 0x16 {
|
||||||
|
// Not a Client Hello message.
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Extract handshake message length.
|
length := binary.BigEndian.Uint16(buf.Bytes()[3:5])
|
||||||
handshakeMessageLength := binary.BigEndian.Uint16(buf.Bytes()[3:5])
|
_, err = io.CopyN(&buf, conn, int64(length))
|
||||||
// Copy handshake message (should be a Client Hello).
|
|
||||||
_, err = io.CopyN(&buf, conn, int64(handshakeMessageLength))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
log.Println(err)
|
||||||
return
|
return
|
||||||
|
@ -275,11 +277,11 @@ func main() {
|
||||||
// Parse arguments
|
// Parse arguments
|
||||||
flag.StringVar(&certFile, "c", "", "path to certificate file")
|
flag.StringVar(&certFile, "c", "", "path to certificate file")
|
||||||
flag.StringVar(&keyFile, "k", "", "path to private key file")
|
flag.StringVar(&keyFile, "k", "", "path to private key file")
|
||||||
flag.StringVar(&userToSwitchTo, "u", "", "user to switch to, if running as root")
|
flag.StringVar(&userToSwitchTo, "u", "www-data", "user to switch to, if running as root")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
hostAndPort = flag.Arg(0)
|
hostAndPort = flag.Arg(0)
|
||||||
if certFile == "" || keyFile == "" || hostAndPort == "" {
|
if certFile == "" || keyFile == "" || hostAndPort == "" {
|
||||||
fmt.Println("usage: client-hello-mirror -c cert.pem -k key.pem [-u user] host:port")
|
fmt.Println("usage: client-hello-mirror -c cert.pem -k key.pem host:port")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -305,48 +307,36 @@ func main() {
|
||||||
defer ln.Close()
|
defer ln.Close()
|
||||||
|
|
||||||
// Drop root
|
// Drop root
|
||||||
if syscall.Getuid() == 0 { // running as root
|
if syscall.Getuid() == 0 {
|
||||||
if userToSwitchTo == "" {
|
if userToSwitchTo == "" {
|
||||||
fmt.Println("When running as root, use the -u option to switch to an unprivileged user.")
|
fmt.Println("Running as root. Please specify an unprivileged user to switch to, using the -u flag")
|
||||||
os.Exit(1)
|
return
|
||||||
}
|
}
|
||||||
// Get user and group IDs for the user we want to switch to.
|
|
||||||
userInfo, err := user.Lookup(userToSwitchTo)
|
userInfo, err := user.Lookup(userToSwitchTo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
if userToSwitchTo == "www-data" {
|
||||||
|
fmt.Println("Running as root. Please specify an unprivileged user to switch to, using the -u flag")
|
||||||
|
}
|
||||||
|
return
|
||||||
}
|
}
|
||||||
// Convert group id and user id from string to int.
|
uid, err := strconv.ParseInt(userInfo.Uid, 10, 32)
|
||||||
gid, err := strconv.Atoi(userInfo.Gid)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
return
|
||||||
}
|
}
|
||||||
uid, err := strconv.Atoi(userInfo.Uid)
|
gid, err := strconv.ParseInt(userInfo.Gid, 10, 32)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
return
|
||||||
}
|
}
|
||||||
if uid == 0 {
|
cerr, errno := C.setgid(C.__gid_t(gid))
|
||||||
fmt.Println("WARNING: running as root is not recommended!")
|
if cerr != 0 {
|
||||||
|
log.Fatalln("Unable to set GID due to error:", errno)
|
||||||
}
|
}
|
||||||
// Unset supplementary group IDs.
|
cerr, errno = C.setuid(C.__uid_t(uid))
|
||||||
err = syscall.Setgroups([]int{})
|
if cerr != 0 {
|
||||||
if err != nil {
|
log.Fatalln("Unable to set UID due to error:", errno)
|
||||||
fmt.Println("Failed to unset supplementary group IDs: " + err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// Set group ID.
|
|
||||||
err = syscall.Setgid(gid)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to set group ID: " + err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// Set user ID.
|
|
||||||
err = syscall.Setuid(uid)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to set user ID: " + err.Error())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue