From 7325aa259e5728ebe669645f1c83fd1058fe68a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Tosser?= Date: Sun, 3 Oct 2021 20:20:32 +0400 Subject: [PATCH 1/3] Audit Logs --- api/tasks/audit.go | 45 ++++++++++++++ api/tasks/runner.go | 3 +- cli/setup/setup.go | 6 ++ util/config.go | 19 +++--- web2/package-lock.json | 133 +++++++++++++++++++++++++---------------- 5 files changed, 145 insertions(+), 61 deletions(-) create mode 100644 api/tasks/audit.go diff --git a/api/tasks/audit.go b/api/tasks/audit.go new file mode 100644 index 000000000..88e81ce0a --- /dev/null +++ b/api/tasks/audit.go @@ -0,0 +1,45 @@ +package tasks + +import ( + "bytes" + "encoding/json" + "github.com/ansible-semaphore/semaphore/util" + "net/http" + "strconv" + "time" +) + +func (t *task) sendAuditLog() { + if !util.Config.AuditLog { + return + } + url := util.Config.AuditLogURL + method := "POST" + payload, err := json.Marshal(map[string]interface{}{ + "start": t.task.Start, + "end": t.task.End, + "status": t.task.Status, + "task_id": t.task.ID, + "project_id": t.projectID, + "task_url": util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID), + }) + requestBody := bytes.NewBuffer(payload) + if err != nil { + util.LogError(err) + return + } + client := &http.Client{Timeout: 5 * time.Second} + req, err := http.NewRequest(method, url, requestBody) + + if err != nil { + util.LogError(err) + return + } + req.Header.Add("Content-Type", "application/json") + + _, err = client.Do(req) + if err != nil { + util.LogError(err) + return + } +} diff --git a/api/tasks/runner.go b/api/tasks/runner.go index 877d87674..b6a1c22f1 100644 --- a/api/tasks/runner.go +++ b/api/tasks/runner.go @@ -80,6 +80,7 @@ func (t *task) updateStatus() { util.LogPanic(err) sockets.Message(user, b) + t.sendAuditLog() } if err := t.store.UpdateTask(t.task); err != nil { @@ -383,7 +384,7 @@ func (t *task) installKey(key db.AccessKey, accessKeyUsage int) error { return fmt.Errorf("ssh key with passphrase not supported") } - return ioutil.WriteFile(path, []byte(key.SshKey.PrivateKey + "\n"), 0600) + return ioutil.WriteFile(path, []byte(key.SshKey.PrivateKey+"\n"), 0600) } func (t *task) updateRepository() error { diff --git a/cli/setup/setup.go b/cli/setup/setup.go index 9b4ecfdf3..3fd85204a 100644 --- a/cli/setup/setup.go +++ b/cli/setup/setup.go @@ -78,6 +78,12 @@ func InteractiveSetup(conf *util.ConfigType) { askValue("LDAP mapping for full name field", "cn", &conf.LdapMappings.CN) askValue("LDAP mapping for email field", "mail", &conf.LdapMappings.Mail) } + + askConfirmation("Enable audit logs?", false, &conf.AuditLog) + if conf.AuditLog { + askValue("URL to send POST audit logs to(it should start with http:// or https://)", "http://127.0.0.1:6666/", &conf.AuditLogURL) + } + } func scanBoltDb(conf *util.ConfigType) { diff --git a/util/config.go b/util/config.go index 253d22f61..e98479628 100644 --- a/util/config.go +++ b/util/config.go @@ -31,11 +31,11 @@ const ( type DbConfig struct { Dialect DbDriver `json:"-"` - Hostname string `json:"host"` - Username string `json:"user"` - Password string `json:"pass"` - DbName string `json:"name"` - Options map[string]string `json:"options"` + Hostname string `json:"host"` + Username string `json:"user"` + Password string `json:"pass"` + DbName string `json:"name"` + Options map[string]string `json:"options"` } type ldapMappings struct { @@ -76,6 +76,9 @@ type ConfigType struct { EmailUsername string `json:"email_username"` EmailPassword string `json:"email_password"` + // Audit Log + AuditLogURL string `json:"audit_log_url"` + // web host WebHost string `json:"web_host"` @@ -104,6 +107,7 @@ type ConfigType struct { TelegramAlert bool `json:"telegram_alert"` LdapEnable bool `json:"ldap_enable"` LdapNeedTLS bool `json:"ldap_needtls"` + AuditLog bool `json:"audit_log"` SshConfigPath string `json:"ssh_config_path"` } @@ -229,7 +233,6 @@ func (d *DbConfig) HasSupportMultipleDatabases() bool { return true } - func (d *DbConfig) GetConnectionString(includeDbName bool) (connectionString string, err error) { switch d.Dialect { case DbDriverBolt: @@ -249,8 +252,8 @@ func (d *DbConfig) GetConnectionString(includeDbName bool) (connectionString str d.Password, d.Hostname) } - options := map[string]string { - "parseTime": "true", + options := map[string]string{ + "parseTime": "true", "interpolateParams": "true", } for v, k := range d.Options { diff --git a/web2/package-lock.json b/web2/package-lock.json index 8a38f11c8..25ba5b1dc 100644 --- a/web2/package-lock.json +++ b/web2/package-lock.json @@ -1913,17 +1913,6 @@ "unique-filename": "^1.1.1" } }, - "chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "optional": true, - "requires": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - } - }, "cliui": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz", @@ -1971,25 +1960,6 @@ "path-exists": "^4.0.0" } }, - "has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "optional": true - }, - "loader-utils": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", - "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", - "dev": true, - "optional": true, - "requires": { - "big.js": "^5.2.2", - "emojis-list": "^3.0.0", - "json5": "^2.1.2" - } - }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -2054,16 +2024,6 @@ "minipass": "^3.1.1" } }, - "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "optional": true, - "requires": { - "has-flag": "^4.0.0" - } - }, "terser-webpack-plugin": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/terser-webpack-plugin/-/terser-webpack-plugin-2.3.8.tgz", @@ -2081,18 +2041,6 @@ "webpack-sources": "^1.4.3" } }, - "vue-loader-v16": { - "version": "npm:vue-loader@16.7.0", - "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.7.0.tgz", - "integrity": "sha512-43I0grWtwSCE8fiH/hAwFK+6sNlmvDuHhXScYH8HVSVAMS81IM66tgUOcxbPCeqhhz/1BE51YPxX59eZKGallQ==", - "dev": true, - "optional": true, - "requires": { - "chalk": "^4.1.0", - "hash-sum": "^2.0.0", - "loader-utils": "^2.0.0" - } - }, "wrap-ansi": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz", @@ -14367,6 +14315,87 @@ } } }, + "vue-loader-v16": { + "version": "npm:vue-loader@16.8.1", + "resolved": "https://registry.npmjs.org/vue-loader/-/vue-loader-16.8.1.tgz", + "integrity": "sha512-V53TJbHmzjBhCG5OYI2JWy/aYDspz4oVHKxS43Iy212GjGIG1T3EsB3+GWXFm/1z5VwjdjLmdZUFYM70y77vtQ==", + "dev": true, + "optional": true, + "requires": { + "chalk": "^4.1.0", + "hash-sum": "^2.0.0", + "loader-utils": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "optional": true, + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "optional": true, + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "optional": true, + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "optional": true + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "optional": true + }, + "loader-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.0.tgz", + "integrity": "sha512-rP4F0h2RaWSvPEkD7BLDFQnvSf+nK+wr3ESUjNTyAGobqrijmW92zc+SO6d4p4B1wh7+B/Jg1mkQe5NYUEHtHQ==", + "dev": true, + "optional": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "optional": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, "vue-router": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.4.4.tgz", From 21d4c566b28cecf59443b1b34504e1fb3212af0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Tosser?= Date: Sun, 3 Oct 2021 20:25:49 +0400 Subject: [PATCH 2/3] Modify where the audit logs is --- api/tasks/runner.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/api/tasks/runner.go b/api/tasks/runner.go index b6a1c22f1..efb09d54f 100644 --- a/api/tasks/runner.go +++ b/api/tasks/runner.go @@ -80,9 +80,8 @@ func (t *task) updateStatus() { util.LogPanic(err) sockets.Message(user, b) - t.sendAuditLog() } - + t.sendAuditLog() if err := t.store.UpdateTask(t.task); err != nil { t.panicOnError(err, "Failed to update task status") } From 3158f87f00fe5ad9e3d1c4e1f610be10e41fa49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Tosser?= Date: Tue, 5 Oct 2021 00:23:09 +0400 Subject: [PATCH 3/3] More logs + link --- api/tasks/audit.go | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/api/tasks/audit.go b/api/tasks/audit.go index 88e81ce0a..7d209837a 100644 --- a/api/tasks/audit.go +++ b/api/tasks/audit.go @@ -16,12 +16,15 @@ func (t *task) sendAuditLog() { url := util.Config.AuditLogURL method := "POST" payload, err := json.Marshal(map[string]interface{}{ - "start": t.task.Start, - "end": t.task.End, - "status": t.task.Status, - "task_id": t.task.ID, - "project_id": t.projectID, - "task_url": util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID), + "project_id": t.projectID, + "template_id": t.task.TemplateID, + "task_id": t.task.ID, + "playbook": t.task.Playbook, + "environment": t.task.Environment, + "start": t.task.Start, + "end": t.task.End, + "status": t.task.Status, + "task_url": util.Config.WebHost + "/project/" + strconv.Itoa(t.template.ProjectID) + "/history/?t=" + strconv.Itoa(t.task.ID), }) requestBody := bytes.NewBuffer(payload) if err != nil {