Compare commits
8 Commits
72ffe8456d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| e77a67a3de | |||
| d815b12719 | |||
| 7660cc57fd | |||
| 25526ac993 | |||
| 0bf26d2019 | |||
| b049c6a779 | |||
| 0bf7b63286 | |||
| 89136cae59 |
19
README.md
19
README.md
@@ -8,7 +8,7 @@ I had some trouble during TLS communication between both of my gRPC server and c
|
||||
## TODO:
|
||||
- [ ] Create intermediate authority to sign certificates on behalf CA to add more security. If intermediate is hacked then you can revoke from CA and generate new intermediates keeping CA isolated from beeing hacked.
|
||||
|
||||
- [ ] Complete tests
|
||||
- ~~[x] Complete tests~~
|
||||
|
||||
## Configuration
|
||||
If you are on `dev` environment, like I've been doing, you must create `.env` file similar as `.env.example` in this repo:
|
||||
@@ -59,6 +59,23 @@ Then you can just run
|
||||
go run main.go
|
||||
```
|
||||
|
||||
## tests
|
||||
Just simply run make command and watch coverage results on `cover.html` within `coverage`
|
||||
```shell
|
||||
make test-coverage
|
||||
rm -rf coverage
|
||||
mkdir coverage
|
||||
go test -v -coverprofile coverage/cover.out ./...
|
||||
=== RUN TestCredentialsFromKeyWithPasswd
|
||||
--- PASS: TestCredentialsFromKeyWithPasswd (0.37s)
|
||||
=== RUN TestCredentialsFromKeyWithPasswdError
|
||||
--- PASS: TestCredentialsFromKeyWithPasswdError (0.46s)
|
||||
PASS
|
||||
coverage: 90.9% of statements
|
||||
ok gitea.urkob.com/urko/go-grpc-certificate/pkg/credentials 0.839s coverage: 90.9% of statements
|
||||
go tool cover -html coverage/cover.out -o coverage/cover.html
|
||||
```
|
||||
|
||||
## goreportcard
|
||||
```bash
|
||||
make goreportcard
|
||||
|
||||
51
cmd/main.go
51
cmd/main.go
@@ -27,9 +27,9 @@ var envConfig struct {
|
||||
|
||||
var writer pkgio.WriterIface
|
||||
|
||||
func intEnvConfig(isProd bool) {
|
||||
if !isProd {
|
||||
err := godotenv.Load(util.RootDir() + "/.env")
|
||||
func intEnvConfig(envFilePath string) {
|
||||
if envFilePath != "" {
|
||||
err := godotenv.Load(envFilePath)
|
||||
if err != nil {
|
||||
log.Fatalf("environment variable ENV is empty and an error occurred while loading the .env file: %s\n", err)
|
||||
}
|
||||
@@ -87,32 +87,59 @@ var rootCmd = &cobra.Command{
|
||||
log.Fatalf("rootCA.WithClientCert: %s", err)
|
||||
}
|
||||
|
||||
exportPem("root-ca.pem", rootCA.PEM())
|
||||
exportPem("root-key.pem", rootCA.Key())
|
||||
outputPath, err := exportPem("root-ca.pem", rootCA.PEM())
|
||||
if err != nil {
|
||||
log.Fatalf("exportPem: %s\n", err)
|
||||
}
|
||||
log.Printf("file created successfully: %s\n", outputPath)
|
||||
|
||||
exportPem("client-cert.pem", clientCert.PEM())
|
||||
exportPem("client-key.pem", clientCert.Key())
|
||||
outputPath, err = exportPem("root-key.pem", rootCA.Key())
|
||||
if err != nil {
|
||||
log.Fatalf("exportPem: %s\n", err)
|
||||
}
|
||||
log.Printf("file created successfully: %s\n", outputPath)
|
||||
|
||||
outputPath, err = exportPem("client-cert.pem", clientCert.PEM())
|
||||
if err != nil {
|
||||
log.Fatalf("exportPem: %s\n", err)
|
||||
}
|
||||
log.Printf("file created successfully: %s\n", outputPath)
|
||||
|
||||
outputPath, err = exportPem("client-key.pem", clientCert.Key())
|
||||
if err != nil {
|
||||
log.Fatalf("exportPem: %s\n", err)
|
||||
}
|
||||
log.Printf("file created successfully: %s\n", outputPath)
|
||||
},
|
||||
}
|
||||
|
||||
func getExtKeyUsage(intKeyUsageSlice []int) []x509.ExtKeyUsage {
|
||||
extKeyUsage := make([]x509.ExtKeyUsage, len(intKeyUsageSlice))
|
||||
if intKeyUsageSlice == nil || len(intKeyUsageSlice) <= 0 {
|
||||
return []x509.ExtKeyUsage{}
|
||||
}
|
||||
|
||||
extKeyUsage := make([]x509.ExtKeyUsage, 0, len(intKeyUsageSlice))
|
||||
for _, v := range intKeyUsageSlice {
|
||||
extKeyUsage = append(extKeyUsage, x509.ExtKeyUsage(v))
|
||||
}
|
||||
return extKeyUsage
|
||||
}
|
||||
|
||||
func exportPem(filename string, data []byte) {
|
||||
func exportPem(filename string, data []byte) (string, error) {
|
||||
outputPath, err := writer.WriteFile(filename, data)
|
||||
if err != nil {
|
||||
log.Fatalf("rootCA.WithClientCert: %s", err)
|
||||
return "", fmt.Errorf("rootCA.WithClientCert: %s", err)
|
||||
}
|
||||
log.Printf("file created successfully: %s\n", outputPath)
|
||||
return outputPath, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
intEnvConfig(false)
|
||||
envFile := ""
|
||||
if os.Getenv("ENV") != "prod" {
|
||||
envFile = "./.env"
|
||||
}
|
||||
|
||||
intEnvConfig(envFile)
|
||||
cobra.OnInitialize(initConfig)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,62 @@
|
||||
package cmd
|
||||
|
||||
import "testing"
|
||||
import (
|
||||
"crypto/x509"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gitlab.com/urkob/go-cert-gen/internal/io"
|
||||
)
|
||||
|
||||
func TestExecute(t *testing.T) {
|
||||
Execute()
|
||||
}
|
||||
|
||||
func Test_getExtKeyUsage(t *testing.T) {
|
||||
intKeyUsageSlice := make([]int, 0, 1)
|
||||
intKeyUsageSlice = append(intKeyUsageSlice, int(x509.ExtKeyUsageClientAuth))
|
||||
|
||||
keyUsage := getExtKeyUsage(intKeyUsageSlice)
|
||||
assert.Len(t, keyUsage, len(intKeyUsageSlice))
|
||||
assert.Equal(t, keyUsage[0], x509.ExtKeyUsageClientAuth)
|
||||
|
||||
intKeyUsageSlice = make([]int, 0)
|
||||
keyUsage = getExtKeyUsage(intKeyUsageSlice)
|
||||
assert.Len(t, keyUsage, 0)
|
||||
|
||||
keyUsage = getExtKeyUsage(nil)
|
||||
assert.Len(t, keyUsage, 0)
|
||||
}
|
||||
|
||||
var testFile = "test-file.txt"
|
||||
|
||||
func init() {
|
||||
wd, err := os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalf("os.Getwd: %s\n", err)
|
||||
}
|
||||
writer = io.NewWriter(wd)
|
||||
}
|
||||
|
||||
func Test_exportPem(t *testing.T) {
|
||||
defer func() {
|
||||
os.Remove(testFile)
|
||||
err := os.Remove(testFile)
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
data := []byte("test data")
|
||||
outputPath, err := exportPem(testFile, data)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, outputPath)
|
||||
}
|
||||
|
||||
func Test_exportPemError(t *testing.T) {
|
||||
data := []byte("test data")
|
||||
outputPath, err := exportPem("", data)
|
||||
require.Error(t, err)
|
||||
require.Empty(t, outputPath)
|
||||
}
|
||||
|
||||
@@ -83,6 +83,12 @@ func Test_newRootCA(t *testing.T) {
|
||||
require.Greater(t, len(keyPEM), 0)
|
||||
}
|
||||
|
||||
func Test_newRootCAError(t *testing.T) {
|
||||
_, _, err := newRootCA(&ca.CaConfig{})
|
||||
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
func Test_parseCertificate(t *testing.T) {
|
||||
caPEM, _, err := newRootCA(&rootTestConfig)
|
||||
require.NoError(t, err)
|
||||
@@ -126,3 +132,12 @@ func Test_rootCA_WithClientCert(t *testing.T) {
|
||||
require.NotNil(t, clientSrv.PEM())
|
||||
require.Greater(t, len(clientSrv.PEM()), 0)
|
||||
}
|
||||
|
||||
func Test_rootCA_WithClientCertEror(t *testing.T) {
|
||||
rootCert := rootCA{
|
||||
caPEM: nil,
|
||||
}
|
||||
|
||||
_, err := rootCert.WithClientCert(&clientTestConfig)
|
||||
require.Error(t, err)
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package io
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"gitlab.com/urkob/go-cert-gen/pkg/io"
|
||||
@@ -11,22 +12,33 @@ type writer struct {
|
||||
dirPath string
|
||||
}
|
||||
|
||||
// WriteFile writes file into `w writer` directory.
|
||||
// Returns outputPath and error
|
||||
func (w writer) WriteFile(filename string, data []byte) (string, error) {
|
||||
if filename == "" {
|
||||
return "", fmt.Errorf("filename cannot be empty")
|
||||
}
|
||||
if w.dirPath == "" {
|
||||
return "", fmt.Errorf("export directory cannot be empty")
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(w.dirPath, 0o755); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
log.Println("to write file")
|
||||
outputPath := w.dirPath + "/" + filename
|
||||
if err := os.WriteFile(outputPath, data, 0o600); err != nil {
|
||||
return "", err
|
||||
}
|
||||
log.Println("file written")
|
||||
return outputPath, nil
|
||||
}
|
||||
|
||||
func NewWriter(dirPath string) io.WriterIface {
|
||||
return newWriter(dirPath)
|
||||
}
|
||||
|
||||
func newWriter(dirPath string) *writer {
|
||||
return &writer{
|
||||
dirPath: dirPath,
|
||||
}
|
||||
|
||||
94
internal/io/writer_test.go
Normal file
94
internal/io/writer_test.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package io
|
||||
|
||||
import (
|
||||
"io/fs"
|
||||
"log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
testWriterPath = "testPath"
|
||||
testWriterPathError = "test Path, @:ººººº\\/.Ç*⁺´+"
|
||||
testFileContent = []byte("test data")
|
||||
testFileName = "test-file.txt"
|
||||
workingDir = ""
|
||||
)
|
||||
|
||||
func deleteAllDirs() error {
|
||||
return os.RemoveAll(testWriterPath)
|
||||
}
|
||||
|
||||
func init() {
|
||||
err := deleteAllDirs()
|
||||
if err != nil {
|
||||
log.Fatalln("deleteAllDirs: ", err)
|
||||
}
|
||||
workingDir, err = os.Getwd()
|
||||
if err != nil {
|
||||
log.Fatalln("os.Getwd: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func Test_newWriter(t *testing.T) {
|
||||
w := newWriter(testWriterPath)
|
||||
require.NotNil(t, w)
|
||||
}
|
||||
|
||||
func TestNewWriter(t *testing.T) {
|
||||
w := NewWriter(testWriterPath)
|
||||
require.NotNil(t, w)
|
||||
}
|
||||
|
||||
func Test_writer_WriteFile(t *testing.T) {
|
||||
err := deleteAllDirs()
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func(t *testing.T) {
|
||||
err := deleteAllDirs()
|
||||
require.NoError(t, err)
|
||||
}(t)
|
||||
|
||||
w := newWriter(testWriterPath)
|
||||
wgot, err := w.WriteFile(testFileName, testFileContent)
|
||||
require.NoError(t, err)
|
||||
|
||||
btsReaded, err := os.ReadFile(wgot)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, btsReaded)
|
||||
require.Equal(t, string(btsReaded), string(testFileContent))
|
||||
}
|
||||
|
||||
func Test_writer_WriteFileError(t *testing.T) {
|
||||
err := deleteAllDirs()
|
||||
require.NoError(t, err)
|
||||
|
||||
defer func(t *testing.T) {
|
||||
err := deleteAllDirs()
|
||||
require.NoError(t, err)
|
||||
}(t)
|
||||
|
||||
w := newWriter("")
|
||||
_, err = w.WriteFile(testFileName, testFileContent)
|
||||
assert.Error(t, err)
|
||||
|
||||
w = newWriter(testWriterPath)
|
||||
_, err = w.WriteFile("", testFileContent)
|
||||
require.Error(t, err)
|
||||
|
||||
err = os.MkdirAll(testWriterPath, fs.ModeDir)
|
||||
require.NoError(t, err, "mkdir should not throw error")
|
||||
|
||||
err = os.MkdirAll(testWriterPath+"/"+testFileName, fs.ModeDir)
|
||||
require.NoError(t, err)
|
||||
|
||||
_, err = w.WriteFile(testFileName, nil)
|
||||
assert.Error(t, err)
|
||||
|
||||
w = newWriter(testWriterPathError)
|
||||
_, err = w.WriteFile(testFileName, testFileContent)
|
||||
require.Error(t, err)
|
||||
}
|
||||
@@ -29,26 +29,10 @@ type Subject struct {
|
||||
PostalCode string
|
||||
}
|
||||
|
||||
/*
|
||||
var (
|
||||
subjectKeyId = []byte{1, 2, 3, 4, 6}
|
||||
extKeyUsage = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth, x509.ExtKeyUsageServerAuth}
|
||||
keyUsage = x509.KeyUsageDigitalSignature
|
||||
)
|
||||
|
||||
func NewDefaultConfig() *ClientCertConfig {
|
||||
return &ClientCertConfig{
|
||||
Serial: big.NewInt(12321),
|
||||
Subject: Subject{
|
||||
Organization: "",
|
||||
Country: "",
|
||||
Province: "",
|
||||
Locality: "",
|
||||
StreetAddress: "",
|
||||
PostalCode: "",
|
||||
},
|
||||
Duration: time.Duration(time.Hour * 24 * 365),
|
||||
SubjectKeyId: subjectKeyId,
|
||||
ExtKeyUsage: extKeyUsage,
|
||||
KeyUsage: keyUsage,
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package io
|
||||
|
||||
type WriterIface interface {
|
||||
// WriteFile writes file into `w writer` directory.
|
||||
// Returns outputPath and error
|
||||
WriteFile(filename string, data []byte) (string, error)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user