Skip to content
This repository was archived by the owner on Mar 16, 2024. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions docs/docs/30-installation/02-options.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,6 @@ If you add this flag, you'll be prompted during install to agree to Let's Encryp
acorn install --lets-encrypt enabled --lets-encrypt-tos-agree=true --lets-encrypt-email <your email>
```

:::note
This feature currently only secures acorn-generated endpoints. If you are configuring custom endpoints for your acorn apps, see our [section on configuring TLS Certificates](/running/certificates).
:::

:::info
Let's Encrypt integration is only useful if you are running a non-local Kubernetes cluster. If you are running acorn on a local cluster such as Docker Desktop, Rancher Desktop, or minikube, enabling Let's Encrypt will have no effect. We don't issue certificates for the `.local.on-acorn.io` domains that are used in this scenario.
:::
Expand Down
8 changes: 3 additions & 5 deletions docs/docs/50-running/03-certificates.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@
title: TLS Certificates
---

Applications that publish HTTP endpoints can be protected by TLS certificates. This page describes the process for securing custom endpoints. Acorn-generated endpoints (ones ending in `on-acorn.io`) can be automatically secured via our [Let's Encrypt integration](/installation/options#tls-via-lets-encrypt). In the future, Acorn will provide a similar mechanism to secure custom endpoints as well, but for now custom endpoints require the following approach.
Applications that publish HTTP endpoints can be protected by TLS certificates. If you've enabled Acorn's [Let's Encrypt integration](/installation/options#tls-via-lets-encrypt), a valid certificate will be provisioned for your app's endpoints. This applies to on-acorn.io generated endpoints and custom endpoints configured using the [publish flag](/running/networking#publish-individual-ports).

## Manually adding certificates
If you don't wish to use Acorn's Let's Encrypt integration, you can configure certificates manually or by integrating with cert-manager. Acorn will automatically look for SANs in secrets of type `kubernetes.io/tls` for the exposed FQDN of the application in the Acorn namespace.

Acorn will automatically look for SANs in secrets of type `kubernetes.io/tls` for the
exposed FQDN of the application in the Acorn namespace.

Assume you are deploying an app and plan to host on `my-app.example.com`
The following examples assume you are deploying an app and plan to host on `my-app.example.com`

### Add existing certificates using kubectl

Expand Down
2 changes: 1 addition & 1 deletion docs/docs/50-running/40-upgrades.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Upgrades
title: Manual Upgrades
---

When upgrading an Acorn app, you do not need to pass in all arguments on every update. The args are persisted between runs and only the passed in updates are changed.
Expand Down
48 changes: 48 additions & 0 deletions docs/docs/50-running/45-auto-upgrades.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
title: Automatic Upgrades
---
You can configure Acorn apps to automatically upgrade when a new version of the Acorn image they are using is available.

Automatic upgrade for an app will be enabled if `#`, `*`, or `**` appears in the image's tag as part of the run command. Tags will sorted according to the rules for these special characters described below. The newest tag will be selected for upgrade.

`#` denotes a segment of the image tag that should be sorted numerically when finding the newest tag.

This example deploys the hello-world app with auto-upgrade enabled and matching all major, minor, and patch versions:
```shell
acorn run myorg/hello-world:v#.#.#
```

`*` denotes a segment of the image tag that should sorted alphabetically when finding the latest tag.

In this example, if you had a tag named alpha and a tag named zeta, zeta would be recognized as the newest:
```shell
acorn run myorg/hello-world:*
```


`**` denotes a wildcard. This segment of the image tag won't be considered when sorting. This is useful if your tags have a segment that is unpredictable.

This example would sort numerically according to major and minor version (ie v1.2) and ignore anything following the "-":

```shell
acorn run myorg/hello-world:v#.#-**
```

Automatic upgrades can be configured explicitly via a flag.

In this example, the tag will always be "latest", but acorn will periodically check to see if new content has been pushed to that tag:
```shell
acorn run --auto-upgrade myorg/hello-world:latest
```

To have acorn notify you that an app has an upgrade available and require confirmation before proceeding, set the notify-upgrade flag:
```shell
acorn run --notify-upgrade myorg/hello-world:v#.#.# myapp

```
To proceed with an upgrade you've been notified of:
```shell
acorn update --confirm-upgrade myapp
```

New image versions are checked for on an interval. You can control the default interval via the install command and the the `--auto-upgrade-interval` flag. You can control the interal on a per app basis as part of the run command by specifying the `--interval` flag.
23 changes: 17 additions & 6 deletions pkg/controller/tls/certs.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"strings"

v1 "github.com/acorn-io/acorn/pkg/apis/internal.acorn.io/v1"
"github.com/acorn-io/acorn/pkg/config"
"github.com/acorn-io/acorn/pkg/labels"
"github.com/acorn-io/acorn/pkg/system"
"github.com/acorn-io/baaah/pkg/router"
Expand All @@ -20,7 +21,7 @@ import (

// ProvisionWildcardCert provisions a Let's Encrypt wildcard certificate for *.<domain>.on-acorn.io
func ProvisionWildcardCert(req router.Request, domain, token string) error {
logrus.Infof("Provisioning wildcard cert for %v", domain)
logrus.Debugf("Provisioning wildcard cert for %v", domain)
// Ensure that we have a Let's Encrypt account ready
leUser, err := ensureLEUser(req.Ctx, req.Client)
if err != nil {
Expand Down Expand Up @@ -51,16 +52,14 @@ func RequireSecretTypeTLS(h router.Handler) router.Handler {
func RenewCert(req router.Request, resp router.Response) error {
sec := req.Object.(*corev1.Secret)

logrus.Infof("Renewing certificate for %v", sec.Name)

leUser, err := ensureLEUser(req.Ctx, req.Client)
if err != nil {
return err
}

// Early exit if existing cert is still valid
if !leUser.mustRenew(sec) {
logrus.Infof("Certificate for %v is still valid", sec.Name)
logrus.Debugf("Certificate for %v is still valid", sec.Name)
return nil
}

Expand All @@ -70,7 +69,7 @@ func RenewCert(req router.Request, resp router.Response) error {

// Do not start a new challenge if we already have one in progress
if !lockDomain(domain) {
logrus.Infof("not starting certificate renewal: %v: %s", ErrCertificateRequestInProgress, domain)
logrus.Debugf("not starting certificate renewal: %v: %s", ErrCertificateRequestInProgress, domain)
return
}
defer unlockDomain(domain)
Expand Down Expand Up @@ -109,6 +108,18 @@ func RenewCert(req router.Request, resp router.Response) error {
// Note: this does not actually provision the certificates, it just creates the empty secret
// which is picked up by the route handled by RenewCert above
func ProvisionCerts(req router.Request, resp router.Response) error {

cfg, err := config.Get(req.Ctx, req.Client)
if err != nil {
return err
}

// Early exit if Let's Encrypt is not enabled
// Just to be on the safe side, we check for all possible allowed configuration values
if strings.EqualFold(*cfg.LetsEncrypt, "disabled") {
return nil
}

appInstance := req.Object.(*v1.AppInstance)

appInstanceIDSegment := strings.SplitN(string(appInstance.GetUID()), "-", 2)[0]
Expand Down Expand Up @@ -150,7 +161,7 @@ func (u *LEUser) provisionCertIfNotExists(ctx context.Context, client kclient.Cl
go func() {
// Do not start a new challenge if we already have one in progress
if !lockDomain(domain) {
logrus.Infof("not starting certificate renewal: %v: %s", ErrCertificateRequestInProgress, domain)
logrus.Debugf("not starting certificate renewal: %v: %s", ErrCertificateRequestInProgress, domain)
return
}
defer unlockDomain(domain)
Expand Down
8 changes: 3 additions & 5 deletions pkg/controller/tls/letsencrypt.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,6 @@ func (u *LEUser) register() error {
}
u.registration = reg

logrus.Infof("registered LE User: %s", u.email)

return nil

}
Expand Down Expand Up @@ -268,7 +266,7 @@ func ensureLEUser(ctx context.Context, client kclient.Client) (*LEUser, error) {
return nil, fmt.Errorf("problem creating Let's Encrypt User secret: %w", err)
}

logrus.Infoln("Registered Let's Encrypt User")
logrus.Infof("Registered Let's Encrypt User: %s", newLEUser.email)

return newLEUser, nil

Expand Down Expand Up @@ -321,7 +319,7 @@ func lockDomain(domain string) bool {
CertificatesRequestLock.Lock()
if _, ok := CertificateRequests[domain]; ok {
CertificatesRequestLock.Unlock()
logrus.Infof("certificate for domain %s is already being requested, waiting for it to be ready", domain)
logrus.Debugf("certificate for domain %s is already being requested, waiting for it to be ready", domain)
return false
}

Expand Down Expand Up @@ -358,7 +356,7 @@ func stillValid(cert []byte) bool {
timeToExpire := x509crt.NotAfter.Sub(time.Now().UTC())
if timeToExpire > 7*24*time.Hour {
// (b) cert is still valid for more than 7 days -> good to go
logrus.Infof("certificate for %s is still valid until %s (%d hours)", x509crt.Subject.CommonName, x509crt.NotAfter, int(timeToExpire.Hours()))
logrus.Debugf("certificate for %s is still valid until %s (%d hours)", x509crt.Subject.CommonName, x509crt.NotAfter, int(timeToExpire.Hours()))
return true
} else {
// (c) cert is expired -> renew
Expand Down