Skip to content
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
18 changes: 18 additions & 0 deletions docs/legacy/world-pkg-openvpn/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# world pkg/openvpn — preserved reference implementation

Source: `github.com/bborbe/world` (repo deleted 2026-07-21 after full migration
to bw), final commit `929ac5a`. Preserved verbatim before deletion.

This is the Go mini-CA + OpenVPN provisioning code that originally built and
managed the VPN now owned by `bundles/openvpn` + `bundles/openvpn-client`:

- `server-config.go` — CA/server cert generation (RSA 4096, PKCS1), dhparam,
ta.key; the CA private key stayed on the operator laptop (`~/.openvpn/`)
- `client-config.go` — client cert signing + the client.conf template
- `server.go` / `client-remote.go` / `client-local.go` — deployment logic
(superseded by the bw bundles)
- `openvpn.go` — types (IRoutes/ClientIPs → now node metadata `clients` map)

Kept for the 2030 cert renewal and as the authoritative answer to "how were
these certs generated". Renewal gotchas are documented in
`bundles/openvpn/README.md`. Not compiled, not imported — reference only.
261 changes: 261 additions & 0 deletions docs/legacy/world-pkg-openvpn/client-config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
// Copyright (c) 2019 Benjamin Borbe All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package openvpn

import (
"context"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"fmt"
"math/big"
"net"
"os"
"os/user"
"path"
"path/filepath"
"time"

"github.com/bborbe/world/pkg/content"
"github.com/bborbe/world/pkg/file"
"github.com/bborbe/world/pkg/template"
"github.com/bborbe/world/pkg/validation"
)

type ClientConfig struct {
ClientName ClientName
ServerConfig ServerConfig
ServerAddress ServerAddress
Routes Routes
Device Device
}

func (c ClientConfig) Validate(ctx context.Context) error {
return validation.Validate(
ctx,
c.ServerConfig.ServerName,
c.ServerConfig.ServerPort,
c.ClientName,
c.ServerAddress,
c.Device,
)
}

func (c *ClientConfig) ConfigContent() content.HasContent {
return content.Func(func(ctx context.Context) ([]byte, error) {
port, err := c.ServerConfig.ServerPort.Port(ctx)
if err != nil {
return nil, err
}

type Route struct {
Gateway string
Net string
Mask string
}
data := struct {
ServerName string
ServerHost string
ServerPort int
Routes []Route
Device string
}{
ServerName: c.ServerConfig.ServerName.String(),
ServerHost: c.ServerAddress.String(),
ServerPort: port,
Routes: []Route{},
Device: c.Device.String(),
}
for _, route := range c.Routes {
gateway, err := route.Gateway.IP(ctx)
if err != nil {
return nil, err
}
ipnet, err := route.IPNet.IPNet(ctx)
if err != nil {
return nil, err
}
data.Routes = append(data.Routes, Route{
Gateway: gateway.String(),
Net: ipnet.IP.String(),
Mask: net.IP(ipnet.Mask).String(),
})
}
return template.Render(`
#viscosity startonopen true
#viscosity usepeerdns false
#viscosity ipv6 false
#viscosity dns off
#viscosity protocol openvpn
#viscosity autoreconnect true
#viscosity dnssupport true
#viscosity name {{.ServerName}}
#viscosity dhcp false

client
dev {{.Device}}
proto tcp4
remote {{.ServerHost}} {{.ServerPort}}
resolv-retry infinite
nobind
persist-key
persist-tun
ca ca.crt
cert client.crt
key client.key
remote-cert-tls client
tls-auth ta.key 1
cipher AES-256-CBC
# comp-lzo

verb 3

{{range $route := .Routes}}
route {{$route.Net}} {{$route.Mask}} {{$route.Gateway}}
{{ end }}
`, data)
})
}

func (c *ClientConfig) localPath(filename string) file.HasPath {
return file.PathFunc(func(ctx context.Context) (string, error) {
directory, err := c.clientDirectory()
if err != nil {
return "", err
}
return path.Join(directory, filename), nil
})
}

func (c *ClientConfig) clientDirectory() (string, error) {
usr, err := user.Current()
if err != nil {
return "", fmt.Errorf("get homedir failed: %w", err)
}
dir := filepath.Join(usr.HomeDir, ".openvpn", c.ClientName.String())
if err := os.MkdirAll(dir, 0700); err != nil {
return "", err
}
return dir, nil
}

func (c *ClientConfig) ClientKey() content.Func {
return func(ctx context.Context) ([]byte, error) {
caPrivKey, err := rsa.GenerateKey(rand.Reader, 4096)
if err != nil {
return nil, err
}
return pem.EncodeToMemory(&pem.Block{
Type: "RSA PRIVATE KEY",
Bytes: x509.MarshalPKCS1PrivateKey(caPrivKey),
}), nil
}
}

func (c *ClientConfig) ClientCertifcate() *x509.Certificate {
return &x509.Certificate{
SerialNumber: big.NewInt(1658),
Subject: pkix.Name{
CommonName: c.ClientName.String(),
},
NotBefore: time.Now(),
NotAfter: time.Now().AddDate(10, 0, 0),
SubjectKeyId: []byte{1, 2, 3, 4, 6},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth},
KeyUsage: x509.KeyUsageDigitalSignature,
}
}

func (c *ClientConfig) ClientCrt() content.Func {
return func(ctx context.Context) ([]byte, error) {
caPriv, err := readLocal(ctx, c.ServerConfig.LocalPathCAPrivateKey())
if err != nil {
return nil, err
}

caPrivPem, _ := pem.Decode(caPriv)
if caPrivPem.Type != "RSA PRIVATE KEY" {
return nil, fmt.Errorf("invalid type %s", caPrivPem.Type)
}

caPrivKey, err := x509.ParsePKCS1PrivateKey(caPrivPem.Bytes)
if err != nil {
return nil, err
}

certKey, err := readLocal(ctx, c.LocalPathClientKey())
if err != nil {
return nil, err
}

certPrivPem, _ := pem.Decode(certKey)
if certPrivPem.Type != "RSA PRIVATE KEY" {
return nil, fmt.Errorf("invalid type %s", certPrivPem.Type)
}

certPrivKey, err := x509.ParsePKCS1PrivateKey(certPrivPem.Bytes)
if err != nil {
return nil, err
}

certBytes, err := x509.CreateCertificate(
rand.Reader,
c.ClientCertifcate(),
c.ServerConfig.CACertifcate(),
&certPrivKey.PublicKey,
caPrivKey,
)
if err != nil {
return nil, err
}

return pem.EncodeToMemory(&pem.Block{
Type: "CERTIFICATE",
Bytes: certBytes,
}), nil
}
}

func (c *ClientConfig) CaCrt() content.Func {
return func(ctx context.Context) (bytes []byte, err error) {
path, err := c.ServerConfig.LocalPathCaCrt().Path(ctx)
if err != nil {
return nil, err
}
return os.ReadFile(path)
}
}

func (c *ClientConfig) TAKey() content.Func {
return func(ctx context.Context) (bytes []byte, err error) {
path, err := c.ServerConfig.LocalPathTaKey().Path(ctx)
if err != nil {
return nil, err
}
return os.ReadFile(path)
}
}

func (c *ClientConfig) LocalPathTaKey() file.HasPath {
return c.localPath("ta.key")
}

func (c *ClientConfig) LocalPathCaCrt() file.HasPath {
return c.localPath("ca.crt")
}

func (c *ClientConfig) LocalPathClientKey() file.HasPath {
return c.localPath("client.key")
}

func (c *ClientConfig) LocalPathClientCrt() file.HasPath {
return c.localPath("client.crt")
}

func (c *ClientConfig) LocalPathConfig() file.HasPath {
return c.localPath("client.ovpn")
}
88 changes: 88 additions & 0 deletions docs/legacy/world-pkg-openvpn/client-local.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// Copyright (c) 2019 Benjamin Borbe All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package openvpn

import (
"context"

"github.com/bborbe/world/pkg/local"
"github.com/bborbe/world/pkg/network"
"github.com/bborbe/world/pkg/validation"
"github.com/bborbe/world/pkg/world"
)

type LocalClient struct {
ClientName ClientName
ServerName ServerName
ServerAddress ServerAddress
Routes Routes
ServerPort network.Port
Device Device
}

func (l *LocalClient) Validate(ctx context.Context) error {
return validation.Validate(
ctx,
l.ClientName,
l.ServerName,
l.ServerAddress,
l.ServerPort,
l.clientConfig(),
l.Device,
)
}

func (l *LocalClient) Children(ctx context.Context) (world.Configurations, error) {
clientConfig := l.clientConfig()
return world.Configurations{
world.NewConfiguraionBuilder().WithApplier(
&local.FileContent{
Path: clientConfig.LocalPathConfig(),
Content: clientConfig.ConfigContent(),
},
),
world.NewConfiguraionBuilder().WithApplier(
&local.FileContent{
Path: clientConfig.LocalPathCaCrt(),
Content: clientConfig.CaCrt(),
},
),
world.NewConfiguraionBuilder().WithApplier(
&local.FileContent{
Path: clientConfig.LocalPathTaKey(),
Content: clientConfig.TAKey(),
},
),
world.NewConfiguraionBuilder().WithApplier(
&local.FileContent{
Path: clientConfig.LocalPathClientKey(),
Content: clientConfig.ClientKey(),
},
),
world.NewConfiguraionBuilder().WithApplier(
&local.FileContent{
Path: clientConfig.LocalPathClientCrt(),
Content: clientConfig.ClientCrt(),
},
),
}, nil
}

func (l *LocalClient) Applier() (world.Applier, error) {
return nil, nil
}

func (l *LocalClient) clientConfig() ClientConfig {
return ClientConfig{
ClientName: l.ClientName,
ServerAddress: l.ServerAddress,
ServerConfig: ServerConfig{
ServerName: l.ServerName,
ServerPort: l.ServerPort,
},
Routes: l.Routes,
Device: l.Device,
}
}
Loading
Loading