Skip to content

Commit

Permalink
feat(ssl): Generating a self-signed CA certificate and installing it …
Browse files Browse the repository at this point in the history
…in Firefox and/or Chrome/Chromium browsers

Refs: DL-T-97
  • Loading branch information
varrcan committed Jun 15, 2023
1 parent 8426f8f commit 2f74729
Show file tree
Hide file tree
Showing 19 changed files with 834 additions and 38 deletions.
5 changes: 3 additions & 2 deletions .github/scripts/packages/preinstall.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#!/bin/bash
set -e

mkdir -p "/etc/dl/config-files"
mkdir -p "/usr/share/zsh/vendor-completions"
install -m 0755 -d /etc/apt/keyrings
install -m 0755 -d /etc/dl/config-files
install -m 0755 -d /usr/share/zsh/vendor-completions
1 change: 1 addition & 0 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ nfpms:
- containerd.io
- docker-buildx-plugin
- docker-compose-plugin
- libnss3-tools
scripts:
preinstall: "./.github/scripts/packages/preinstall.sh"
contents:
Expand Down
30 changes: 30 additions & 0 deletions command/cert.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package command

import (
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

var certCmd = &cobra.Command{
Use: "cert",
Short: "CA certificate management",
Long: `Generating and (un)installing a root certificate in Firefox and/or Chrome/Chromium browsers.`,
ValidArgs: []string{"install", "i", "uninstall", "delete"},
}

func certCommand() *cobra.Command {
certCmd.AddCommand(
installCertCommand(),
uninstallCertCommand(),
)
return certCmd
}

func storeCertConfig(status bool) {
viper.Set("ca", status)
err := viper.WriteConfig()
if err != nil {
pterm.FgRed.Println(err)
}
}
92 changes: 92 additions & 0 deletions command/cert_install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package command

import (
"context"
"os"
"path/filepath"

"github.com/local-deploy/dl/helper"
"github.com/local-deploy/dl/utils/cert"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
)

var reinstallCert bool

func installCertCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "install",
Aliases: []string{"i"},
Short: "Installing CA certificate",
Long: `Generating a self-signed CA certificate and installing it in Firefox and/or Chrome/Chromium browsers.`,
Run: func(cmd *cobra.Command, args []string) {
installCertRun()
},
ValidArgs: []string{"--reinstall", "-r"},
}
cmd.Flags().BoolVarP(&reinstallCert, "reinstall", "r", false, "Reinstall certificate")
return cmd
}

func installCertRun() {
certutilPath, err := helper.CertutilPath()
if err != nil {
pterm.FgRed.Printfln("Error: %s", err)
return
}

err = helper.CreateDirectory(filepath.Join(helper.CertDir(), "conf"))
if err != nil {
pterm.FgRed.Printfln("Error: %s \n", err)
os.Exit(1)
}

c := &cert.Cert{
CertutilPath: certutilPath,
CaFileName: cert.CaRootName,
CaFileKeyName: cert.CaRootKeyName,
CaPath: helper.CertDir(),
}

if reinstallCert {
err = c.LoadCA()
if err != nil {
pterm.FgRed.Printfln("Error: %s", err)
return
}
c.Uninstall()
helper.RemoveFilesInPath(filepath.Join(helper.CertDir(), "conf"))
helper.RemoveFilesInPath(helper.CertDir())
}

_, err = os.Stat(filepath.Join(helper.CertDir(), cert.CaRootName))
if err != nil {
err := c.CreateCA()
if err != nil {
pterm.FgRed.Printfln("Error: %s", err)
return
}
}

err = c.LoadCA()
if err != nil {
pterm.FgRed.Printfln("Error: %s", err)
return
}

if c.Check() {
pterm.FgGreen.Println("The local CA is already installed in the browsers trust store!")
} else if c.Install() {
storeCertConfig(true)
pterm.FgGreen.Println("The local CA is now installed in the browsers trust store (requires browser restart)!")

// Restart traefik
source = "traefik"
ctx := context.Background()
_ = downServiceRun(ctx)
err = upServiceRun(ctx)
if err != nil {
pterm.FgYellow.Println("Restart services for changes to take effect: dl service recreate")
}
}
}
58 changes: 58 additions & 0 deletions command/cert_uninstall.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package command

import (
"path/filepath"

"github.com/local-deploy/dl/helper"
"github.com/local-deploy/dl/utils/cert"
"github.com/pterm/pterm"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func uninstallCertCommand() *cobra.Command {
cmd := &cobra.Command{
Use: "uninstall",
Short: "Removing CA certificate",
Long: `Removing a self-signed CA certificate from the Firefox and/or Chrome/Chromium browsers.`,
Run: func(cmd *cobra.Command, args []string) {
uninstallCertRun()
},
}
return cmd
}

func uninstallCertRun() {
certutilPath, err := helper.CertutilPath()
if err != nil {
pterm.FgRed.Printfln("Error: %s", err)
return
}

c := &cert.Cert{
CertutilPath: certutilPath,
CaFileName: cert.CaRootName,
CaFileKeyName: cert.CaRootKeyName,
CaPath: helper.CertDir(),
}

err = c.LoadCA()
if err != nil {
pterm.FgRed.Printfln("Error: %s", err)
return
}

ca := viper.GetBool("ca")
if !ca {
pterm.FgYellow.Println("The local CA is not installed")
return
}

c.Uninstall()

helper.RemoveFilesInPath(filepath.Join(helper.CertDir(), "conf"))
helper.RemoveFilesInPath(helper.CertDir())

storeCertConfig(false)
pterm.FgYellow.Println("The local CA is now uninstalled from the browsers trust store!")
}
7 changes: 7 additions & 0 deletions command/down.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,14 @@ import (
"fmt"
"os"
"os/exec"
"path/filepath"

"github.com/local-deploy/dl/helper"
"github.com/local-deploy/dl/project"
"github.com/pterm/pterm"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func downCommand() *cobra.Command {
Expand All @@ -30,6 +32,11 @@ func downRun() {

pterm.FgGreen.Printfln("Stopping project...")

if viper.GetBool("ca") {
_ = helper.RemoveDirectory(filepath.Join(helper.CertDir(), "conf", project.Env.GetString("NETWORK_NAME")+".yaml"))
_ = helper.RemoveDirectory(filepath.Join(helper.CertDir(), project.Env.GetString("NETWORK_NAME")))
}

bin, option := helper.GetCompose()
Args := []string{bin}
preArgs := []string{"-p", project.Env.GetString("NETWORK_NAME"), "down"}
Expand Down
4 changes: 2 additions & 2 deletions command/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ func showEnvMenu() {

func printEnvConfig() {
templateDir := helper.TemplateDir()
src := filepath.Join(templateDir, "/config-files/.env.example")
src := filepath.Join(templateDir, ".env.example")

file, err := os.Open(src)
if err != nil {
Expand Down Expand Up @@ -96,7 +96,7 @@ func copyEnv() bool {
if project.IsEnvExampleFileExists() {
src = filepath.Join(currentDir, ".env.example")
} else {
src = filepath.Join(templateDir, "/config-files/.env.example")
src = filepath.Join(templateDir, ".env.example")
}

dest := filepath.Join(currentDir, ".env")
Expand Down
2 changes: 1 addition & 1 deletion command/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func runPs() error {

cli, err := docker.NewClient()
if err != nil {
pterm.Fatal.Printfln("Failed to connect to socket")
pterm.FgRed.Printfln("Failed to connect to socket")
return err
}

Expand Down
1 change: 1 addition & 0 deletions command/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ func Execute() {
rootCmd.AddCommand(
envCommand(),
psCommand(),
certCommand(),
bashCommand(),
execCommand(),
completionCommand(),
Expand Down
10 changes: 10 additions & 0 deletions command/service.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package command

import (
"path/filepath"

"github.com/docker/docker/api/types/mount"
"github.com/docker/docker/client"
"github.com/docker/docker/integration/network"
"github.com/local-deploy/dl/helper"
"github.com/local-deploy/dl/utils/docker"
"github.com/spf13/cobra"
)
Expand Down Expand Up @@ -40,6 +43,7 @@ func getServicesContainer() []docker.Container {
"--providers.docker",
"--providers.docker.network=dl_default",
"--providers.docker.exposedbydefault=false",
"--providers.file.directory=/certs/conf",
"--entrypoints.web.address=:80",
"--entrypoints.websecure.address=:443",
"--entrypoints.ws.address=:8081",
Expand All @@ -64,6 +68,12 @@ func getServicesContainer() []docker.Container {
Target: "/var/run/docker.sock",
ReadOnly: true,
},
{
Type: mount.TypeBind,
Source: filepath.Join(helper.ConfigDir(), "certs"),
Target: "/certs",
ReadOnly: true,
},
},
Env: nil,
Network: servicesNetworkName,
Expand Down
20 changes: 16 additions & 4 deletions command/up.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/pterm/pterm"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)

func upCommand() *cobra.Command {
Expand Down Expand Up @@ -46,12 +47,12 @@ func upRun() {
ctx := context.Background()
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
pterm.Fatal.Printfln("Failed to connect to socket")
pterm.FgRed.Printfln("Failed to connect to socket")
return
}

containerFilter := filters.NewArgs(filters.Arg("name", "traefik"))
traefikExists, err := cli.ContainerList(ctx, types.ContainerListOptions{Filters: containerFilter})
traefikExists, _ := cli.ContainerList(ctx, types.ContainerListOptions{Filters: containerFilter})

if len(traefikExists) == 0 {
err := startLocalServices()
Expand All @@ -63,6 +64,11 @@ func upRun() {

pterm.FgGreen.Printfln("Starting project...")

if viper.GetBool("ca") {
pterm.FgGreen.Printfln("SSL certificate enabled")
project.CreateCert()
}

bin, option := helper.GetCompose()
Args := []string{bin}
preArgs := []string{"-p", project.Env.GetString("NETWORK_NAME"), "up", "-d"}
Expand Down Expand Up @@ -113,18 +119,24 @@ func startLocalServices() error {
return nil
}
//goland:noinspection GoErrorStringFormat
return errors.New("Start local services first: dl service up")
return errors.New("start local services first: dl service up")
}

// showProjectInfo Display project links
func showProjectInfo() {
l := project.Env.GetString("LOCAL_DOMAIN")
n := project.Env.GetString("NIP_DOMAIN")

schema := "http"

if viper.GetBool("ca") {
schema = "https"
}

pterm.FgCyan.Println()
panels := pterm.Panels{
{{Data: pterm.FgYellow.Sprintf("nip.io\nlocal")},
{Data: pterm.FgYellow.Sprintf("http://%s/\nhttp://%s/", n, l)}},
{Data: pterm.FgYellow.Sprintf(schema+"://%s/\n"+schema+"://%s/", n, l)}},
}

_ = pterm.DefaultPanel.WithPanels(panels).WithPadding(5).Render()
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
golang.org/x/crypto v0.4.0
golang.org/x/sync v0.1.0
golang.org/x/text v0.7.0
gopkg.in/yaml.v3 v3.0.1
)

require (
Expand Down Expand Up @@ -99,6 +100,5 @@ require (
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.4.0 // indirect
)
Loading

0 comments on commit 2f74729

Please sign in to comment.