Skip to content

Commit

Permalink
Embed Certificate Authorities (#41)
Browse files Browse the repository at this point in the history
  • Loading branch information
mikenye authored May 10, 2024
1 parent 0b43366 commit 2a4f343
Show file tree
Hide file tree
Showing 8 changed files with 145 additions and 7 deletions.
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

0 comments on commit 2a4f343

Please sign in to comment.