From c86db09cd82ddea83b44b497b4750b85b0ba67e2 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Fri, 12 Aug 2022 17:37:29 +0200 Subject: [PATCH] event manager: add Certificate renewal trigger Signed-off-by: Nicola Murino --- docker/README.md | 7 +- docs/eventmanager.md | 2 + go.mod | 42 +++++----- go.sum | 80 +++++++++---------- internal/acme/acme.go | 31 +++++++- internal/common/defenderdb.go | 1 + internal/common/defendermem.go | 1 + internal/common/eventmanager.go | 58 +++++++++++--- internal/common/protocol_test.go | 119 ++++++++++++++++++++++++++++ internal/dataprovider/eventrule.go | 17 ++-- internal/httpd/server.go | 2 +- openapi/openapi.yaml | 8 ++ templates/webadmin/eventaction.html | 2 +- templates/webadmin/eventrule.html | 2 + 14 files changed, 290 insertions(+), 82 deletions(-) diff --git a/docker/README.md b/docker/README.md index d5490fa8..f4dc82de 100644 --- a/docker/README.md +++ b/docker/README.md @@ -206,4 +206,9 @@ These tags provide the standard image with the addition of all "official" plugin ## Helm Chart -An helm chart is [available](https://artifacthub.io/packages/helm/sagikazarmark/sftpgo). You can find the source code [here](https://github.com/sagikazarmark/helm-charts/tree/master/charts/sftpgo). +Some helm charts are available: + +- [sagikazarmark/sftpgo](https://artifacthub.io/packages/helm/sagikazarmark/sftpgo) +- [truecharts/sftpgo](https://artifacthub.io/packages/helm/truecharts/sftpgo) + +These charts are not maintained by the SFTPGo project and any issues with the charts should be raised to the upstream repo. diff --git a/docs/eventmanager.md b/docs/eventmanager.md index 824c1338..fc3c0028 100644 --- a/docs/eventmanager.md +++ b/docs/eventmanager.md @@ -41,6 +41,7 @@ The following trigger events are supported: - `Provider events`, for example `add`, `update`, `delete` user or other resources. - `Schedules`. - `IP Blocked`, this event can be generated if you enable the [defender](./defender.md). +- `Certificate`, this event is generated when a certificate is renewed using the built-in ACME protocol. Both successful and failed renewals are notified. You can further restrict a rule by specifying additional conditions that must be met before the rule’s actions are taken. For example you can react to uploads only if they are performed by a particular user or using a specified protocol. @@ -60,3 +61,4 @@ Some actions are not supported for some triggers, rules containing incompatible - `Provider events`, user quota reset, transfer quota reset, data retention check and filesystem actions can be executed only if we modify a user. They will be executed for the affected user. Folder quota reset can be executed only for folders. Filesystem actions are not executed for `delete` user events because the actions is executed after the user deletion. - `Schedules`, filesystem actions cannot be executed, they require a user. - `IP Blocked`, user quota reset, folder quota reset, transfer quota reset, data retention check and filesystem actions cannot be executed, we only have an IP. +- `Certificate`, user quota reset, folder quota reset, transfer quota reset, data retention check and filesystem actions cannot be executed. diff --git a/go.mod b/go.mod index 53a868e0..7fcdf339 100644 --- a/go.mod +++ b/go.mod @@ -8,15 +8,15 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.4.1 github.com/GehirnInc/crypt v0.0.0-20200316065508-bb7000b8a962 github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387 - github.com/aws/aws-sdk-go-v2 v1.16.10 - github.com/aws/aws-sdk-go-v2/config v1.16.0 - github.com/aws/aws-sdk-go-v2/credentials v1.12.12 - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.11 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.24 - github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.11 - github.com/aws/aws-sdk-go-v2/service/s3 v1.27.4 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.16 - github.com/aws/aws-sdk-go-v2/service/sts v1.16.12 + github.com/aws/aws-sdk-go-v2 v1.16.11 + github.com/aws/aws-sdk-go-v2/config v1.16.1 + github.com/aws/aws-sdk-go-v2/credentials v1.12.13 + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.25 + github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.12 + github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.17 + github.com/aws/aws-sdk-go-v2/service/sts v1.16.13 github.com/cockroachdb/cockroach-go/v2 v2.2.15 github.com/coreos/go-oidc/v3 v3.2.0 github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 @@ -66,7 +66,7 @@ require ( go.uber.org/automaxprocs v1.5.1 gocloud.dev v0.26.0 golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa - golang.org/x/net v0.0.0-20220809184613-07c6da5e1ced + golang.org/x/net v0.0.0-20220811182439-13a9a731de15 golang.org/x/oauth2 v0.0.0-20220808172628-8227340efae7 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 @@ -81,15 +81,15 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0 // indirect github.com/ajg/form v1.5.1 // indirect github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.17 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.11 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.3.18 // indirect - github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.8 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.12 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.11 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.11 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.11.15 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.3.19 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.9 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.5 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.13 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.12 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.12 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.11.16 // indirect github.com/aws/smithy-go v1.12.1 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/boombuler/barcode v1.0.1 // indirect @@ -155,7 +155,7 @@ require ( golang.org/x/tools v0.1.12 // indirect golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/genproto v0.0.0-20220810155839-1856144b1d9c // indirect + google.golang.org/genproto v0.0.0-20220812140447-cec7f5303424 // indirect google.golang.org/grpc v1.48.0 // indirect google.golang.org/protobuf v1.28.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect @@ -168,5 +168,5 @@ replace ( github.com/jlaffaye/ftp => github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 github.com/pkg/sftp => github.com/drakkan/sftp v0.0.0-20220716075551-51a5aa4e044d golang.org/x/crypto => github.com/drakkan/crypto v0.0.0-20220723143649-81550382d55e - golang.org/x/net => github.com/drakkan/net v0.0.0-20220811173512-bde04f9047cc + golang.org/x/net => github.com/drakkan/net v0.0.0-20220812153436-025c6c7680ee ) diff --git a/go.sum b/go.sum index f2620424..d00ad533 100644 --- a/go.sum +++ b/go.sum @@ -144,64 +144,64 @@ github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZo github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= github.com/aws/aws-sdk-go v1.43.31/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo= github.com/aws/aws-sdk-go-v2 v1.16.2/go.mod h1:ytwTPBG6fXTZLxxeeCCWj2/EMYp/xDUgX+OET6TLNNU= -github.com/aws/aws-sdk-go-v2 v1.16.10 h1:+yDD0tcuHRQZgqONkpDwzepqmElQaSlFPymHRHR9mrc= -github.com/aws/aws-sdk-go-v2 v1.16.10/go.mod h1:WTACcleLz6VZTp7fak4EO5b9Q4foxbn+8PIz3PmyKlo= +github.com/aws/aws-sdk-go-v2 v1.16.11 h1:xM1ZPSvty3xVmdxiGr7ay/wlqv+MWhH0rMlyLdbC0YQ= +github.com/aws/aws-sdk-go-v2 v1.16.11/go.mod h1:WTACcleLz6VZTp7fak4EO5b9Q4foxbn+8PIz3PmyKlo= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.1/go.mod h1:n8Bs1ElDD2wJ9kCRTczA83gYbBmjSwZp3umc6zF4EeM= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4 h1:zfT11pa7ifu/VlLDpmc5OY2W4nYmnKkFDGeMVnmqAI0= github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.4/go.mod h1:ES0I1GBs+YYgcDS1ek47Erbn4TOL811JKqBXtgzqyZ8= github.com/aws/aws-sdk-go-v2/config v1.15.3/go.mod h1:9YL3v07Xc/ohTsxFXzan9ZpFpdTOFl4X65BAKYaz8jg= -github.com/aws/aws-sdk-go-v2/config v1.16.0 h1:LxHC50cwOLxYo67NEpwpNUiOi6ngXfDpEETphSZ6bAw= -github.com/aws/aws-sdk-go-v2/config v1.16.0/go.mod h1:eatrtwIm5WdvASoYCy5oPkinfiwiYFg2jLG9tJoKzkE= +github.com/aws/aws-sdk-go-v2/config v1.16.1 h1:jasqFPOoNPXHOYGEEuvyT87ACiXhD3OkQckIm5uqi5I= +github.com/aws/aws-sdk-go-v2/config v1.16.1/go.mod h1:4SKzBMiB8lV0fw2w7eDBo/LjQyHFITN4vUUuqpurFmI= github.com/aws/aws-sdk-go-v2/credentials v1.11.2/go.mod h1:j8YsY9TXTm31k4eFhspiQicfXPLZ0gYXA50i4gxPE8g= -github.com/aws/aws-sdk-go-v2/credentials v1.12.12 h1:iShu6VaWZZZfUZvlGtRjl+g1lWk44g1QmiCTD4KS0jI= -github.com/aws/aws-sdk-go-v2/credentials v1.12.12/go.mod h1:vFHC2HifIWHebmoVsfpqliKuqbAY2LaVlvy03JzF4c4= +github.com/aws/aws-sdk-go-v2/credentials v1.12.13 h1:cuPzIsjKAWBUAAk8ZUR2l02Sxafl9hiaMsc7tlnjwAY= +github.com/aws/aws-sdk-go-v2/credentials v1.12.13/go.mod h1:9fDEemXizwXrxPU1MTzv69LP/9D8HVl5qHAQO9A9ikY= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.3/go.mod h1:uk1vhHHERfSVCUnqSqz8O48LBYDSC+k6brng09jcMOk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.11 h1:zZHPdM2x09/0F8D7XyVvQnP2/jaW7bEMmtcSCPYq/iI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.11/go.mod h1:38Asv/UyQbDNpSXCurZRlDMjzIl6J+wUe8vY3TtUuzA= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12 h1:wgJBHO58Pc1V1QAnzdVM3JK3WbE/6eUF0JxCZ+/izz0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.12.12/go.mod h1:aZ4vZnyUuxedC7eD4JyEHpGnCz+O2sHQEx3VvAwklSE= github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.3/go.mod h1:0dHuD2HZZSiwfJSy1FO5bX1hQ1TxVV1QXXjpn3XUE44= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.24 h1:9MflwbI3Ua4PFyCNo39nnJ2ZYaQ/GabPUPdutegSJUs= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.24/go.mod h1:W970x9QKWWb0Y30Num5dFFji/qRQSt0UP4UzbM3sYCo= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.25 h1:ShUxLkMxarXylGxfYwg8p+xEKY+C1y54oUU3wFsUMFo= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.25/go.mod h1:cam5wV1ebd3ZVuh2r2CA8FtSAA/eUMtRH4owk0ygfFs= github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.9/go.mod h1:AnVH5pvai0pAF4lXRq0bmhbes1u9R8wTE+g+183bZNM= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.17 h1:U8DZvyFFesBmK62dYC6BRXm4Cd/wPP3aPcecu3xv/F4= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.17/go.mod h1:6qtGip7sJEyvgsLjphRZWF9qPe3xJf1mL/MM01E35Wc= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18 h1:OmiwoVyLKEqqD5GvB683dbSqxiOfvx4U2lDZhG2Esc4= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.1.18/go.mod h1:348MLhzV1GSlZSMusdwQpXKbhD7X2gbI/TxwAPKkYZQ= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.3/go.mod h1:ssOhaLpRlh88H3UmEcsBoVKq309quMvm3Ds8e9d4eJM= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.11 h1:GMp98usVW5tzQhxd26KWhoNQPlR2noIlfbzqjVGBhLU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.11/go.mod h1:cYAfnB+9ZkmZWpQWmPDsuIGm4EA+6k2ZVtxKjw/XJBY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12 h1:5mvQDtNWtI6H56+E4LUnLWEmATMB7oEh+Z9RurtIuC0= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.12/go.mod h1:ckaCVTEdGAxO6KwTGzgskxR1xM+iJW4lxMyDFVda2Fc= github.com/aws/aws-sdk-go-v2/internal/ini v1.3.10/go.mod h1:8DcYQcz0+ZJaSxANlHIsbbi6S+zMwjwdDqwW3r9AzaE= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.18 h1:/spg6h3tG4pefphbvhpgdMtFMegSajPPSEJd1t8lnpc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.3.18/go.mod h1:hTHq8hL4bAxJyng364s9d4IUGXZOs7Y5LSqAhIiIQ2A= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.8 h1:9PY5a+kHQzC6d9eR+KLNSJP3DHDLYmPFA5/+eSDBo9o= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.8/go.mod h1:pcQfUOFVK4lMnSzgX3dCA81UsA9YCilRUSYgkjSU2i8= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.19 h1:g5qq9sgtEzt2szMaDqQO6fqKe026T6dHTFJp5NsPzkQ= +github.com/aws/aws-sdk-go-v2/internal/ini v1.3.19/go.mod h1:cVHo8KTuHjShb9V8/VjH3S/8+xPu16qx8fdGwmotJhE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.9 h1:agLpf3vtYX1rtKTrOGpevdP3iC2W0hKDmzmhhxJzL+A= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.9/go.mod h1:cv+n1mdyh+0B8tAtlEBzTYFA2Uv15SISEn6kabYhIgE= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.1/go.mod h1:GeUru+8VzrTXV/83XyMJ80KpH8xO89VPoUileyNQ+tc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.4 h1:akfcyqM9SvrBKWZOkBcXAGDrHfKaEP4Aca8H/bCiLW8= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.4/go.mod h1:oehQLbMQkppKLXvpx/1Eo0X47Fe+0971DXC9UjGnKcI= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.5 h1:g1ITJ9i9ixa+/WVggLNK20KyliAA8ltnuxfZEDfo2hM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.5/go.mod h1:oehQLbMQkppKLXvpx/1Eo0X47Fe+0971DXC9UjGnKcI= github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.3/go.mod h1:Seb8KNmD6kVTjwRjVEgOT5hPin6sq+v4C2ycJQDwuH8= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.12 h1:eNQYkKjDSLDjIbBQ85rIkjpBGgnavrl/U3YKDdxAz14= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.12/go.mod h1:k2HaF2yfT082M+kKo3Xdf4rd5HGKvDmrPC5Kwzc2KUw= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.13 h1:3GamN8jcdz/a3nvL/ZVtoH/6xxeshfsiXj5O+6GW4Rg= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.13/go.mod h1:89CSPn69UECDLVn0H6FwKNgbtirksl8C8i3aBeeeihw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.3/go.mod h1:wlY6SVjuwvh3TVRpTqdy4I1JpBFLX4UGeKZdWntaocw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.11 h1:GkYtp4gi4wdWUV+pPetjk5y2aDxbr0t8n5OjVBwZdII= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.11/go.mod h1:OEofCUKF7Hri4ShOCokF6k6hGq9PCB2sywt/9rLSXjY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.12 h1:7iPTTX4SAI2U2VOogD7/gmHlsgnYSgoNHt7MSQXtG2M= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.12/go.mod h1:1TODGhheLWjpQWSuhYuAUWYTCKwEjx2iblIFKDHjeTc= github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.3/go.mod h1:Bm/v2IaN6rZ+Op7zX+bOUMdL4fsrYZiD0dsjLhNKwZc= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.11 h1:ZBLEKweAzBBtJa8H+MTFfVyvo+eHdM8xec5oTm9IlqI= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.11/go.mod h1:mNS1VHxYXPNqxIdCTxf87j9ROfTMa4fNpIkA+iAfz0g= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.12 h1:QFjSOmHSb77qRTv7KI9UFon9X5wLWY5/M+6la3dTcZc= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.13.12/go.mod h1:MADjAN0GHFDuc5lRa5Y5ki+oIO/w7X4qczHy+OUx0IA= github.com/aws/aws-sdk-go-v2/service/kms v1.16.3/go.mod h1:QuiHPBqlOFCi4LqdSskYYAWpQlx3PKmohy+rE2F+o5g= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.11 h1:GFxBWTb0DLD+PkhVPvNWtPsGBFusifSwHb2uDrIV0E0= -github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.11/go.mod h1:jETcaK7szguipGK6ibOHjRemfxelIygcSUZe+xv9Vp8= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.12 h1:KgwQKIp/yb9xCXVb+lZdPwoPLG621v+0bGm7pBJyhIQ= +github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.13.12/go.mod h1:ZlZaygKJuKAxT4OUuoKCVPWil0+QALcb8fZxsMVO1b4= github.com/aws/aws-sdk-go-v2/service/s3 v1.26.3/go.mod h1:g1qvDuRsJY+XghsV6zg00Z4KJ7DtFFCx8fJD2a491Ak= -github.com/aws/aws-sdk-go-v2/service/s3 v1.27.4 h1:0RPAahwT63znFepvhfS+/WYtT+gEuAwaeNcCrzTQMH0= -github.com/aws/aws-sdk-go-v2/service/s3 v1.27.4/go.mod h1:wcpDmROpK5W7oWI6JcJIYGrVpHbF/Pu+FHxyBXyoa1E= +github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5 h1:h9qqTedYnA9JcWjKyLV6UYIMSdp91ExLCUbjbpDLH7A= +github.com/aws/aws-sdk-go-v2/service/s3 v1.27.5/go.mod h1:J8SS5Tp/zeLxaubB0xGfKnVrvssNBNLwTipreTKLhjQ= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.4/go.mod h1:PJc8s+lxyU8rrre0/4a0pn2wgwiDvOEzoOjcJUBr67o= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.16 h1:+8J3OA/fUAAKpSyI6lAPyPhZVleLxDmuT2dv4lVHK20= -github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.16/go.mod h1:vveF0vVbSg0WNZNsi27F0Tbyx9JB8NyExl5Iv0RKLcY= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.17 h1:x4JtJ0TaVVCoNc3bUtv0W5VvMLFiQ1++ReiRfSxRYf8= +github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.15.17/go.mod h1:HvF8QZUW+evBsd/SJn4VA0WWW5qVMKxPpWiRRK4w3eM= github.com/aws/aws-sdk-go-v2/service/sns v1.17.4/go.mod h1:kElt+uCcXxcqFyc+bQqZPFD9DME/eC6oHBXvFzQ9Bcw= github.com/aws/aws-sdk-go-v2/service/sqs v1.18.3/go.mod h1:skmQo0UPvsjsuYYSYMVmrPc1HWCbHUJyrCEp+ZaLzqM= github.com/aws/aws-sdk-go-v2/service/ssm v1.24.1/go.mod h1:NR/xoKjdbRJ+qx0pMR4mI+N/H1I1ynHwXnO6FowXJc0= github.com/aws/aws-sdk-go-v2/service/sso v1.11.3/go.mod h1:7UQ/e69kU7LDPtY40OyoHYgRmgfGM4mgsLYtcObdveU= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.15 h1:HaIE5/TtKr66qZTJpvMifDxH4lRt2JZawbkLYOo1F+Y= -github.com/aws/aws-sdk-go-v2/service/sso v1.11.15/go.mod h1:dDVD4ElJRTQXx7dOQ59EkqGyNU9tnwy1RKln+oLIOTU= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.16 h1:YK8L7TNlGwMWHYqLs+i6dlITpxqzq08FqQUy26nm+T8= +github.com/aws/aws-sdk-go-v2/service/sso v1.11.16/go.mod h1:mS5xqLZc/6kc06IpXn5vRxdLaED+jEuaSRv5BxtnsiY= github.com/aws/aws-sdk-go-v2/service/sts v1.16.3/go.mod h1:bfBj0iVmsUyUg4weDB4NxktD9rDGeKSVWnjTnwbx9b8= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.12 h1:YU9UHPukkCCnETHEExOptF/BxPvGJKXO/NBx+RMQ/2A= -github.com/aws/aws-sdk-go-v2/service/sts v1.16.12/go.mod h1:b53qpmhHk7mTL2J/tfG6f38neZiyBQSiNXGCuNKq4+4= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.13 h1:dl8T0PJlN92rvEGOEUiD0+YPYdPEaCZK0TqHukvSfII= +github.com/aws/aws-sdk-go-v2/service/sts v1.16.13/go.mod h1:Ru3QVMLygVs/07UQ3YDur1AQZZp2tUNje8wfloFttC0= github.com/aws/smithy-go v1.11.2/go.mod h1:3xHYmszWVx2c0kIwQeEVf9uSm4fYZt67FBJnwub1bgM= github.com/aws/smithy-go v1.12.1 h1:yQRC55aXN/y1W10HgwHle01DRuV9Dpf31iGkotjt3Ag= github.com/aws/smithy-go v1.12.1/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA= @@ -266,8 +266,8 @@ github.com/drakkan/crypto v0.0.0-20220723143649-81550382d55e h1:ZvOJ5DqEUZig5lGl github.com/drakkan/crypto v0.0.0-20220723143649-81550382d55e/go.mod h1:SiM6ypd8Xu1xldObYtbDztuUU7xUzMnUULfphXFZmro= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9 h1:LPH1dEblAOO/LoG7yHPMtBLXhQmjaga91/DDjWk9jWA= github.com/drakkan/ftp v0.0.0-20201114075148-9b9adce499a9/go.mod h1:2lmrmq866uF2tnje75wQHzmPXhmSWUt7Gyx2vgK1RCU= -github.com/drakkan/net v0.0.0-20220811173512-bde04f9047cc h1:nWhdNJ31a4S7oBCwIRRPY/QfpOdHl3i3irjrJXrfM7w= -github.com/drakkan/net v0.0.0-20220811173512-bde04f9047cc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +github.com/drakkan/net v0.0.0-20220812153436-025c6c7680ee h1:hTHRVJ//MvApWBRVLrZOCfh+row8txg1G9BJVKsq+qk= +github.com/drakkan/net v0.0.0-20220812153436-025c6c7680ee/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= github.com/drakkan/sftp v0.0.0-20220716075551-51a5aa4e044d h1:kNk/KRhszPJASp7WvjagNW254aKK643Lu8/fr4/ukiM= github.com/drakkan/sftp v0.0.0-20220716075551-51a5aa4e044d/go.mod h1:wHDZ0IZX6JcBYRK1TH9bcVq8G7TLpVHYIGJRFnmPfxg= github.com/eikenb/pipeat v0.0.0-20210730190139-06b3e6902001 h1:/ZshrfQzayqRSBDodmp3rhNCHJCff+utvgBuWRbiqu4= @@ -1226,8 +1226,8 @@ google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljW google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA= -google.golang.org/genproto v0.0.0-20220810155839-1856144b1d9c h1:IooGDWedfLC6KLczH/uduUsKQP42ZZYhKx+zd50L1Sk= -google.golang.org/genproto v0.0.0-20220810155839-1856144b1d9c/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= +google.golang.org/genproto v0.0.0-20220812140447-cec7f5303424 h1:zZnTt15U44/Txe/9cN/tVbteBkPMiyXK48hPsKRmqj4= +google.golang.org/genproto v0.0.0-20220812140447-cec7f5303424/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= diff --git a/internal/acme/acme.go b/internal/acme/acme.go index 82b20fc3..c17c3d08 100644 --- a/internal/acme/acme.go +++ b/internal/acme/acme.go @@ -45,6 +45,7 @@ import ( "github.com/go-acme/lego/v4/registration" "github.com/robfig/cron/v3" + "github.com/drakkan/sftpgo/v2/internal/common" "github.com/drakkan/sftpgo/v2/internal/ftpd" "github.com/drakkan/sftpgo/v2/internal/httpd" "github.com/drakkan/sftpgo/v2/internal/logger" @@ -561,6 +562,24 @@ func (c *Configuration) getCertificates() error { return nil } +func (c *Configuration) notifyCertificateRenewal(domain string, err error) { + if domain == "" { + domain = strings.Join(c.Domains, ",") + } + params := common.EventParams{ + Name: domain, + Timestamp: time.Now().UnixNano(), + } + if err != nil { + params.Status = 2 + params.Event = "Certificate renewal failed" + } else { + params.Status = 1 + params.Event = "Successful certificate renewal" + } + common.HandleCertificateEvent(params) +} + func (c *Configuration) renewCertificates() error { lockTime, err := c.getLockTime() if err != nil { @@ -573,22 +592,28 @@ func (c *Configuration) renewCertificates() error { } err = c.setLockTime() if err != nil { + c.notifyCertificateRenewal("", err) return err } account, client, err := c.setup() if err != nil { + c.notifyCertificateRenewal("", err) return err } if account.Registration == nil { acmeLog(logger.LevelError, "cannot renew certificates, your account is not registered") - return fmt.Errorf("cannot renew certificates, your account is not registered") + err = errors.New("cannot renew certificates, your account is not registered") + c.notifyCertificateRenewal("", err) + return err } var errRenew error needReload := false for _, domain := range c.Domains { certificates, err := c.loadCertificatesForDomain(domain) if err != nil { - return err + c.notifyCertificateRenewal(domain, err) + errRenew = err + continue } cert := certificates[0] if !c.needRenewal(cert, domain) { @@ -596,8 +621,10 @@ func (c *Configuration) renewCertificates() error { } err = c.obtainAndSaveCertificate(client, domain) if err != nil { + c.notifyCertificateRenewal(domain, err) errRenew = err } else { + c.notifyCertificateRenewal(domain, nil) needReload = true } } diff --git a/internal/common/defenderdb.go b/internal/common/defenderdb.go index 9c15675f..40f03cf1 100644 --- a/internal/common/defenderdb.go +++ b/internal/common/defenderdb.go @@ -112,6 +112,7 @@ func (d *dbDefender) AddEvent(ip string, event HostEvent) { Event: ipBlockedEventName, IP: ip, Timestamp: time.Now().UnixNano(), + Status: 1, }) } } diff --git a/internal/common/defendermem.go b/internal/common/defendermem.go index d457cd35..4d669b5a 100644 --- a/internal/common/defendermem.go +++ b/internal/common/defendermem.go @@ -213,6 +213,7 @@ func (d *memoryDefender) AddEvent(ip string, event HostEvent) { Event: ipBlockedEventName, IP: ip, Timestamp: time.Now().UnixNano(), + Status: 1, }) } else { d.hosts[ip] = hs diff --git a/internal/common/eventmanager.go b/internal/common/eventmanager.go index b3baf2a2..ecf67bf4 100644 --- a/internal/common/eventmanager.go +++ b/internal/common/eventmanager.go @@ -72,16 +72,22 @@ func init() { }) } +// HandleCertificateEvent checks and executes action rules for certificate events +func HandleCertificateEvent(params EventParams) { + eventManager.handleCertificateEvent(params) +} + // eventRulesContainer stores event rules by trigger type eventRulesContainer struct { sync.RWMutex - lastLoad int64 - FsEvents []dataprovider.EventRule - ProviderEvents []dataprovider.EventRule - Schedules []dataprovider.EventRule - IPBlockedEvents []dataprovider.EventRule - schedulesMapping map[string][]cron.EntryID - concurrencyGuard chan struct{} + lastLoad int64 + FsEvents []dataprovider.EventRule + ProviderEvents []dataprovider.EventRule + Schedules []dataprovider.EventRule + IPBlockedEvents []dataprovider.EventRule + CertificateEvents []dataprovider.EventRule + schedulesMapping map[string][]cron.EntryID + concurrencyGuard chan struct{} } func (r *eventRulesContainer) addAsyncTask() { @@ -138,6 +144,15 @@ func (r *eventRulesContainer) removeRuleInternal(name string) { return } } + for idx := range r.CertificateEvents { + if r.CertificateEvents[idx].Name == name { + lastIdx := len(r.CertificateEvents) - 1 + r.CertificateEvents[idx] = r.CertificateEvents[lastIdx] + r.CertificateEvents = r.CertificateEvents[:lastIdx] + eventManagerLog(logger.LevelDebug, "removed rule %q from certificate events", name) + return + } + } for idx := range r.Schedules { if r.Schedules[idx].Name == name { if schedules, ok := r.schedulesMapping[name]; ok { @@ -177,6 +192,9 @@ func (r *eventRulesContainer) addUpdateRuleInternal(rule dataprovider.EventRule) case dataprovider.EventTriggerIPBlocked: r.IPBlockedEvents = append(r.IPBlockedEvents, rule) eventManagerLog(logger.LevelDebug, "added rule %q to IP blocked events", rule.Name) + case dataprovider.EventTriggerCertificate: + r.CertificateEvents = append(r.CertificateEvents, rule) + eventManagerLog(logger.LevelDebug, "added rule %q to certificate events", rule.Name) case dataprovider.EventTriggerSchedule: for _, schedule := range rule.Conditions.Schedules { cronSpec := schedule.GetCronSpec() @@ -217,8 +235,8 @@ func (r *eventRulesContainer) loadRules() { r.addUpdateRuleInternal(rule) } } - eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d, ip blocked events: %d", - len(r.FsEvents), len(r.ProviderEvents), len(r.Schedules), len(r.IPBlockedEvents)) + eventManagerLog(logger.LevelDebug, "event rules updated, fs events: %d, provider events: %d, schedules: %d, ip blocked events: %d, certificate events: %d", + len(r.FsEvents), len(r.ProviderEvents), len(r.Schedules), len(r.IPBlockedEvents), len(r.CertificateEvents)) r.setLastLoadTime(modTime) } @@ -362,6 +380,28 @@ func (r *eventRulesContainer) handleIPBlockedEvent(params EventParams) { } } +func (r *eventRulesContainer) handleCertificateEvent(params EventParams) { + r.RLock() + defer r.RUnlock() + + if len(r.CertificateEvents) == 0 { + return + } + var rules []dataprovider.EventRule + for _, rule := range r.CertificateEvents { + if err := rule.CheckActionsConsistency(""); err == nil { + rules = append(rules, rule) + } else { + eventManagerLog(logger.LevelWarn, "rule %q skipped: %v, event %q", + rule.Name, err, params.Event) + } + } + + if len(rules) > 0 { + go executeAsyncRulesActions(rules, params) + } +} + // EventParams defines the supported event parameters type EventParams struct { Name string diff --git a/internal/common/protocol_test.go b/internal/common/protocol_test.go index 50f5bfb0..eff2795a 100644 --- a/internal/common/protocol_test.go +++ b/internal/common/protocol_test.go @@ -3530,6 +3530,125 @@ func TestEventRuleFsActions(t *testing.T) { assert.NoError(t, err) } +func TestEventRuleCertificate(t *testing.T) { + smtpCfg := smtp.Config{ + Host: "127.0.0.1", + Port: 2525, + From: "notify@example.com", + TemplatesPath: "templates", + } + err := smtpCfg.Initialize(configDir) + require.NoError(t, err) + lastReceivedEmail.reset() + + a1 := dataprovider.BaseEventAction{ + Name: "action1", + Type: dataprovider.ActionTypeEmail, + Options: dataprovider.BaseEventActionOptions{ + EmailConfig: dataprovider.EventActionEmailConfig{ + Recipients: []string{"test@example.com"}, + Subject: `"{{Event}}"`, + Body: "Domain: {{Name}} Timestamp: {{Timestamp}}", + }, + }, + } + action1, _, err := httpdtest.AddEventAction(a1, http.StatusCreated) + assert.NoError(t, err) + + a2 := dataprovider.BaseEventAction{ + Name: "action2", + Type: dataprovider.ActionTypeFolderQuotaReset, + } + action2, _, err := httpdtest.AddEventAction(a2, http.StatusCreated) + assert.NoError(t, err) + + r1 := dataprovider.EventRule{ + Name: "test rule certificate", + Trigger: dataprovider.EventTriggerCertificate, + Actions: []dataprovider.EventAction{ + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action1.Name, + }, + Order: 1, + }, + }, + } + rule1, _, err := httpdtest.AddEventRule(r1, http.StatusCreated) + assert.NoError(t, err) + r2 := dataprovider.EventRule{ + Name: "test rule 2", + Trigger: dataprovider.EventTriggerCertificate, + Actions: []dataprovider.EventAction{ + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action1.Name, + }, + Order: 1, + }, + { + BaseEventAction: dataprovider.BaseEventAction{ + Name: action2.Name, + }, + Order: 2, + }, + }, + } + rule2, _, err := httpdtest.AddEventRule(r2, http.StatusCreated) + assert.NoError(t, err) + + common.HandleCertificateEvent(common.EventParams{ + Name: "example.com", + Timestamp: time.Now().UnixNano(), + Status: 1, + Event: "Successful certificate renewal", + }) + assert.Eventually(t, func() bool { + return lastReceivedEmail.get().From != "" + }, 3000*time.Millisecond, 100*time.Millisecond) + email := lastReceivedEmail.get() + assert.Len(t, email.To, 1) + assert.True(t, util.Contains(email.To, "test@example.com")) + assert.Contains(t, string(email.Data), `Subject: "Successful certificate renewal"`) + assert.Contains(t, string(email.Data), `Domain: example.com Timestamp`) + + lastReceivedEmail.reset() + common.HandleCertificateEvent(common.EventParams{ + Name: "example.com", + Timestamp: time.Now().UnixNano(), + Status: 2, + Event: "Certificate renewal failed", + }) + assert.Eventually(t, func() bool { + return lastReceivedEmail.get().From != "" + }, 3000*time.Millisecond, 100*time.Millisecond) + email = lastReceivedEmail.get() + assert.Len(t, email.To, 1) + assert.True(t, util.Contains(email.To, "test@example.com")) + assert.Contains(t, string(email.Data), `Subject: "Certificate renewal failed"`) + assert.Contains(t, string(email.Data), `Domain: example.com Timestamp`) + + _, err = httpdtest.RemoveEventRule(rule1, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventRule(rule2, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventAction(action1, http.StatusOK) + assert.NoError(t, err) + _, err = httpdtest.RemoveEventAction(action2, http.StatusOK) + assert.NoError(t, err) + // ignored no more certificate rules + common.HandleCertificateEvent(common.EventParams{ + Name: "example.com", + Timestamp: time.Now().UnixNano(), + Status: 1, + Event: "Successful certificate renewal", + }) + + smtpCfg = smtp.Config{} + err = smtpCfg.Initialize(configDir) + require.NoError(t, err) +} + func TestEventRuleIPBlocked(t *testing.T) { oldConfig := config.GetCommonConfig() diff --git a/internal/dataprovider/eventrule.go b/internal/dataprovider/eventrule.go index 50951d87..4d396d59 100644 --- a/internal/dataprovider/eventrule.go +++ b/internal/dataprovider/eventrule.go @@ -85,11 +85,12 @@ const ( EventTriggerProviderEvent EventTriggerSchedule EventTriggerIPBlocked + EventTriggerCertificate ) var ( supportedEventTriggers = []int{EventTriggerFsEvent, EventTriggerProviderEvent, EventTriggerSchedule, - EventTriggerIPBlocked} + EventTriggerIPBlocked, EventTriggerCertificate} ) func isEventTriggerValid(trigger int) bool { @@ -104,6 +105,8 @@ func getTriggerTypeAsString(trigger int) string { return "Provider event" case EventTriggerIPBlocked: return "IP blocked" + case EventTriggerCertificate: + return "Certificate renewal" default: return "Schedule" } @@ -885,7 +888,7 @@ func (c *EventConditions) validate(trigger int) error { return err } } - case EventTriggerIPBlocked: + case EventTriggerIPBlocked, EventTriggerCertificate: c.FsEvents = nil c.ProviderEvents = nil c.Options.Names = nil @@ -1013,13 +1016,13 @@ func (r *EventRule) validate() error { return nil } -func (r *EventRule) checkIPBlockedActions() error { +func (r *EventRule) checkIPBlockedAndCertificateActions() error { unavailableActions := []int{ActionTypeUserQuotaReset, ActionTypeFolderQuotaReset, ActionTypeTransferQuotaReset, ActionTypeDataRetentionCheck, ActionTypeFilesystem} for _, action := range r.Actions { if util.Contains(unavailableActions, action.Type) { - return fmt.Errorf("action %q, type %q is not supported for IP blocked events", - action.Name, getActionTypeAsString(action.Type)) + return fmt.Errorf("action %q, type %q is not supported for event trigger %q", + action.Name, getActionTypeAsString(action.Type), getTriggerTypeAsString(r.Trigger)) } } return nil @@ -1067,8 +1070,8 @@ func (r *EventRule) CheckActionsConsistency(providerObjectType string) error { action.Name, getActionTypeAsString(action.Type)) } } - case EventTriggerIPBlocked: - if err := r.checkIPBlockedActions(); err != nil { + case EventTriggerIPBlocked, EventTriggerCertificate: + if err := r.checkIPBlockedAndCertificateActions(); err != nil { return err } } diff --git a/internal/httpd/server.go b/internal/httpd/server.go index b06aa619..990c4274 100644 --- a/internal/httpd/server.go +++ b/internal/httpd/server.go @@ -95,7 +95,7 @@ func (s *httpdServer) listenAndServe() error { ReadTimeout: 60 * time.Second, WriteTimeout: 60 * time.Second, IdleTimeout: 60 * time.Second, - MaxHeaderBytes: 1 << 16, // 64KB + MaxHeaderBytes: 1 << 18, // 256KB ErrorLog: log.New(&logger.StdLoggerWrapper{Sender: logSender}, "", 0), } if certMgr != nil && s.binding.EnableHTTPS { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 1b56b982..402181f4 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -4343,6 +4343,8 @@ components: - 5 - 6 - 7 + - 8 + - 9 description: | Supported event action types: * `1` - HTTP @@ -4352,17 +4354,23 @@ components: * `5` - User quota reset * `6` - Folder quota reset * `7` - Transfer quota reset + * `8` - Data retention check + * `9` - Filesystem EventTriggerTypes: type: integer enum: - 1 - 2 - 3 + - 4 + - 5 description: | Supported event trigger types: * `1` - Filesystem event * `2` - Provider event * `3` - Schedule + * `4` - IP blocked + * `5` - Certificate renewal LoginMethods: type: string enum: diff --git a/templates/webadmin/eventaction.html b/templates/webadmin/eventaction.html index 0ced8df9..589fca6c 100644 --- a/templates/webadmin/eventaction.html +++ b/templates/webadmin/eventaction.html @@ -511,7 +511,7 @@ along with this program. If not, see .