Compare commits
No commits in common. "main" and "0.1.0" have entirely different histories.
42 changed files with 214 additions and 928 deletions
41
.gitignore
vendored
41
.gitignore
vendored
|
@ -1,28 +1,31 @@
|
|||
# Allowlisting gitignore template for GO projects prevents us
|
||||
# from adding various unwanted local files, such as generated
|
||||
# files, developer configurations or IDE-specific files etc.
|
||||
# ---> Go
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Recommended: Go.AllowList.gitignore
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Ignore everything
|
||||
*
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# But not these files...
|
||||
!/.gitignore
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
!*.go
|
||||
!go.sum
|
||||
!go.mod
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
!examples/*
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
!*.md
|
||||
!LICENSE
|
||||
bin/
|
||||
|
||||
!Makefile
|
||||
examples/testing/
|
||||
|
||||
# Woodpecker CI
|
||||
!.woodpecker/*
|
||||
*.yaml
|
||||
!examples/*.yaml
|
||||
|
||||
# ...even if they are in subdirectories
|
||||
!*/
|
||||
test/
|
||||
|
|
24
.vscode/launch.json
vendored
24
.vscode/launch.json
vendored
|
@ -1,24 +0,0 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Debug Dry Run",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/certwarden-deploy/main.go",
|
||||
"args": ["--config", "${workspaceFolder}/config.yaml", "--dry-run"]
|
||||
},
|
||||
{
|
||||
"name": "Debug",
|
||||
"type": "go",
|
||||
"request": "launch",
|
||||
"mode": "auto",
|
||||
"program": "${workspaceFolder}/cmd/certwarden-deploy/main.go",
|
||||
"args": ["--config", "${workspaceFolder}/config.yaml", "--verbose"]
|
||||
}
|
||||
]
|
||||
}
|
|
@ -20,13 +20,13 @@ steps:
|
|||
- APP_NAME=certwarden-deploy
|
||||
- FORGE=https://code.lila.network
|
||||
commands:
|
||||
- apk add --update --no-cache xz curl jq make git
|
||||
- make build
|
||||
- cd bin/
|
||||
- mv $APP_NAME $APP_NAME-${GOOS}-${GOARCH}${GOARM}
|
||||
- xz --keep --compress $APP_NAME-${GOOS}-${GOARCH}${GOARM}
|
||||
- sha256sum $APP_NAME-${GOOS}-${GOARCH}${GOARM} >> $APP_NAME-${GOOS}-${GOARCH}${GOARM}.sha256
|
||||
- sha256sum $APP_NAME-${GOOS}-${GOARCH}${GOARM}.xz >> $APP_NAME-${GOOS}-${GOARCH}${GOARM}.xz.sha256
|
||||
- apk add --update --no-cache xz curl jq
|
||||
- go mod download
|
||||
- go build -o output/$APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM} main.go
|
||||
- cd output
|
||||
- xz --keep --compress $APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}
|
||||
- sha256sum $APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM} >> $APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}.sha256
|
||||
- sha256sum $APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}.xz >> $APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}.xz.sha256
|
||||
- |-
|
||||
export RELEASE_ID=`curl --location "$FORGE/api/v1/repos/$CI_REPO/releases?limit=10" \
|
||||
--header 'Accept: application/json' -s -S \
|
||||
|
@ -35,23 +35,23 @@ steps:
|
|||
curl --location "$FORGE/api/v1/repos/$CI_REPO/releases/$RELEASE_ID/assets" \
|
||||
--header "Authorization: token $FORGEJO_APIKEY" \
|
||||
--header 'Content-Type: multipart/form-data' -s -S \
|
||||
--form "attachment=@$APP_NAME-${GOOS}-${GOARCH}${GOARM};type=application/octet-stream" \
|
||||
--form "attachment=@$APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM};type=application/octet-stream" \
|
||||
--fail-with-body
|
||||
- |-
|
||||
curl --location "$FORGE/api/v1/repos/$CI_REPO/releases/$RELEASE_ID/assets" \
|
||||
--header "Authorization: token $FORGEJO_APIKEY" \
|
||||
--header 'Content-Type: multipart/form-data' -s -S \
|
||||
--form "attachment=@$APP_NAME-${GOOS}-${GOARCH}${GOARM}.xz;type=application/octet-stream" \
|
||||
--form "attachment=@$APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}.xz;type=application/octet-stream" \
|
||||
--fail-with-body
|
||||
- |-
|
||||
curl --location "$FORGE/api/v1/repos/$CI_REPO/releases/$RELEASE_ID/assets" \
|
||||
--header "Authorization: token $FORGEJO_APIKEY" \
|
||||
--header 'Content-Type: multipart/form-data' -s -S \
|
||||
--form "attachment=@$APP_NAME-${GOOS}-${GOARCH}${GOARM}.sha256;type=application/octet-stream" \
|
||||
--form "attachment=@$APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}.sha256;type=application/octet-stream" \
|
||||
--fail-with-body
|
||||
- |-
|
||||
curl --location "$FORGE/api/v1/repos/$CI_REPO/releases/$RELEASE_ID/assets" \
|
||||
--header "Authorization: token $FORGEJO_APIKEY" \
|
||||
--header 'Content-Type: multipart/form-data' -s -S \
|
||||
--form "attachment=@$APP_NAME-${GOOS}-${GOARCH}${GOARM}.xz.sha256;type=application/octet-stream" \
|
||||
--form "attachment=@$APP_NAME-${CI_COMMIT_TAG##v}-${GOOS}-${GOARCH}${GOARM}.xz.sha256;type=application/octet-stream" \
|
||||
--fail-with-body
|
||||
|
|
|
@ -1,38 +0,0 @@
|
|||
when:
|
||||
- event: push
|
||||
branch: main
|
||||
path:
|
||||
include:
|
||||
- 'docs/**'
|
||||
- '.woodpecker/deploy-docs.yml'
|
||||
ignore_message: '[ALL]'
|
||||
|
||||
steps:
|
||||
build:
|
||||
image: golang:1.22-bookworm
|
||||
environment:
|
||||
- HUGO_VERSION=0.128.1
|
||||
- TZ=Europe/Berlin
|
||||
|
||||
commands:
|
||||
- cd docs/
|
||||
- wget https://github.com/gohugoio/hugo/releases/download/v$${HUGO_VERSION}/hugo_extended_$${HUGO_VERSION}_linux-amd64.deb && apt install ./hugo_extended_$${HUGO_VERSION}_linux-amd64.deb && rm -f hugo_extended_$${HUGO_VERSION}_linux-amd64.deb
|
||||
- hugo --minify --destination ./public
|
||||
|
||||
upload:
|
||||
image: alpine:latest
|
||||
secrets:
|
||||
- RSYNC_SSHKEY
|
||||
- RSYNC_TARGET_SERVER
|
||||
- RSYNC_TARGET_USER
|
||||
environment:
|
||||
- TARGET_PATH=/webroot/certwarden-deploy.adora.codes
|
||||
- RSYNC_TARGET_PORT=2003
|
||||
commands:
|
||||
- cd docs/
|
||||
- apk add --update --no-cache openssh rsync git
|
||||
- mkdir -p $HOME/.ssh
|
||||
- echo "$RSYNC_SSHKEY" > $HOME/.ssh/id_ed25519
|
||||
- chmod 0600 $HOME/.ssh/id_ed25519
|
||||
- ssh-keyscan -t ed25519 -p $RSYNC_TARGET_PORT $RSYNC_TARGET_SERVER >> $HOME/.ssh/known_hosts
|
||||
- rsync -avh -e "ssh -p $RSYNC_TARGET_PORT" --delete ./public/ $RSYNC_TARGET_USER@$RSYNC_TARGET_SERVER:$TARGET_PATH
|
58
CHANGELOG.md
58
CHANGELOG.md
|
@ -1,58 +0,0 @@
|
|||
# Changelog
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [Unreleased]
|
||||
## [0.2.2] - 2024-07-30
|
||||
|
||||
### Changed
|
||||
- changed the way the version string is handled internally
|
||||
- CI pipeline changed
|
||||
- documentation is now more sophisticated and has a new theme
|
||||
|
||||
### Added
|
||||
- Makefile
|
||||
|
||||
## [0.2.1] - 2024-07-12
|
||||
### Fixed
|
||||
- Configuration validation did not work as intended
|
||||
|
||||
### Changed
|
||||
- updated example config file
|
||||
|
||||
## [0.2.0] - 2024-07-11
|
||||
### ⚠️ Breaking Changes
|
||||
- Config file syntax was changed to accomodate both private and public key deployment for certificates.
|
||||
|
||||
This change is __NOT__ backwards compatible!
|
||||
The following yaml keys were changed/added:
|
||||
- `api_key`: changed to `cert_secret`
|
||||
- `file_path`: changed to `cert_path`
|
||||
- added keys: `key_secret`, `key_path`
|
||||
|
||||
### Changed
|
||||
- config file syntax to enable deployment of private keys too
|
||||
- refactor code
|
||||
|
||||
## [0.1.1] - 2024-07-03
|
||||
|
||||
### Fixed
|
||||
- Fixed handling of the post certificate action
|
||||
|
||||
## [0.1.0] - 2024-07-03
|
||||
|
||||
### Added
|
||||
- Minimal viable application
|
||||
- some documentation
|
||||
|
||||
|
||||
[unreleased]: https://code.lila.network/adoralaura/certwarden-deploy/compare/0.2.2...HEAD
|
||||
[0.2.2]: https://code.lila.network/adoralaura/certwarden-deploy/compare/0.2.1...0.2.2
|
||||
[0.2.1]: https://code.lila.network/adoralaura/certwarden-deploy/compare/0.2.0...0.2.1
|
||||
[0.2.0]: https://code.lila.network/adoralaura/certwarden-deploy/compare/0.1.1...0.2.0
|
||||
[0.1.1]: https://code.lila.network/adoralaura/certwarden-deploy/compare/0.1.0...0.1.1
|
||||
[0.1.0]: https://code.lila.network/adoralaura/certwarden-deploy/releases/tag/0.1.0
|
|
@ -1,17 +0,0 @@
|
|||
# Contributing
|
||||
|
||||
I use my own [Forgejo Instance](https://code.lila.network) to manage issues and pull requests.
|
||||
|
||||
* If you have a trivial fix or improvement, go ahead and create a pull request,
|
||||
addressing (with `@...`) the maintainer of this repository (see
|
||||
[MAINTAINERS.md](MAINTAINERS.md)) in the description of the pull request.
|
||||
|
||||
* If you plan to do something more involved, first please [send me a mail]( mailto:dev@lauka.net?subject=%5Bcertwarden-deploy%5D).
|
||||
|
||||
# What to contribute
|
||||
|
||||
The best way to help without speaking a lot of Go would be to share your
|
||||
configuration, alerts, dashboards, and recording rules. If you have something
|
||||
that works and is not in the repository, please pay it forward and
|
||||
share what works.
|
||||
|
|
@ -1 +0,0 @@
|
|||
* Adora Laura Kalb <dev@lauka.net> @adoralaura
|
13
Makefile
13
Makefile
|
@ -1,13 +0,0 @@
|
|||
# Set the default Go build flags
|
||||
GOFLAGS = -ldflags='-w -s -X constants.Version=$(VERSION)'
|
||||
|
||||
# Build the application
|
||||
build:
|
||||
go build $(GOFLAGS) -o bin/certwarden-deploy cmd/certwarden-deploy/main.go
|
||||
|
||||
# Clean the build artifacts
|
||||
clean:
|
||||
rm -rf bin
|
||||
|
||||
# Set a version for the build
|
||||
VERSION := $(shell git describe --tags --always)
|
75
README.md
75
README.md
|
@ -2,81 +2,10 @@
|
|||
![status-badge](https://ci.lila.network/api/badges/22/status.svg)
|
||||
[![Please don't upload to GitHub](https://nogithub.codeberg.page/badge.svg)](https://nogithub.codeberg.page)
|
||||
|
||||
This is a simple binary to deploy certificates from a [CertWarden](https://www.certwarden.com/) instance.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Installation of the required CertWarden instance is out of scope of this documentation. For detailed instructions regarding CertWarden, please visit [it's documentation](https://www.certwarden.com/docs/introduction/)
|
||||
|
||||
|
||||
To quickly get started with `certwarden-deploy`, just download the binary...
|
||||
|
||||
```shell
|
||||
# this downloads certwarden-deploy version 0.2.2
|
||||
# to /usr/local/bin/certwarden-deploy
|
||||
sudo wget https://code.lila.network/adoralaura/certwarden-deploy/releases/download/0.2.2/certwarden-deploy-linux-amd64 -O /usr/local/bin/certwarden-deploy
|
||||
|
||||
sudo chmod +x /usr/local/bin/certwarden-deploy
|
||||
```
|
||||
|
||||
... fill out the config file...
|
||||
```shell
|
||||
vi /etc/certwarden-deploy/config.yaml
|
||||
```
|
||||
```yaml
|
||||
# Base URL of the CertWarden instance
|
||||
# required
|
||||
base_url: "https://certwarden.example.com"
|
||||
|
||||
# Set this to true if your CertWarden instance does not have a publicly trusted
|
||||
# TLS certificate (e.g. it has a self signed one)
|
||||
# default is false
|
||||
disable_certificate_validation: false
|
||||
|
||||
# define all managed certificates here
|
||||
certificates:
|
||||
# name is a unique identifier that must start and end with an alphanumeric character,
|
||||
# and can contain the following characters: a-zA-Z0-9._-
|
||||
# required
|
||||
- name: test-certificate.example.com
|
||||
# Contains the API-Key to fetch the certificate from the server
|
||||
# required
|
||||
cert_secret: examplekey_notvalid_hrzjGDDw8z
|
||||
# path where to save the certificate
|
||||
# required
|
||||
cert_path: "/path/to/test-certificate.example.com-cert.pem"
|
||||
# Contains the API-Key to fetch the private key from the server
|
||||
# required
|
||||
key_secret: examplekey_notvalid_hrzbbDDw8z
|
||||
# path where to save the private key
|
||||
# required
|
||||
key_path: "/path/to/test-certificate.example.com-key.pem"
|
||||
# action to run when certificate was updated or --force is on
|
||||
action: "/usr/bin/systemd reload caddy"
|
||||
```
|
||||
|
||||
... and run it!
|
||||
```shell
|
||||
certwarden-deploy -v
|
||||
```
|
||||
## Contributing
|
||||
|
||||
I use my own [Forgejo](https://forgejo.org/) Instance [code.lila.network](https://code.lila.network) to manage issues, pull requests and CI/CD.
|
||||
|
||||
* If you have a trivial fix or improvement, go ahead and send a diff to the maintainer(s) of this repository (see
|
||||
[MAINTAINERS.md](https://code.lila.network/adoralaura/certwarden-deploy/src/branch/main/MAINTAINERS.md)).
|
||||
|
||||
* If you plan to do something more involved, first please [send me a mail]( mailto:dev@lauka.net?subject=%5Bcertwarden-deploy%5D)mso I can create an account for you.
|
||||
|
||||
### Non-development Contibutions
|
||||
|
||||
The best way to help without speaking a lot of Go would be to share your
|
||||
configuration, setup, and tips. If you have something
|
||||
that works and is not in the repository, please pay it forward and
|
||||
share what works.
|
||||
This is a tool to deploy certificates from a [CertWarden](https://www.certwarden.com/) instance.
|
||||
|
||||
## Changelog
|
||||
You can find the Changelog here: [Changelog](https://code.lila.network/adoralaura/certwarden-deploy/src/branch/main/CHANGELOG.md)
|
||||
|
||||
## License
|
||||
`certwarden-deploy` is available under the MIT license. See the [LICENSE](https://code.lila.network/adoralaura/certwarden-deploy/src/branch/main/LICENSE) file for more info.
|
||||
`certwarden-deploy` is available under the MIT license. See the LICENSE file for more info.
|
||||
|
|
|
@ -2,20 +2,25 @@
|
|||
Copyright © 2024 Laura Kalb <dev@lauka.net>
|
||||
The code of this project is available under the MIT license. See the LICENSE file for more info.
|
||||
*/
|
||||
package main
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/cli"
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/configuration"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||
func Execute() {
|
||||
err := cli.RootCmd.Execute()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
}
|
||||
defer sentry.Flush(2 * time.Second)
|
||||
}
|
||||
|
||||
func init() {
|
15
docs/.gitignore
vendored
15
docs/.gitignore
vendored
|
@ -1,15 +0,0 @@
|
|||
# Generated files by hugo
|
||||
/public/
|
||||
/resources/_gen/
|
||||
/assets/jsconfig.json
|
||||
hugo_stats.json
|
||||
|
||||
# Executable may be added to repository
|
||||
hugo.exe
|
||||
hugo.darwin
|
||||
hugo.linux
|
||||
|
||||
# Temporary lock file while building
|
||||
/.hugo_build.lock
|
||||
|
||||
!**.html
|
|
@ -1,9 +0,0 @@
|
|||
MIT License
|
||||
|
||||
Copyright 2024 Adora Laura Kalb <dev@lauka.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -1 +0,0 @@
|
|||
# certwarden-deploy Documentation
|
|
@ -1,5 +0,0 @@
|
|||
+++
|
||||
title = '{{ replace .File.ContentBaseName "-" " " | title }}'
|
||||
date = {{ .Date }}
|
||||
draft = true
|
||||
+++
|
|
@ -1,54 +0,0 @@
|
|||
baseURL = 'https://certwarden-deploy.adora.codes/'
|
||||
languageCode = 'en-us'
|
||||
title = 'certwarden-deploy'
|
||||
author = ""
|
||||
|
||||
theme = "github.com/McShelby/hugo-theme-relearn"
|
||||
repo = "https://code.lila.network/adoralaura/certwarden-deploy"
|
||||
|
||||
enableGitInfo = true
|
||||
enableRobotsTXT = true
|
||||
uniqueHomePage = false # change to false to add sidebar to homepage
|
||||
|
||||
|
||||
[params]
|
||||
disableLandingPageButton = false
|
||||
disableLanguageSwitchingButton = false
|
||||
editURL = "https://code.lila.network/adoralaura/certwarden-deploy/_edit/main/docs/content/${FilePath}"
|
||||
externalLinkTarget = "_blank"
|
||||
headingPre = '<script defer src="https://esseles.adora.codes/script.js" data-website-id="fe4ec517-25b2-4e0d-b502-6bd3a7420849"></script>'
|
||||
disableExplicitIndexURLs = true
|
||||
|
||||
[params.author]
|
||||
name = 'Adora Laura Kalb'
|
||||
|
||||
[outputs]
|
||||
home = ['html', 'rss', 'search']
|
||||
|
||||
[menu]
|
||||
[[menu.shortcuts]]
|
||||
identifier = 'ds'
|
||||
name = "<i class='fa-fw fas fa-code-branch'></i> certwarden-deploy Git Repository"
|
||||
url = 'https://code.lila.network/adoralaura/certwarden-deploy'
|
||||
weight = 10
|
||||
|
||||
[[menu.shortcuts]]
|
||||
name = "<i class='fa-fw fas fa-arrow-up-right-from-square'></i> CertWarden GitHub"
|
||||
url = 'showcase/'
|
||||
weight = 11
|
||||
|
||||
[[menu.shortcuts]]
|
||||
identifier = 'hugodoc'
|
||||
name = "<i class='fa-fw fas fa-arrow-up-right-from-square'></i> CertWarden Website"
|
||||
url = 'https://gohugo.io/'
|
||||
weight = 20
|
||||
|
||||
# [[menu.shortcuts]]
|
||||
# name = "<i class='fa-fw fas fa-bullhorn'></i> Credits"
|
||||
# url = 'more/credits/'
|
||||
# weight = 30
|
||||
#
|
||||
# [[menu.shortcuts]]
|
||||
# name = "<i class='fa-fw fas fa-tags'></i> Tags"
|
||||
# url = 'tags/'
|
||||
# weight = 40
|
|
@ -1,93 +0,0 @@
|
|||
---
|
||||
title: CertWarden-Deploy
|
||||
type: docs
|
||||
---
|
||||
|
||||
[CertWarden](https://www.certwarden.com/) is a self-hosted Centralized ACME Certificate Management platform. With it you can manage and aquire Let's Encrypt certificates.
|
||||
|
||||
However, to deploy them to your hosts, for now there only was a docker client, and that was too bloated for me.
|
||||
|
||||
So I built `certwarden-deploy`, a dependency-less binary that can run via crontab/systemd timers and that can fetch new certificates and run actions after new certificates got rolled out.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Installation of the required CertWarden instance is out of scope of this documentation. For detailed instructions regarding CertWarden, please visit [it's documentation](https://www.certwarden.com/docs/introduction/)
|
||||
|
||||
|
||||
To quickly get started with `certwarden-deploy`, just download the binary...
|
||||
|
||||
```shell
|
||||
# this downloads certwarden-deploy version 0.2.2
|
||||
# to /usr/local/bin/certwarden-deploy
|
||||
sudo wget https://code.lila.network/adoralaura/certwarden-deploy/releases/download/0.2.2/certwarden-deploy-linux-amd64 -O /usr/local/bin/certwarden-deploy
|
||||
|
||||
sudo chmod +x /usr/local/bin/certwarden-deploy
|
||||
```
|
||||
|
||||
... fill out the config file...
|
||||
|
||||
|
||||
`vi /etc/certwarden-deploy/config.yaml`
|
||||
```yaml
|
||||
# Base URL of the CertWarden instance
|
||||
# required
|
||||
base_url: "https://certwarden.example.com"
|
||||
|
||||
# Set this to true if your CertWarden instance does not have a publicly trusted
|
||||
# TLS certificate (e.g. it has a self signed one)
|
||||
# default is false
|
||||
disable_certificate_validation: false
|
||||
|
||||
# define all managed certificates here
|
||||
certificates:
|
||||
|
||||
# name is a unique identifier that must start and end with an alphanumeric character,
|
||||
# and can contain the following characters: a-zA-Z0-9._-
|
||||
# required
|
||||
- name: test-certificate.example.com
|
||||
|
||||
# Contains the API-Key to fetch the certificate from the server
|
||||
# required
|
||||
cert_secret: examplekey_notvalid_hrzjGDDw8z
|
||||
|
||||
# path where to save the certificate
|
||||
# required
|
||||
cert_path: "/path/to/test-certificate.example.com-cert.pem"
|
||||
|
||||
# Contains the API-Key to fetch the private key from the server
|
||||
# required
|
||||
key_secret: examplekey_notvalid_hrzbbDDw8z
|
||||
|
||||
# path where to save the private key
|
||||
# required
|
||||
key_path: "/path/to/test-certificate.example.com-key.pem"
|
||||
|
||||
# action to run when certificate was updated or --force is on
|
||||
action: "/usr/bin/systemd reload caddy"
|
||||
```
|
||||
|
||||
... and run it!
|
||||
```shell
|
||||
certwarden-deploy -v
|
||||
```
|
||||
## Contributing
|
||||
|
||||
I use my own [Forgejo](https://forgejo.org/) Instance [code.lila.network](https://code.lila.network) to manage issues, pull requests and CI/CD.
|
||||
|
||||
* If you have a trivial fix or improvement, go ahead and send a diff to the maintainer(s) of this repository (see
|
||||
[MAINTAINERS.md](https://code.lila.network/adoralaura/certwarden-deploy/src/branch/main/MAINTAINERS.md)).
|
||||
|
||||
* If you plan to do something more involved, first please [send me a mail](mailto:dev@lauka.net?subject=%5Bcertwarden-deploy%5D) so I can create an account for you.
|
||||
|
||||
### Non-development Contibutions
|
||||
|
||||
The best way to help without speaking a lot of Go would be to share your
|
||||
configuration, setup, and tips. If you have something
|
||||
that works and is not in the repository, please pay it forward and
|
||||
share what works.
|
||||
|
||||
## Changelog
|
||||
You can find the Changelog here: [Changelog](https://code.lila.network/adoralaura/certwarden-deploy/src/branch/main/CHANGELOG.md)
|
||||
|
||||
## License
|
||||
`certwarden-deploy` is available under the MIT license. See the [License page](/license/) for more info.
|
|
@ -1,85 +0,0 @@
|
|||
---
|
||||
title: Configuration
|
||||
weight: 20
|
||||
---
|
||||
|
||||
|
||||
This document describes how to configure `certwarden-deploy` and which certificates should be managed by it. The configuration file uses the [YAML format](https://yaml.org/) for a human-readable and easy-to-maintain structure.
|
||||
|
||||
## certwarden-deploy CLI Options
|
||||
```plaintext
|
||||
$ ./certwarden-deploy --help
|
||||
certwarden-deploy is a CLI utility to deploy certificates managed by CertWarden.
|
||||
Configuration is handled by a single YAML file, so you can get started quickly.
|
||||
|
||||
For more information on how to configure this tool, visit the docs at https://certwarden-deploy.adora.codes
|
||||
|
||||
Usage:
|
||||
certwarden-deploy [flags]
|
||||
|
||||
Flags:
|
||||
-c, --config string Path to config file (default is /etc/certwarden-deploy/config.yaml) (default "/etc/certwarden-deploy/config.yaml")
|
||||
-d, --dry-run Just show the would-be changes without changing the file system (turns on verbose logging)
|
||||
-f, --force Force overwriting and execution action to occur, regardless if certificate already exists
|
||||
-h, --help help for certwarden-deploy
|
||||
-q, --quiet Disable any logging (if both -q and -v are set, quiet wins)
|
||||
-v, --verbose Enable verbose logging
|
||||
--version version for certwarden-deploy
|
||||
```
|
||||
|
||||
## Configuration File Options
|
||||
|
||||
`base_url` (required):
|
||||
This string specifies the base URL of your CertWarden instance.
|
||||
|
||||
`disable_certificate_validation` (optional, default: false):
|
||||
This boolean flag indicates whether to disable certificate validation for the CertWarden instance. Set this to true only if your CertWarden instance uses a self-signed certificate and you trust it explicitly. **Disabling validation weakens security, so use it with caution.**
|
||||
|
||||
`certificates:` (required):
|
||||
This is a list that defines each certificate to be managed.
|
||||
Each certificate definition is a nested YAML block with the following properties:
|
||||
|
||||
Each certificate configuration consists of:
|
||||
|
||||
`name` (required):
|
||||
This string is a unique identifier for the certificate and must be the same as in you CertWarden instance.
|
||||
It must start and end with an alphanumeric character and can contain letters (a-zA-Z), numbers (0-9), underscore (_), hyphen (-), and period (.).
|
||||
|
||||
`cert_secret` (required):
|
||||
This string holds the API key used to fetch the certificate data from the CertWarden server.
|
||||
|
||||
`cert_path` (required):
|
||||
This string defines the file path where the downloaded certificate will be saved.
|
||||
|
||||
`key_secret` (required):
|
||||
This string holds the API key used to fetch the private key data from the CertWarden server.
|
||||
|
||||
`key_path` (required):
|
||||
This string defines the file path where the downloaded private key will be saved.
|
||||
|
||||
`action` (optional):
|
||||
This string specifies a command to run after a certificate is updated or when the --force flag is used during execution.
|
||||
The example uses a systemd reload command for the popular reverse proxy named "caddy".
|
||||
|
||||
Example Configuration:
|
||||
```yaml
|
||||
# Base URL of the CertWarden instance
|
||||
base_url: "https://certwarden.example.com"
|
||||
|
||||
# Disable certificate validation (not recommended for production)
|
||||
disable_certificate_validation: false
|
||||
|
||||
# Define all managed certificates here
|
||||
certificates:
|
||||
- name: test-certificate.example.com
|
||||
cert_secret: examplekey_notvalid_hrzjGDDw8z # Replace with your actual key
|
||||
cert_path: "/path/to/test-certificate.example.com-cert.pem"
|
||||
key_secret: examplekey_notvalid_hrzbbDDw8z # Replace with your actual key
|
||||
key_path: "/path/to/test-certificate.example.com-key.pem"
|
||||
action: "/usr/bin/systemctl reload caddy"
|
||||
```
|
||||
Use code with caution.
|
||||
|
||||
## Notes
|
||||
- This documentation assumes you have a basic understanding of YAML syntax. Resources for learning YAML are readily available online.
|
||||
- Replace placeholder values like `examplekey_notvalid_hrzjGDDw8z` with your actual API keys.
|
|
@ -1,30 +0,0 @@
|
|||
---
|
||||
title: Installation
|
||||
weight: 10
|
||||
---
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before building the project, ensure you have the following installed:
|
||||
- make: A build automation tool
|
||||
- Go: Version 1.22 or later
|
||||
|
||||
## Building the Project from Source
|
||||
|
||||
To build the project, first clone the projects git repository, then navigate to the project's root directory and run the following command:
|
||||
```shell
|
||||
make build
|
||||
```
|
||||
This command will generate the `certwarden-deploy` binary in the `bin/` folder.
|
||||
|
||||
## Getting pre-built Binaries
|
||||
You can also get pre-built binaries from the [releases page](https://code.lila.network/adoralaura/certwarden-deploy/releases). Make sure you get the binaries fitting your architecture!
|
||||
|
||||
## Setting up automatic Certificate Renewals
|
||||
Although not required for `certwarden-deploy` to work, it's highly rrecommended to set up automatic renewals for `certwarden-deploy`, so that you don't need to worry about rolling out your certificates every time they get renewed by CertWarden.
|
||||
|
||||
To do that, there are example `systemd` Service and Timer files included in the `examples/` directory of the `certwarden-deploy` repository.
|
||||
|
||||
Please make sure to customize them to your requirements (path to `certwarden-deploy` binary, user and group, execution interval...) and then drop them into the `/etc/systemd/system/` directory, then enable the timer with `systemctl enable --now certwarden-deploy.timer`
|
||||
|
||||
If you kept the example schedule, `certwarden-deploy` will run every saturday at ~4am.
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
title: License
|
||||
weight: 99
|
||||
---
|
||||
|
||||
## Documentation
|
||||
This documentation is available under the [CC BY-SA 4.0](https://creativecommons.org/licenses/by-sa/4.0/)
|
||||
|
||||
## Source Code
|
||||
The source code of `certwarden-deploy` is available under the MIT license:
|
||||
|
||||
```plaintext
|
||||
MIT License
|
||||
|
||||
Copyright © 2024 Adora Laura Kalb <dev@lauka.net>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to use,
|
||||
copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the
|
||||
Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
|
||||
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
|
||||
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
||||
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
```
|
|
@ -1,5 +0,0 @@
|
|||
module code.lila.network/adoralaura/certwarden-deploy-docs
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require github.com/McShelby/hugo-theme-relearn v0.0.0-20240721222908-7aec99b38dc2 // indirect
|
|
@ -1,2 +0,0 @@
|
|||
github.com/McShelby/hugo-theme-relearn v0.0.0-20240721222908-7aec99b38dc2 h1:022HGVq2CBuTftLgNRiU3rxqh+w3M3ZcschnXbjgomc=
|
||||
github.com/McShelby/hugo-theme-relearn v0.0.0-20240721222908-7aec99b38dc2/go.mod h1:mKQQdxZNIlLvAj8X3tMq+RzntIJSr9z7XdzuMomt0IM=
|
|
@ -1,2 +0,0 @@
|
|||
{{ .Params.headingPost | safeHTML }}
|
||||
<script defer src="https://esseles.adora.codes/script.js" data-website-id="fe4ec517-25b2-4e0d-b502-6bd3a7420849"></script>
|
|
@ -1 +0,0 @@
|
|||
<img src="/images/logo.svg"/>
|
BIN
docs/static/images/favicon.ico
vendored
BIN
docs/static/images/favicon.ico
vendored
Binary file not shown.
Before Width: | Height: | Size: 15 KiB |
1
docs/static/images/logo.svg
vendored
1
docs/static/images/logo.svg
vendored
|
@ -1 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 3246 924" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g id="BG"></g><rect id="Artboard1" x="0" y="0" width="3245.03" height="923.786" style="fill:none;"/><rect x="142.173" y="142.589" width="513.711" height="659.756" style="fill:#96c8d1;fill-rule:nonzero;"/><rect x="207.956" y="204.786" width="380.629" height="533.018" style="fill:#cde9e3;fill-rule:nonzero;"/><path d="M518.389,552.593l-55.026,-0l0,109.913l27.444,-24.548l27.582,24.548l-0,-109.913Z" style="fill:#96c96c;fill-rule:nonzero;"/><path d="M536.731,543.491c-0,25.237 -20.549,45.785 -45.924,45.785c-25.375,0 -45.924,-20.548 -45.924,-45.785c0,-25.376 20.549,-45.786 45.924,-45.786c25.375,-0 45.924,20.548 45.924,45.786Z" style="fill:#fbba22;fill-rule:nonzero;"/><path d="M655.884,124.385l-513.711,0c-10.067,0 -18.342,8.275 -18.342,18.342l0,659.756c0,10.068 8.275,18.342 18.342,18.342l513.711,0c10.067,0 18.342,-8.274 18.342,-18.342l-0,-659.894c-0,-10.067 -8.275,-18.204 -18.342,-18.204Zm-18.342,659.757l-477.027,-0l0,-623.211l477.027,0l0,623.211Z" style="fill:#211f1e;fill-rule:nonzero;"/><path d="M206.439,747.458l385.179,-0c5.103,-0 9.24,-4.137 9.24,-9.102l0,-531.501c0,-5.103 -4.137,-9.102 -9.24,-9.102l-385.179,-0c-5.103,-0 -9.24,4.137 -9.24,9.102l-0,531.501c-0,4.965 4.137,9.102 9.24,9.102Zm9.102,-531.501l366.838,-0l-0,513.159l-366.838,-0l-0,-513.159Z" style="fill:#211f1e;fill-rule:nonzero;"/><path d="M341.728,600.723l-55.026,-0c-5.103,-0 -9.24,4.137 -9.24,9.102c-0,5.102 4.137,9.102 9.24,9.102l55.026,-0c5.102,-0 9.239,-4.138 9.239,-9.102c-0.138,-4.965 -4.275,-9.102 -9.239,-9.102Zm-61.922,-201.899l238.445,0c5.103,0 9.24,-4.137 9.24,-9.102c-0,-5.102 -4.137,-9.102 -9.24,-9.102l-238.445,0c-5.102,0 -9.239,4.137 -9.239,9.102c-0,4.965 4.137,9.102 9.239,9.102Zm119.154,36.684l-119.292,-0c-5.102,-0 -9.239,4.137 -9.239,9.102c-0,5.103 4.137,9.102 9.239,9.102l119.292,-0c5.102,-0 9.24,-4.137 9.24,-9.102c-0,-5.103 -4.138,-9.102 -9.24,-9.102Zm-119.154,-91.71l238.445,0c5.103,0 9.24,-4.137 9.24,-9.102c-0,-5.102 -4.137,-9.102 -9.24,-9.102l-238.445,0c-5.102,0 -9.239,4.138 -9.239,9.102c-0,4.965 4.137,9.102 9.239,9.102Zm0,-54.887l238.445,-0c5.103,-0 9.24,-4.138 9.24,-9.102c-0,-5.103 -4.137,-9.102 -9.24,-9.102l-238.445,-0c-5.102,-0 -9.239,4.137 -9.239,9.102c-0,4.964 4.137,9.102 9.239,9.102Zm201.899,201.485c-30.34,-0 -55.026,24.685 -55.026,55.025c0,16.274 7.172,30.754 18.342,40.821l0,78.195c0,3.585 2.069,6.895 5.379,8.412c3.31,1.517 7.171,0.828 9.929,-1.517l21.376,-19.169l21.376,19.169c1.655,1.517 3.861,2.345 6.068,2.345c1.241,-0 2.482,-0.276 3.723,-0.828c3.31,-1.517 5.379,-4.689 5.379,-8.412l-0,-78.195c11.171,-10.067 18.342,-24.685 18.342,-40.821c0.138,-30.34 -24.548,-55.025 -54.888,-55.025Zm18.342,153.492l-12.274,-10.895c-3.448,-3.171 -8.826,-3.171 -12.274,0l-12.274,10.895l0,-46.751c5.792,2.069 11.86,3.172 18.342,3.172c6.482,0 12.55,-1.103 18.342,-3.172l0,46.751l0.138,0Zm-18.342,-61.783c-20.273,0 -36.684,-16.411 -36.684,-36.684c0,-20.272 16.411,-36.683 36.684,-36.683c20.273,-0 36.684,16.411 36.684,36.683c-0,20.273 -16.411,36.684 -36.684,36.684Z" style="fill:#211f1e;fill-rule:nonzero;"/><g transform="matrix(283.53,0,0,283.53,3117.47,579.164)"></g><text x="784.992px" y="579.164px" style="font-family:'ArialMT', 'Arial', sans-serif;font-size:283.53px;fill:#fff;">certwarden-deploy</text></svg>
|
Before Width: | Height: | Size: 3.6 KiB |
|
@ -1,13 +0,0 @@
|
|||
[Unit]
|
||||
Description=CertWarden Deployer binary
|
||||
Documentation=https://code.lila.network/adoralaura/certwarden-deploy
|
||||
|
||||
[Service]
|
||||
# uncomment if you want to use a different user than root
|
||||
# User=certwarden-deploy
|
||||
# Group=certwarden-deploy
|
||||
ExecStart=/usr/local/bin/certwarden-deploy
|
||||
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
|
@ -1,10 +0,0 @@
|
|||
[Unit]
|
||||
Description=Timer for certwarden-deploy
|
||||
|
||||
[Timer]
|
||||
Persistent=true
|
||||
OnCalendar=Sat *-*-* 04:00:00
|
||||
RandomizedDelaySec=2h
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
|
@ -17,20 +17,12 @@ certificates:
|
|||
|
||||
# Contains the API-Key to fetch the certificate from the server
|
||||
# required
|
||||
cert_secret: examplekey_notvalid_hrzjGDDw8z
|
||||
|
||||
# path where to save the certificate
|
||||
# required
|
||||
cert_path: "/path/to/test-certificate.example.com-cert.pem"
|
||||
|
||||
# Contains the API-Key to fetch the private key from the server
|
||||
# required
|
||||
key_secret: examplekey_notvalid_hrzbbDDw8z
|
||||
|
||||
# path where to save the private key
|
||||
# required
|
||||
key_path: "/path/to/test-certificate.example.com-key.pem"
|
||||
api_key: examplekey_notvalid_hrzjGDDw8z
|
||||
|
||||
# action to run when certificate was updated or --force is on
|
||||
action: "/usr/bin/systemd reload caddy"
|
||||
|
||||
# path where to save the certificate
|
||||
# required
|
||||
file_path: "/path/to/test-certificate.example.com-cert.pem"
|
||||
|
|
3
go.mod
3
go.mod
|
@ -8,8 +8,11 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/getsentry/sentry-go v0.28.1 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/kr/pretty v0.3.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||
)
|
||||
|
|
6
go.sum
6
go.sum
|
@ -1,5 +1,7 @@
|
|||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/getsentry/sentry-go v0.28.1 h1:zzaSm/vHmGllRM6Tpx1492r0YDzauArdBfkJRtY6P5k=
|
||||
github.com/getsentry/sentry-go v0.28.1/go.mod h1:1fQZ+7l7eeJ3wYi82q5Hg8GqAPgefRq+FP/QhafYVgg=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
|
@ -14,6 +16,10 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
|||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
|
@ -17,203 +17,133 @@ import (
|
|||
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/configuration"
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/constants"
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
func HandleCertificates(logger *slog.Logger, config *configuration.ConfigFileData) {
|
||||
for _, cert := range config.Certificates {
|
||||
certInfos := GenericCertificate{
|
||||
Name: cert.Name,
|
||||
FilePath: cert.CertificatePath,
|
||||
Secret: cert.CertificateSecret,
|
||||
IsKey: false,
|
||||
}
|
||||
|
||||
keyInfos := GenericCertificate{
|
||||
Name: cert.Name,
|
||||
FilePath: cert.KeyPath,
|
||||
Secret: cert.KeySecret,
|
||||
IsKey: true,
|
||||
}
|
||||
|
||||
// Rollout Certificate
|
||||
certOnDiskChanged, err := certInfos.Rollout(logger, config.BaseURL, config.DisableCertificateValidation)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
"Failed to roll out Certificate", "path",
|
||||
certInfos.FilePath, "name", cert.Name, "error", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// Rollout Key
|
||||
keyOnDiskChanged, err := keyInfos.Rollout(logger, config.BaseURL, config.DisableCertificateValidation)
|
||||
if err != nil {
|
||||
logger.Error(
|
||||
"Failed to roll out Key", "path",
|
||||
keyInfos.FilePath, "name", cert.Name, "error", err,
|
||||
)
|
||||
continue
|
||||
}
|
||||
|
||||
// if cert OR key changed OR --force
|
||||
if (certOnDiskChanged || keyOnDiskChanged) || configuration.Force {
|
||||
|
||||
if configuration.Force {
|
||||
logger.Info("Forcing file system change due to --force", "name", cert.Name)
|
||||
}
|
||||
err = handleCertificateAction(cert.Action)
|
||||
if err != nil {
|
||||
logger.Error("Failed to execute post-rollout action", "name", cert.Name, "error", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rollout handles getting the certificate/key data from the
|
||||
// server and writing it to disk if the data differs.
|
||||
//
|
||||
// Returns error on error, true if certificate action needs to be executed, false if not
|
||||
func (c *GenericCertificate) Rollout(logger *slog.Logger, baseUrl string, skipInsecure bool) (bool, error) {
|
||||
err := c.fetchFromServer(
|
||||
certBytes, err := getCertFromServer(
|
||||
logger,
|
||||
baseUrl,
|
||||
skipInsecure,
|
||||
cert.Name,
|
||||
cert.ApiKey,
|
||||
config.BaseURL,
|
||||
config.DisableCertificateValidation,
|
||||
)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to get certificate from server: %w", err)
|
||||
logger.Error("Failed to get certificate from server", "cert-id", cert.Name, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
fileNeedsRollout, err := c.needsRollout(logger)
|
||||
certIsDifferent, err := checkCertIsDifferent(logger, cert.FilePath, certBytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to check certificate on disk: %w", err)
|
||||
logger.Error("failed to handle certificate", "cert-id", cert.Name, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if fileNeedsRollout || configuration.Force {
|
||||
if certIsDifferent || configuration.Force {
|
||||
if configuration.Force {
|
||||
logger.Info("Forcing file system change due to --force", "name", c.Name)
|
||||
logger.Info("Forcing file system change due to --force", "cert-id", cert.Name)
|
||||
}
|
||||
|
||||
err = c.writeToDisk(logger)
|
||||
err = updateCertOnFS(logger, cert.FilePath, certBytes)
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("failed to handle certificate: %w", err)
|
||||
logger.Error("failed to handle certificate", "cert-id", cert.Name, "error", err)
|
||||
return
|
||||
}
|
||||
|
||||
if configuration.Force {
|
||||
logger.Info("Forcing file system change due to --force", "cert-id", cert.Name)
|
||||
}
|
||||
if fileNeedsRollout {
|
||||
logger.Info("New file deployed", "path", c.FilePath)
|
||||
return true, nil
|
||||
} else if configuration.Force {
|
||||
logger.Info("File deployed", "path", c.FilePath)
|
||||
return true, nil
|
||||
} else {
|
||||
logger.Info("File not changed, skipping...", "path", c.FilePath)
|
||||
return false, nil
|
||||
err = handleCertificateAction(cert)
|
||||
if err != nil {
|
||||
logger.Error("post certificate change command failed", "cert-id", cert.Name, "error", err)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Info("Certificate updated successfully", "cert-id", cert.Name)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// readFromDisk reads file data from disk and populates the data []byte field.
|
||||
//
|
||||
// Returns error or nil on success
|
||||
func (c *GenericCertificate) readFromDisk() error {
|
||||
filebytes, err := os.ReadFile(c.FilePath)
|
||||
func getCertFromFile(path string) ([]byte, error) {
|
||||
filebytes, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return err
|
||||
return []byte{}, err
|
||||
} else {
|
||||
return fmt.Errorf("failed to read file from disk: %w", err)
|
||||
return []byte{}, fmt.Errorf("failed to read certificate file on disk: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
c.diskBytes = filebytes
|
||||
return nil
|
||||
return filebytes, nil
|
||||
}
|
||||
|
||||
// needsRollout checks the data []bytes against the data on disk.
|
||||
//
|
||||
// Returns true if file needs rollout, false if not
|
||||
func (c *GenericCertificate) needsRollout(logger *slog.Logger) (bool, error) {
|
||||
err := c.readFromDisk()
|
||||
func checkCertIsDifferent(logger *slog.Logger, path string, data []byte) (bool, error) {
|
||||
filebytes, err := getCertFromFile(path)
|
||||
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return true, nil
|
||||
} else {
|
||||
return false, fmt.Errorf("failed to compare data to file on disk: %w", err)
|
||||
return false, fmt.Errorf("failed to compare certificates: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
diskHash := sha256.Sum256(c.diskBytes)
|
||||
serverHash := sha256.Sum256(c.serverBytes)
|
||||
existingSha256 := sha256.Sum256(filebytes)
|
||||
newSha256 := sha256.Sum256(data)
|
||||
|
||||
hashesAreDifferent := diskHash != serverHash
|
||||
if hashesAreDifferent {
|
||||
logger.Debug("File on disk differs from server source", "path", c.FilePath)
|
||||
sumsAreDifferent := existingSha256 != newSha256
|
||||
if sumsAreDifferent {
|
||||
logger.Debug("Certificate on file differs from the certificate on the server", "cert-path", path)
|
||||
} else {
|
||||
logger.Debug("File on disk is identical to server source", "path", c.FilePath)
|
||||
logger.Debug("Certificate on file is identical to the certificate on the server", "cert-path", path)
|
||||
}
|
||||
|
||||
return hashesAreDifferent, nil
|
||||
return sumsAreDifferent, nil
|
||||
}
|
||||
|
||||
// writeToDisk flushes the certificate data to disk.
|
||||
//
|
||||
// Returns error or nil on success.
|
||||
func (c *GenericCertificate) writeToDisk(logger *slog.Logger) error {
|
||||
func updateCertOnFS(logger *slog.Logger, path string, data []byte) error {
|
||||
if configuration.DryRun {
|
||||
logger.Debug("DRY-RUN: writing data to file", "path", c.FilePath)
|
||||
logger.Debug("DRY-RUN: writing certificate data to file", "cert-path", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
file, err := os.Create(c.FilePath)
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to open file for writing: %w", err)
|
||||
return fmt.Errorf("failed to open certificate for writing: %w", err)
|
||||
}
|
||||
|
||||
defer func(l *slog.Logger) {
|
||||
if err := file.Close(); err != nil {
|
||||
l.Error("failed to close file", "path", c.FilePath, "error", err)
|
||||
l.Error("failed to close file", "file-path", path, "error", err)
|
||||
}
|
||||
}(logger)
|
||||
|
||||
w := bufio.NewWriter(file)
|
||||
|
||||
if _, err := w.Write(c.serverBytes); err != nil {
|
||||
return fmt.Errorf("failed to write data to file: %w", err)
|
||||
if _, err := w.Write(data); err != nil {
|
||||
return fmt.Errorf("failed to write certificate data to file: %w", err)
|
||||
}
|
||||
|
||||
if err = w.Flush(); err != nil {
|
||||
return fmt.Errorf("failed to flush data to file: %w", err)
|
||||
return fmt.Errorf("failed to flush certificate data to file: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("Successfully wrote to file", "path", c.FilePath)
|
||||
logger.Debug("wrote certificate to file", "file-path", path)
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchFromServer fetches the cert/key data from the CertWarden server and
|
||||
// fills the serverBytes field.
|
||||
//
|
||||
// Returns error or nil on success.
|
||||
func (c *GenericCertificate) fetchFromServer(logger *slog.Logger, baseUrl string, skipInsecure bool) error {
|
||||
var url string
|
||||
var fileType string
|
||||
if c.IsKey {
|
||||
url = baseUrl + constants.KeyApiPath + c.Name
|
||||
fileType = "privatekey"
|
||||
} else {
|
||||
url = baseUrl + constants.CertificateApiPath + c.Name
|
||||
fileType = "certificate"
|
||||
}
|
||||
|
||||
logger.Debug("Data request URL: "+url, "file-type", fileType)
|
||||
func getCertFromServer(logger *slog.Logger, certName string, certKey string, baseUrl string, skipInsecure bool) ([]byte, error) {
|
||||
url := baseUrl + constants.CertificateApiPath + certName
|
||||
logger.Debug("Certificate request URL: " + url)
|
||||
var transport http.RoundTripper
|
||||
|
||||
if skipInsecure {
|
||||
logger.Debug("Upstream Server TLS Certificate Validation is disabled")
|
||||
logger.Debug("TLS Certificate Validation is disabled")
|
||||
transport = &http.Transport{
|
||||
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
|
||||
}
|
||||
} else {
|
||||
logger.Debug("Upstream Server HTTP TLS Certificate Validation is enabled")
|
||||
logger.Debug("TLS Certificate Validation is enabled")
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
|
@ -222,15 +152,17 @@ func (c *GenericCertificate) fetchFromServer(logger *slog.Logger, baseUrl string
|
|||
}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to prepare to request data from server: %w", err)
|
||||
return []byte{}, fmt.Errorf("failed to prepare to request certificate from server: %w", err)
|
||||
}
|
||||
|
||||
req.Header.Set("User-Agent", constants.UserAgent)
|
||||
req.Header.Add(constants.ApiKeyHeaderName, c.Secret)
|
||||
req.Header.Add(constants.ApiKeyHeaderName, certKey)
|
||||
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to request data from server: %w", err)
|
||||
e := fmt.Errorf("failed to request certificate from server: %w", err)
|
||||
sentry.CaptureException(e)
|
||||
return []byte{}, e
|
||||
}
|
||||
|
||||
defer func(l *slog.Logger) {
|
||||
|
@ -240,30 +172,33 @@ func (c *GenericCertificate) fetchFromServer(logger *slog.Logger, baseUrl string
|
|||
}(logger)
|
||||
|
||||
if res.StatusCode == http.StatusUnauthorized {
|
||||
logger.Error("API-Key for request is invalid, skipping certificate!", "name", c.Name, "file-type", fileType)
|
||||
return errors.New("API-Key invalid")
|
||||
logger.Error("API-Key for Certificate is invalid, skipping certificate!", "cert-id", certName)
|
||||
return []byte{}, errors.New("API-Key invalid")
|
||||
} else if res.StatusCode != http.StatusOK {
|
||||
logger.Error("failed to get data from server", "name", c.Name, "http-response", res.Status, "file-type", fileType)
|
||||
logger.Error("failed to get certificate from server", "cert-id", certName, "http-response", res.Status)
|
||||
}
|
||||
|
||||
bodyBytes, err := io.ReadAll(res.Body)
|
||||
body, err := io.ReadAll(res.Body)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read response from server: %w", err)
|
||||
e := fmt.Errorf("failed to read certificate response from server: %w", err)
|
||||
sentry.CaptureException(e)
|
||||
return []byte{}, e
|
||||
}
|
||||
|
||||
c.serverBytes = bodyBytes
|
||||
return nil
|
||||
return body, nil
|
||||
}
|
||||
|
||||
// handleCertificateAction executes the user-defined action after successful certificate deployment
|
||||
func handleCertificateAction(action string) error {
|
||||
if action == "" {
|
||||
func handleCertificateAction(cert configuration.CertificateData) error {
|
||||
if cert.Action == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
action := strings.ReplaceAll(cert.Action, "{name}", cert.Name)
|
||||
action = strings.ReplaceAll(action, "{path}", cert.FilePath)
|
||||
|
||||
sargs := strings.Split(action, " ")
|
||||
|
||||
cmd := exec.Command(sargs[0], sargs[1:]...)
|
||||
cmd := exec.Command(sargs[0], sargs...)
|
||||
err := cmd.Run()
|
||||
return err
|
||||
}
|
||||
|
|
1
internal/certificates/certificates_test.go
Normal file
1
internal/certificates/certificates_test.go
Normal file
|
@ -0,0 +1 @@
|
|||
package certificates
|
|
@ -1,18 +0,0 @@
|
|||
package certificates
|
||||
|
||||
// GenericCertificate is a generic container to enable us to
|
||||
// handle both certificates and keys with one function
|
||||
type GenericCertificate struct {
|
||||
Name string
|
||||
FilePath string
|
||||
Secret string
|
||||
|
||||
// True if key, false if certificate
|
||||
IsKey bool
|
||||
|
||||
// Bytes fetched from the server
|
||||
serverBytes []byte
|
||||
|
||||
// Bytes fetched from disk
|
||||
diskBytes []byte
|
||||
}
|
|
@ -7,6 +7,7 @@ import (
|
|||
"code.lila.network/adoralaura/certwarden-deploy/internal/certificates"
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/configuration"
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/constants"
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/errlog"
|
||||
"code.lila.network/adoralaura/certwarden-deploy/internal/logger"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
@ -30,15 +31,13 @@ func handleRootCmd(cmd *cobra.Command, args []string) {
|
|||
slog.Error("failed to initialize config", "error", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
log := logger.Initialize()
|
||||
config.SubstituteKeys(log)
|
||||
|
||||
validation := config.IsValid()
|
||||
if validation.HasMessages() {
|
||||
validation.Print(log)
|
||||
slog.Error("The configuration file has errors! Application cannot start unless all errors are corrected!")
|
||||
os.Exit(1)
|
||||
log := logger.InitializeLogger()
|
||||
err = errlog.SetupSentry(log, config.Sentry.DSN)
|
||||
if err != nil {
|
||||
slog.Error("failed to initialize sentry", "error", err)
|
||||
}
|
||||
|
||||
configuration.ValidateConfig(log, *config)
|
||||
|
||||
certificates.HandleCertificates(log, config)
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package configuration
|
||||
|
||||
import "log/slog"
|
||||
|
||||
// Config file gets read into here
|
||||
var Config *ConfigFileData
|
||||
|
||||
|
@ -24,37 +22,18 @@ var Force bool
|
|||
type ConfigFileData struct {
|
||||
BaseURL string `yaml:"base_url"`
|
||||
DisableCertificateValidation bool `yaml:"disable_certificate_validation"`
|
||||
Sentry SentryData `yaml:"sentry,omitempty"`
|
||||
Certificates []CertificateData `yaml:"certificates"`
|
||||
}
|
||||
|
||||
// Struct that holds the details of a single managed certificate
|
||||
type CertificateData struct {
|
||||
Name string `yaml:"name"`
|
||||
CertificateSecret string `yaml:"cert_secret"`
|
||||
CertificatePath string `yaml:"cert_path"`
|
||||
KeySecret string `yaml:"key_secret"`
|
||||
KeyPath string `yaml:"key_path"`
|
||||
ApiKey string `yaml:"api_key"`
|
||||
Action string `yaml:"action"`
|
||||
FilePath string `yaml:"file_path"`
|
||||
}
|
||||
|
||||
type ConfigValidationError struct {
|
||||
ErrorMessages []string
|
||||
}
|
||||
|
||||
func (e *ConfigValidationError) Error() string {
|
||||
return "Configuration file has errors! Application cannot start unless the errors are corrected."
|
||||
}
|
||||
|
||||
func (e *ConfigValidationError) Add(msg string) {
|
||||
e.ErrorMessages = append(e.ErrorMessages, msg)
|
||||
}
|
||||
|
||||
func (e *ConfigValidationError) HasMessages() bool {
|
||||
return len(e.ErrorMessages) != 0
|
||||
}
|
||||
|
||||
func (e *ConfigValidationError) Print(logger *slog.Logger) {
|
||||
for _, line := range e.ErrorMessages {
|
||||
logger.Error(line)
|
||||
}
|
||||
type SentryData struct {
|
||||
DSN string `yaml:"dsn"`
|
||||
}
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (c *ConfigFileData) SubstituteKeys(logger *slog.Logger) {
|
||||
for index, cert := range c.Certificates {
|
||||
c.Certificates[index].CertificatePath = strings.ReplaceAll(cert.CertificatePath, "{name}", c.Certificates[index].Name)
|
||||
c.Certificates[index].KeyPath = strings.ReplaceAll(cert.KeyPath, "{name}", c.Certificates[index].Name)
|
||||
|
||||
c.Certificates[index].Action = strings.ReplaceAll(cert.Action, "{name}", c.Certificates[index].Name)
|
||||
c.Certificates[index].Action = strings.ReplaceAll(c.Certificates[index].Action, "{cert_path}", c.Certificates[index].CertificatePath)
|
||||
c.Certificates[index].Action = strings.ReplaceAll(c.Certificates[index].Action, "{key_path}", c.Certificates[index].KeyPath)
|
||||
}
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestStringSubstitutionWithPlaceholders tests the string substitution feature.
|
||||
// It ensures that {name}, {cert_path} and {key_path} get substituted correctly.
|
||||
func TestStringSubstitutionWithPlaceholders(t *testing.T) {
|
||||
cert := CertificateData{
|
||||
Name: "qwer",
|
||||
CertificatePath: "/fake/path/{name}",
|
||||
KeyPath: "/fake/path/{name}-key",
|
||||
Action: "./fake action {cert_path} {key_path}",
|
||||
}
|
||||
|
||||
cfg := ConfigFileData{
|
||||
Certificates: []CertificateData{cert},
|
||||
}
|
||||
|
||||
cfg.SubstituteKeys(nil)
|
||||
|
||||
if cfg.Certificates[0].CertificatePath != "/fake/path/qwer" {
|
||||
t.Fail()
|
||||
t.Logf(`CertificatePath = %q, want "/fake/path/qwer"`, cfg.Certificates[0].CertificatePath)
|
||||
}
|
||||
if cfg.Certificates[0].KeyPath != "/fake/path/qwer-key" {
|
||||
t.Fail()
|
||||
t.Logf(`KeyPath = %q, want "/fake/path/qwer-key"`, cfg.Certificates[0].KeyPath)
|
||||
}
|
||||
if cfg.Certificates[0].Action != "./fake action /fake/path/qwer /fake/path/qwer-key" {
|
||||
t.Fail()
|
||||
t.Logf(`Action = %q, want "./fake action /fake/path/qwer /fake/path/qwer-key"`, cfg.Certificates[0].Action)
|
||||
}
|
||||
}
|
||||
|
||||
// TestStringSubstitutionWithPlaceholders tests the string substitution feature.
|
||||
// It ensures that if no substitutes are present, the config values are not changed.
|
||||
func TestStringSubstitutionWithoutPlaceholders(t *testing.T) {
|
||||
cert := CertificateData{
|
||||
Name: "qwer",
|
||||
CertificatePath: "/fake/path/asd",
|
||||
KeyPath: "/fake/path/asdf-key",
|
||||
Action: "./fake action abcd efgh",
|
||||
}
|
||||
|
||||
cfg := ConfigFileData{
|
||||
Certificates: []CertificateData{cert},
|
||||
}
|
||||
|
||||
cfg.SubstituteKeys(nil)
|
||||
|
||||
if cfg.Certificates[0].CertificatePath != "/fake/path/asd" {
|
||||
t.Fail()
|
||||
t.Logf(`CertificatePath = %q, want "/fake/path/asd"`, cfg.Certificates[0].CertificatePath)
|
||||
}
|
||||
if cfg.Certificates[0].KeyPath != "/fake/path/asdf-key" {
|
||||
t.Fail()
|
||||
t.Logf(`KeyPath = %q, want "/fake/path/asdf-key"`, cfg.Certificates[0].KeyPath)
|
||||
}
|
||||
if cfg.Certificates[0].Action != "./fake action abcd efgh" {
|
||||
t.Fail()
|
||||
t.Logf(`Action = %q, want "./fake action abcd efgh"`, cfg.Certificates[0].Action)
|
||||
}
|
||||
}
|
|
@ -1,38 +1,45 @@
|
|||
package configuration
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"os"
|
||||
"regexp"
|
||||
)
|
||||
|
||||
// IsValid tests if the config read from file has all required parameters set.
|
||||
//
|
||||
// Exits the app if errors are detected
|
||||
func (c *ConfigFileData) IsValid() ConfigValidationError {
|
||||
err := ConfigValidationError{}
|
||||
func ValidateConfig(logger *slog.Logger, config ConfigFileData) {
|
||||
validationFailed := false
|
||||
|
||||
if c.BaseURL == "" {
|
||||
err.Add(`Field 'base_url' in config file is required!`)
|
||||
if config.BaseURL == "" {
|
||||
logger.Error(`Field 'base_url' in config file is required!`)
|
||||
validationFailed = true
|
||||
}
|
||||
|
||||
for _, cert := range c.Certificates {
|
||||
for _, cert := range config.Certificates {
|
||||
if cert.Name == "" {
|
||||
cert.Name = "unnamed_certificate"
|
||||
err.Add(`Field 'name' for certificates cannot be blank!`)
|
||||
logger.Error(`Field 'name' for certificates cannot be blank!`)
|
||||
validationFailed = true
|
||||
}
|
||||
|
||||
if cert.CertificateSecret == "" {
|
||||
err.Add(`Field 'cert_secret' for certificate ` + cert.Name + " cannot be blank!")
|
||||
if cert.ApiKey == "" {
|
||||
logger.Error(`Field 'api_key' for certificate ` + cert.Name + " cannot be blank!")
|
||||
validationFailed = true
|
||||
}
|
||||
|
||||
if cert.CertificatePath == "" {
|
||||
err.Add(`Field 'cert_path' for certificate ` + cert.Name + " cannot be blank!")
|
||||
if cert.FilePath == "" {
|
||||
logger.Error(`Field 'file_path' for certificate ` + cert.Name + " cannot be blank!")
|
||||
validationFailed = true
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`^[a-zA-Z0-9._-]+$`)
|
||||
if !re.MatchString(cert.Name) {
|
||||
err.Add(`Field 'name' for certificate may only contain -_. and alphanumeric characters!`)
|
||||
logger.Error(`Field 'name' for certificate may only contain -_. and alphanumeric characters!`)
|
||||
validationFailed = true
|
||||
}
|
||||
}
|
||||
|
||||
return err
|
||||
if validationFailed {
|
||||
logger.Error("Config file has errors! Please fix errors above! Exiting...", "config-path", ConfigFile)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package constants
|
||||
|
||||
var Version string
|
||||
|
||||
var UserAgent = "certwarden-deploy/" + Version + " +https://code.lila.network/adoralaura/certwarden-deploy"
|
||||
|
||||
const Version = "0.1.0"
|
||||
const CertificateApiPath = "/certwarden/api/v1/download/certificates/"
|
||||
const KeyApiPath = "/certwarden/api/v1/download/privatekeys/"
|
||||
const ApiKeyHeaderName = "X-API-Key"
|
||||
const UserAgent = "certwarden-deploy/" + Version + " +https://code.lila.network/adoralaura/certwarden-deploy"
|
||||
|
|
27
internal/errlog/sentry.go
Normal file
27
internal/errlog/sentry.go
Normal file
|
@ -0,0 +1,27 @@
|
|||
package errlog
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
|
||||
"github.com/getsentry/sentry-go"
|
||||
)
|
||||
|
||||
func SetupSentry(logger *slog.Logger, dsn string) error {
|
||||
if dsn == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
err := sentry.Init(sentry.ClientOptions{
|
||||
Dsn: dsn,
|
||||
// Set TracesSampleRate to 1.0 to capture 100%
|
||||
// of transactions for performance monitoring.
|
||||
// We recommend adjusting this value in production,
|
||||
TracesSampleRate: 1.0,
|
||||
})
|
||||
if err != nil {
|
||||
logger.Error("failed to set up sentry")
|
||||
}
|
||||
// Flush buffered events before the program terminates.
|
||||
|
||||
return nil
|
||||
}
|
|
@ -8,12 +8,13 @@ import (
|
|||
"code.lila.network/adoralaura/certwarden-deploy/internal/configuration"
|
||||
)
|
||||
|
||||
// Initialize initializes a *slog.Logger with the right log level and options.
|
||||
func Initialize() *slog.Logger {
|
||||
func InitializeLogger() *slog.Logger {
|
||||
logLevel := slog.LevelInfo
|
||||
sourceLogging := false
|
||||
|
||||
if configuration.VerboseLogging {
|
||||
logLevel = slog.LevelDebug
|
||||
sourceLogging = true
|
||||
}
|
||||
if configuration.QuietLogging {
|
||||
logLevel = slog.LevelError
|
||||
|
@ -24,6 +25,7 @@ func Initialize() *slog.Logger {
|
|||
|
||||
opts := &slog.HandlerOptions{
|
||||
Level: logLevel,
|
||||
AddSource: sourceLogging,
|
||||
}
|
||||
|
||||
handler := slog.NewTextHandler(os.Stdout, opts)
|
||||
|
|
11
main.go
Normal file
11
main.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Copyright © 2024 Laura Kalb <dev@lauka.net>
|
||||
The code of this project is available under the MIT license. See the LICENSE file for more info.
|
||||
*/
|
||||
package main
|
||||
|
||||
import cmd "code.lila.network/adoralaura/certwarden-deploy/cmd/certwarden-deploy"
|
||||
|
||||
func main() {
|
||||
cmd.Execute()
|
||||
}
|
Loading…
Reference in a new issue