Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Embed Certificate Authorities #41

Merged
merged 9 commits into from
May 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ jobs:
run: |
cd pw-feeder
go mod tidy
- name: "go generate"
run: |
cd pw-feeder
go generate ./...
- name: "go test"
run: |
cd pw-feeder
Expand All @@ -40,6 +44,10 @@ jobs:
run: |
cd pw-feeder
go mod tidy
- name: "go generate"
run: |
cd pw-feeder
go generate ./...
- name: "go build"
run: |
cd pw-feeder
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ jobs:
run: |
cd pw-feeder
go mod tidy
- name: "go generate"
run: |
cd pw-feeder
go generate ./...
- name: "go test"
run: |
cd pw-feeder
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
pw-feeder/pw-feeder
pw-feeder/lib/stunnel/*.pem
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,6 @@ Tunnels BEAST and MLAT data from your client to plane.watch over a TLS tunnel.
* Clone the repo
* Change into the `pw-feeder` directory
* Run `go mod tidy` to download required modules
* Run `go generate ./...` to download required CA certs
* Test: `go test ./...`
* Build & Install: `go build -o /usr/local/bin/pw-feeder ./cmd/pw-feeder`
3 changes: 2 additions & 1 deletion pw-feeder/cmd/pw-feeder/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ import (
var (
app = &cli.App{
Name: "pw-feeder",
Usage: "feed ADS-B data to plane.watch",
Description: `Plane Watch Feeder Client`,
Version: "0.0.2",
Version: "0.0.3",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "apikey",
Expand Down
27 changes: 27 additions & 0 deletions pw-feeder/lib/stunnel/cacerts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package stunnel

import (
"embed"
)

// Let's Encrypt CAs

//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/isrgrootx1.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/isrg-root-x2.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/e5.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/e6.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/e7.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/e8.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/e9.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/r10.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/r11.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/r12.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/r13.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/2024/r14.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/lets-encrypt-e1.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/lets-encrypt-e2.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/lets-encrypt-r3.pem
//go:generate curl --progress-bar -w %{filename_effective}\n -O https://letsencrypt.org/certs/lets-encrypt-r4.pem

//go:embed *.pem
var caCertPEMs embed.FS
69 changes: 63 additions & 6 deletions pw-feeder/lib/stunnel/stunnel.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,63 @@ package stunnel
import (
"crypto/tls"
"crypto/x509"
"encoding/pem"
"net"
"strings"
"time"

"github.com/rs/zerolog/log"
)

func addEmbeddedCertsToCertPool(scp *x509.CertPool) error {
// load embedded Let's Encrypt CAs
// get list of files in caCertPEMs embed.FS
pemFiles, err := caCertPEMs.ReadDir(".")
if err != nil {
return err
}

// for each file...
for _, pemFile := range pemFiles {

func() {

log := log.With().Str("cafile", pemFile.Name()).Logger()

// open file
f, err := caCertPEMs.Open(pemFile.Name())
if err != nil {
log.Err(err).Msg("could not open embedded CA cert")
}
defer f.Close()

// get file stat (for size)
s, err := f.Stat()
if err != nil {
log.Err(err).Msg("could not stat embedded CA cert")
}

// read bytes from file
b := make([]byte, s.Size())
n, err := f.Read(b)
if err != nil {
log.Err(err).Msg("could not read embedded CA cert")
}

// parse cert
p, _ := pem.Decode(b[:n])
c, err := x509.ParseCertificate(p.Bytes)
if err != nil {
log.Err(err).Msg("could not parse embedded CA cert")
}

// add cert to system cert pool
scp.AddCert(c)
}()
}
return nil
}

func StunnelConnect(name, addr, sni string) (c *tls.Conn, err error) {

log := log.With().Str("name", name).Str("addr", addr).Logger()
Expand Down Expand Up @@ -40,12 +90,16 @@ func StunnelConnect(name, addr, sni string) (c *tls.Conn, err error) {
return err
}

// load root CAs
// load system cert pool CAs
scp, err := x509.SystemCertPool()
if err != nil {
log.Err(err).Caller().Msg("could not use system cert pool")
return err
}
err = addEmbeddedCertsToCertPool(scp)
if err != nil {
return err
}

// TODO: fix this
// verify server cert
Expand All @@ -55,7 +109,6 @@ func StunnelConnect(name, addr, sni string) (c *tls.Conn, err error) {
vo.DNSName = remoteHost
_, err = cert.Verify(vo)
if err != nil {
log.Err(err).Caller().Msg("could not verify server cert")
return err
}
}
Expand All @@ -66,7 +119,11 @@ func StunnelConnect(name, addr, sni string) (c *tls.Conn, err error) {
// load root CAs
scp, err := x509.SystemCertPool()
if err != nil {
log.Err(err).Caller().Msg("could not use system cert pool")
// log.Err(err).Caller().Msg("could not use system cert pool")
return c, err
}
err = addEmbeddedCertsToCertPool(scp)
if err != nil {
return c, err
}

Expand All @@ -85,18 +142,18 @@ func StunnelConnect(name, addr, sni string) (c *tls.Conn, err error) {
// dial remote
c, err = tls.DialWithDialer(&d, "tcp", addr, &tlsConfig)
if err != nil {
log.Err(err).Caller().Msg("could not connect")
// log.Err(err).Caller().Msg("could not connect")
return c, err
}

// perform handshake
err = c.Handshake()
if err != nil {
log.Err(err).Caller().Msg("handshake error")
// log.Err(err).Caller().Msg("handshake error")
return c, err
}

log.Debug().Msg("endpoint connected")
// log.Debug().Msg("endpoint connected")
return c, err

}
39 changes: 39 additions & 0 deletions pw-feeder/lib/stunnel/stunnel_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,45 @@ func init() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.UnixDate})
}

func TestCACerts(t *testing.T) {

// get list of files in caCertPEMs embed.FS
pemFiles, err := caCertPEMs.ReadDir(".")
require.NoError(t, err)

// for each file...
for _, pemFile := range pemFiles {
t.Run(pemFile.Name(), func(t *testing.T) {

// open file
f, err := caCertPEMs.Open(pemFile.Name())
require.NoError(t, err)
defer f.Close()

// get file stat (for size)
s, err := f.Stat()
require.NoError(t, err)

// read bytes from file
b := make([]byte, s.Size())
n, err := f.Read(b)
require.NoError(t, err)

// parse cert
p, _ := pem.Decode(b[:n])
c, err := x509.ParseCertificate(p.Bytes)
require.NoError(t, err)

// check validity
assert.True(t, c.NotBefore.Before(time.Now()), "Certificate not yet valid")
assert.False(t, c.NotAfter.Before(time.Now()), "Certificate expired")

// check if certs are due to expire within 90 days
assert.True(t, c.NotAfter.After(time.Now().Add(time.Hour*24*90)), "Certificate expires within 90 days")
})
}
}

func GenerateSelfSignedTLSCertAndKey(keyFile, certFile *os.File) error {
// Thanks to: https://go.dev/src/crypto/tls/generate_cert.go

Expand Down
Loading