mirror of
https://github.com/kittywitch/nixfiles.git
synced 2026-02-09 12:29:19 -08:00
feat: SSH CA
This commit is contained in:
parent
a28e1ce6e2
commit
ccf6a6f704
23 changed files with 678 additions and 431 deletions
205
iac/device.go
Normal file
205
iac/device.go
Normal file
|
|
@ -0,0 +1,205 @@
|
|||
package iac
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
"github.com/pulumi/pulumi-command/sdk/go/command/remote"
|
||||
"github.com/pulumi/pulumi-tailscale/sdk/go/tailscale"
|
||||
"github.com/pulumi/pulumi-tls/sdk/v4/go/tls"
|
||||
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
|
||||
"golang.org/x/crypto/ssh"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Tailnet struct {
|
||||
Devices []Device
|
||||
Zone *Zone
|
||||
}
|
||||
|
||||
func (t *Tailnet) handle(ctx *pulumi.Context, zone *Zone, CAKey *tls.PrivateKey, CACert *tls.SelfSignedCert) (err error) {
|
||||
t.Zone = zone
|
||||
tailnet, err := tailscale.GetDevices(ctx, &tailscale.GetDevicesArgs{}, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, device := range tailnet.Devices {
|
||||
device_ := Device{
|
||||
Addresses: device.Addresses,
|
||||
Id: device.Id,
|
||||
Name: device.Name,
|
||||
Tags: device.Tags,
|
||||
User: device.User,
|
||||
}
|
||||
err = device_.handle(ctx, zone, CAKey, CACert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
t.Devices = append(t.Devices, device_)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
type Device struct {
|
||||
Addresses []string
|
||||
Id string
|
||||
Name string
|
||||
Hostname string
|
||||
Tailskip string
|
||||
Tags []string
|
||||
User string
|
||||
Files []*remote.Command
|
||||
Context *pulumi.Context
|
||||
Records []DNSRecord
|
||||
PrivateKey *tls.PrivateKey
|
||||
TLSCertRequest *tls.CertRequest
|
||||
TLSCert *tls.LocallySignedCert
|
||||
OSHCertificate pulumi.StringOutput
|
||||
}
|
||||
|
||||
func (d *Device) handle(ctx *pulumi.Context, zone *Zone, CAKey *tls.PrivateKey, CACert *tls.SelfSignedCert) (err error) {
|
||||
d.Context = ctx
|
||||
d.Records = make([]DNSRecord, 0, 50)
|
||||
d.Files = make([]*remote.Command, 0, 20)
|
||||
d.Hostname = strings.Split(d.Name, ".")[0]
|
||||
d.Tailskip = fmt.Sprintf("%s.inskip.me", d.Hostname)
|
||||
if d.User != "kat@inskip.me" {
|
||||
return nil
|
||||
}
|
||||
err = d.record(zone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if d.Hostname != "koishi" {
|
||||
return err
|
||||
}
|
||||
err = d.handleTLS(CAKey, CACert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = d.handleOSH(CAKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) handleOSH(CAKey *tls.PrivateKey) (err error) {
|
||||
d.OSHCertificate = CAKey.PrivateKeyOpenssh.ApplyT(func(CAPriv string) pulumi.StringOutput {
|
||||
OSHCertificate_ := d.PrivateKey.PrivateKeyOpenssh.ApplyT(func(UserPriv string) pulumi.String {
|
||||
CARSAPriv, err := PrivateKeyOpenSSHToRSAPrivateKey(CAPriv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
signer, err := ssh.NewSignerFromKey(CARSAPriv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
var cert ssh.Certificate
|
||||
cert.Nonce = make([]byte, 32)
|
||||
cert.CertType = 2
|
||||
UserRSAPriv, err := PrivateKeyOpenSSHToRSAPrivateKey(UserPriv)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cert.Key, err = ssh.NewPublicKey(UserRSAPriv.Public())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
cert.Serial = 0
|
||||
cert.KeyId = d.Tailskip
|
||||
cert.ValidPrincipals = []string{d.Tailskip}
|
||||
cert.ValidAfter = 60
|
||||
threeMonths, err := time.ParseDuration("730h")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
threeMonthsInSeconds := uint64(threeMonths.Seconds())
|
||||
cert.ValidBefore = threeMonthsInSeconds
|
||||
err = cert.SignCert(rand.Reader, signer)
|
||||
return pulumi.String(string(ssh.MarshalAuthorizedKey(&cert)))
|
||||
}).(pulumi.StringOutput)
|
||||
return OSHCertificate_
|
||||
}).(pulumi.StringOutput)
|
||||
file, err := CreatePulumiFile(d.Context, fmt.Sprintf("%s-osh-cert", d.Hostname), d.Tailskip, d.OSHCertificate, []pulumi.Resource{d.PrivateKey, CAKey})
|
||||
d.Files = append(d.Files, file)
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) record(zone *Zone) (err error) {
|
||||
for _, address := range d.Addresses {
|
||||
ip := net.ParseIP(address)
|
||||
kind := A
|
||||
if ip.To4() == nil {
|
||||
kind = AAAA
|
||||
}
|
||||
record := DNSRecord{
|
||||
Name: d.Hostname,
|
||||
Kind: kind,
|
||||
Value: ip.String(),
|
||||
Ttl: 3600,
|
||||
}
|
||||
err = record.handle(d.Context, zone)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Records = append(d.Records, record)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (d *Device) handleTLS(CAKey *tls.PrivateKey, CACert *tls.SelfSignedCert) (err error) {
|
||||
PrivateKeyDepends := []pulumi.Resource{CAKey, CACert}
|
||||
d.PrivateKey, err = tls.NewPrivateKey(d.Context, fmt.Sprintf("%s-key", d.Hostname), &tls.PrivateKeyArgs{
|
||||
Algorithm: pulumi.String("RSA"),
|
||||
RsaBits: pulumi.Int(4096),
|
||||
}, pulumi.DependsOn(PrivateKeyDepends))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
PrivateKeyDepends = append(PrivateKeyDepends, d.PrivateKey)
|
||||
file, err := CreatePulumiFile(d.Context, fmt.Sprintf("%s-pem-pk", d.Hostname), d.Tailskip, d.PrivateKey.PrivateKeyPem, PrivateKeyDepends)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Files = append(d.Files, file)
|
||||
file, err = CreatePulumiFile(d.Context, fmt.Sprintf("%s-osh-pk", d.Hostname), d.Tailskip, d.PrivateKey.PrivateKeyOpenssh, PrivateKeyDepends)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
d.Files = append(d.Files, file)
|
||||
TLSCertRequestDepends := []pulumi.Resource{CAKey, CACert, d.PrivateKey}
|
||||
d.TLSCertRequest, err = tls.NewCertRequest(d.Context, fmt.Sprintf("%s-tls-cr", d.Hostname), &tls.CertRequestArgs{
|
||||
PrivateKeyPem: d.PrivateKey.PrivateKeyPem,
|
||||
DnsNames: goStringArrayToPulumiStringArray([]string{d.Hostname}),
|
||||
IpAddresses: goStringArrayToPulumiStringArray(d.Addresses),
|
||||
Subject: &tls.CertRequestSubjectArgs{
|
||||
CommonName: pulumi.String("inskip.me"),
|
||||
Organization: pulumi.String("Kat Inskip"),
|
||||
},
|
||||
}, pulumi.DependsOn(TLSCertRequestDepends))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
TLSCertDepends := []pulumi.Resource{CAKey, CACert, d.TLSCertRequest, d.PrivateKey}
|
||||
d.TLSCert, err = tls.NewLocallySignedCert(d.Context, fmt.Sprintf("%s-tls-cert", d.Hostname), &tls.LocallySignedCertArgs{
|
||||
AllowedUses: goStringArrayToPulumiStringArray([]string{"digital_signature",
|
||||
"digital_signature",
|
||||
"key_encipherment",
|
||||
"key_agreement",
|
||||
"email_protection",
|
||||
}),
|
||||
CaPrivateKeyPem: CAKey.PrivateKeyPem,
|
||||
CaCertPem: CACert.CertPem,
|
||||
CertRequestPem: d.TLSCertRequest.CertRequestPem,
|
||||
ValidityPeriodHours: pulumi.Int(1440),
|
||||
EarlyRenewalHours: pulumi.Int(168),
|
||||
}, pulumi.DependsOn(TLSCertDepends))
|
||||
file, err = CreatePulumiFile(d.Context, fmt.Sprintf("%s-pem-cert", d.Hostname), d.Tailskip, d.TLSCert.CertPem, TLSCertDepends)
|
||||
d.Files = append(d.Files, file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue