Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d3f71f9d0a | ||
|
|
8187b49599 | ||
|
|
2188643387 | ||
|
|
344031b575 | ||
|
|
a320093cb8 |
@@ -105,6 +105,7 @@ dockers:
|
|||||||
- &arm64v8_image "binwiederhier/ntfy:{{ .Tag }}-arm64v8"
|
- &arm64v8_image "binwiederhier/ntfy:{{ .Tag }}-arm64v8"
|
||||||
use: buildx
|
use: buildx
|
||||||
dockerfile: Dockerfile
|
dockerfile: Dockerfile
|
||||||
|
goarch: arm64
|
||||||
build_flag_templates:
|
build_flag_templates:
|
||||||
- "--platform=linux/arm64/v8"
|
- "--platform=linux/arm64/v8"
|
||||||
- image_templates:
|
- image_templates:
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
bcryptCost = 11
|
bcryptCost = 10
|
||||||
intentionalSlowDownHash = "$2a$11$eX15DeF27FwAgXt9wqJF0uAUMz74XywJcGBH3kP93pzKYv6ATk2ka" // Cost should match bcryptCost
|
intentionalSlowDownHash = "$2a$10$YFCQvqQDwIIwnJM1xkAYOeih0dg17UVGanaTStnrSzC8NCWxcLDwy" // Cost should match bcryptCost
|
||||||
)
|
)
|
||||||
|
|
||||||
// Auther-related queries
|
// Auther-related queries
|
||||||
|
|||||||
@@ -9,6 +9,8 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const minBcryptTimingMillis = int64(50) // Ideally should be >100ms, but this should also run on a Raspberry Pi without massive resources
|
||||||
|
|
||||||
func TestSQLiteAuth_FullScenario_Default_DenyAll(t *testing.T) {
|
func TestSQLiteAuth_FullScenario_Default_DenyAll(t *testing.T) {
|
||||||
a := newTestAuth(t, false, false)
|
a := newTestAuth(t, false, false)
|
||||||
require.Nil(t, a.AddUser("phil", "phil", auth.RoleAdmin))
|
require.Nil(t, a.AddUser("phil", "phil", auth.RoleAdmin))
|
||||||
@@ -24,14 +26,14 @@ func TestSQLiteAuth_FullScenario_Default_DenyAll(t *testing.T) {
|
|||||||
phil, err := a.Authenticate("phil", "phil")
|
phil, err := a.Authenticate("phil", "phil")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, "phil", phil.Name)
|
require.Equal(t, "phil", phil.Name)
|
||||||
require.True(t, strings.HasPrefix(phil.Hash, "$2a$11$"))
|
require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
|
||||||
require.Equal(t, auth.RoleAdmin, phil.Role)
|
require.Equal(t, auth.RoleAdmin, phil.Role)
|
||||||
require.Equal(t, []auth.Grant{}, phil.Grants)
|
require.Equal(t, []auth.Grant{}, phil.Grants)
|
||||||
|
|
||||||
ben, err := a.Authenticate("ben", "ben")
|
ben, err := a.Authenticate("ben", "ben")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, "ben", ben.Name)
|
require.Equal(t, "ben", ben.Name)
|
||||||
require.True(t, strings.HasPrefix(ben.Hash, "$2a$11$"))
|
require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
|
||||||
require.Equal(t, auth.RoleUser, ben.Role)
|
require.Equal(t, auth.RoleUser, ben.Role)
|
||||||
require.Equal(t, []auth.Grant{
|
require.Equal(t, []auth.Grant{
|
||||||
{"mytopic", true, true},
|
{"mytopic", true, true},
|
||||||
@@ -92,7 +94,7 @@ func TestSQLiteAuth_AddUser_Timing(t *testing.T) {
|
|||||||
a := newTestAuth(t, false, false)
|
a := newTestAuth(t, false, false)
|
||||||
start := time.Now().UnixMilli()
|
start := time.Now().UnixMilli()
|
||||||
require.Nil(t, a.AddUser("user", "pass", auth.RoleAdmin))
|
require.Nil(t, a.AddUser("user", "pass", auth.RoleAdmin))
|
||||||
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
|
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSQLiteAuth_Authenticate_Timing(t *testing.T) {
|
func TestSQLiteAuth_Authenticate_Timing(t *testing.T) {
|
||||||
@@ -103,19 +105,19 @@ func TestSQLiteAuth_Authenticate_Timing(t *testing.T) {
|
|||||||
start := time.Now().UnixMilli()
|
start := time.Now().UnixMilli()
|
||||||
_, err := a.Authenticate("user", "pass")
|
_, err := a.Authenticate("user", "pass")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
|
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
|
||||||
|
|
||||||
// Timing an incorrect attempt
|
// Timing an incorrect attempt
|
||||||
start = time.Now().UnixMilli()
|
start = time.Now().UnixMilli()
|
||||||
_, err = a.Authenticate("user", "INCORRECT")
|
_, err = a.Authenticate("user", "INCORRECT")
|
||||||
require.Equal(t, auth.ErrUnauthenticated, err)
|
require.Equal(t, auth.ErrUnauthenticated, err)
|
||||||
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
|
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
|
||||||
|
|
||||||
// Timing a non-existing user attempt
|
// Timing a non-existing user attempt
|
||||||
start = time.Now().UnixMilli()
|
start = time.Now().UnixMilli()
|
||||||
_, err = a.Authenticate("DOES-NOT-EXIST", "hithere")
|
_, err = a.Authenticate("DOES-NOT-EXIST", "hithere")
|
||||||
require.Equal(t, auth.ErrUnauthenticated, err)
|
require.Equal(t, auth.ErrUnauthenticated, err)
|
||||||
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, int64(100)) // Ideally should be > 200ms, but let's not make a brittle
|
require.GreaterOrEqual(t, time.Now().UnixMilli()-start, minBcryptTimingMillis)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSQLiteAuth_UserManagement(t *testing.T) {
|
func TestSQLiteAuth_UserManagement(t *testing.T) {
|
||||||
@@ -133,14 +135,14 @@ func TestSQLiteAuth_UserManagement(t *testing.T) {
|
|||||||
phil, err := a.User("phil")
|
phil, err := a.User("phil")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, "phil", phil.Name)
|
require.Equal(t, "phil", phil.Name)
|
||||||
require.True(t, strings.HasPrefix(phil.Hash, "$2a$11$"))
|
require.True(t, strings.HasPrefix(phil.Hash, "$2a$10$"))
|
||||||
require.Equal(t, auth.RoleAdmin, phil.Role)
|
require.Equal(t, auth.RoleAdmin, phil.Role)
|
||||||
require.Equal(t, []auth.Grant{}, phil.Grants)
|
require.Equal(t, []auth.Grant{}, phil.Grants)
|
||||||
|
|
||||||
ben, err := a.User("ben")
|
ben, err := a.User("ben")
|
||||||
require.Nil(t, err)
|
require.Nil(t, err)
|
||||||
require.Equal(t, "ben", ben.Name)
|
require.Equal(t, "ben", ben.Name)
|
||||||
require.True(t, strings.HasPrefix(ben.Hash, "$2a$11$"))
|
require.True(t, strings.HasPrefix(ben.Hash, "$2a$10$"))
|
||||||
require.Equal(t, auth.RoleUser, ben.Role)
|
require.Equal(t, auth.RoleUser, ben.Role)
|
||||||
require.Equal(t, []auth.Grant{
|
require.Equal(t, []auth.Grant{
|
||||||
{"mytopic", true, true},
|
{"mytopic", true, true},
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ var cmdPublish = &cli.Command{
|
|||||||
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, EnvVars: []string{"NTFY_DELAY"}, Usage: "delay/schedule message"},
|
&cli.StringFlag{Name: "delay", Aliases: []string{"at", "in", "D"}, EnvVars: []string{"NTFY_DELAY"}, Usage: "delay/schedule message"},
|
||||||
&cli.StringFlag{Name: "click", Aliases: []string{"U"}, EnvVars: []string{"NTFY_CLICK"}, Usage: "URL to open when notification is clicked"},
|
&cli.StringFlag{Name: "click", Aliases: []string{"U"}, EnvVars: []string{"NTFY_CLICK"}, Usage: "URL to open when notification is clicked"},
|
||||||
&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, EnvVars: []string{"NTFY_ATTACH"}, Usage: "URL to send as an external attachment"},
|
&cli.StringFlag{Name: "attach", Aliases: []string{"a"}, EnvVars: []string{"NTFY_ATTACH"}, Usage: "URL to send as an external attachment"},
|
||||||
&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, EnvVars: []string{"NTFY_FILENAME"}, Usage: "Filename for the attachment"},
|
&cli.StringFlag{Name: "filename", Aliases: []string{"name", "n"}, EnvVars: []string{"NTFY_FILENAME"}, Usage: "filename for the attachment"},
|
||||||
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, EnvVars: []string{"NTFY_FILE"}, Usage: "File to upload as an attachment"},
|
&cli.StringFlag{Name: "file", Aliases: []string{"f"}, EnvVars: []string{"NTFY_FILE"}, Usage: "file to upload as an attachment"},
|
||||||
&cli.StringFlag{Name: "email", Aliases: []string{"mail", "e"}, EnvVars: []string{"NTFY_EMAIL"}, Usage: "also send to e-mail address"},
|
&cli.StringFlag{Name: "email", Aliases: []string{"mail", "e"}, EnvVars: []string{"NTFY_EMAIL"}, Usage: "also send to e-mail address"},
|
||||||
&cli.StringFlag{Name: "user", Aliases: []string{"u"}, EnvVars: []string{"NTFY_USER"}, Usage: "username[:password] used to auth against the server"},
|
&cli.StringFlag{Name: "user", Aliases: []string{"u"}, EnvVars: []string{"NTFY_USER"}, Usage: "username[:password] used to auth against the server"},
|
||||||
&cli.BoolFlag{Name: "no-cache", Aliases: []string{"C"}, EnvVars: []string{"NTFY_NO_CACHE"}, Usage: "do not cache message server-side"},
|
&cli.BoolFlag{Name: "no-cache", Aliases: []string{"C"}, EnvVars: []string{"NTFY_NO_CACHE"}, Usage: "do not cache message server-side"},
|
||||||
|
|||||||
@@ -528,10 +528,7 @@ or the root domain:
|
|||||||
# Note that this config is most certainly incomplete. Please help out and let me know what's missing
|
# Note that this config is most certainly incomplete. Please help out and let me know what's missing
|
||||||
# via Discord/Matrix or in a GitHub issue.
|
# via Discord/Matrix or in a GitHub issue.
|
||||||
|
|
||||||
ntfy.sh {
|
ntfy.sh, http://nfty.sh {
|
||||||
reverse_proxy 127.0.0.1:2586
|
|
||||||
}
|
|
||||||
http://nfty.sh {
|
|
||||||
reverse_proxy 127.0.0.1:2586
|
reverse_proxy 127.0.0.1:2586
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -26,21 +26,21 @@ deb/rpm packages.
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_x86_64.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_x86_64.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
||||||
sudo ./ntfy serve
|
sudo ./ntfy serve
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_armv7.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_armv7.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
||||||
sudo ./ntfy serve
|
sudo ./ntfy serve
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_arm64.tar.gz
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_arm64.tar.gz
|
||||||
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
sudo tar -C /usr/bin -zxf ntfy_*.tar.gz ntfy
|
||||||
sudo ./ntfy serve
|
sudo ./ntfy serve
|
||||||
```
|
```
|
||||||
@@ -88,7 +88,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_amd64.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_amd64.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -96,7 +96,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_armv7.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_armv7.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -104,7 +104,7 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
wget https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_arm64.deb
|
wget https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_arm64.deb
|
||||||
sudo dpkg -i ntfy_*.deb
|
sudo dpkg -i ntfy_*.deb
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
@@ -114,21 +114,21 @@ Manually installing the .deb file:
|
|||||||
|
|
||||||
=== "x86_64/amd64"
|
=== "x86_64/amd64"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_amd64.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_amd64.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "armv7/armhf"
|
=== "armv7/armhf"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_armv7.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_armv7.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|
||||||
=== "arm64"
|
=== "arm64"
|
||||||
```bash
|
```bash
|
||||||
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.13.0/ntfy_1.13.0_linux_arm64.rpm
|
sudo rpm -ivh https://github.com/binwiederhier/ntfy/releases/download/v1.14.1/ntfy_1.14.1_linux_arm64.rpm
|
||||||
sudo systemctl enable ntfy
|
sudo systemctl enable ntfy
|
||||||
sudo systemctl start ntfy
|
sudo systemctl start ntfy
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -1174,6 +1174,18 @@ parameter (or any of its aliases `unifiedpush` or `up`) to `1` to [disable Fireb
|
|||||||
option is mostly equivalent to `Firebase: no`, but was introduced to allow future flexibility. The flag additionally
|
option is mostly equivalent to `Firebase: no`, but was introduced to allow future flexibility. The flag additionally
|
||||||
enables auto-detection of the message encoding. If the message is binary, it'll be encoded as base64.
|
enables auto-detection of the message encoding. If the message is binary, it'll be encoded as base64.
|
||||||
|
|
||||||
|
## Public topics
|
||||||
|
Obviously all topics on ntfy.sh are public, but there are a few designated topics that are used in examples, and topics
|
||||||
|
that you can use to try out what [authentication and access control](#authentication) looks like.
|
||||||
|
|
||||||
|
| Topic | User | Permissions | Description |
|
||||||
|
|------------------------------------------------|-----------------------------------|------------------------------------------------------|--------------------------------------|
|
||||||
|
| [announcements](https://ntfy.sh/announcements) | `*` (unauthenticated) | Read-only for everyone | Release announcements and such |
|
||||||
|
| [stats](https://ntfy.sh/stats) | `*` (unauthenticated) | Read-only for everyone | Daily statistics about ntfy.sh usage |
|
||||||
|
| [mytopic-rw](https://ntfy.sh/mytopic-rw) | `testuser` (password: `testuser`) | Read-write for `testuser`, no access for anyone else | Test topic |
|
||||||
|
| [mytopic-ro](https://ntfy.sh/mytopic-ro) | `testuser` (password: `testuser`) | Read-only for `testuser`, no access for anyone else | Test topic |
|
||||||
|
| [mytopic-wo](https://ntfy.sh/mytopic-wo) | `testuser` (password: `testuser`) | Write-only for `testuser`, no access for anyone else | Test topic |
|
||||||
|
|
||||||
## Limitations
|
## Limitations
|
||||||
There are a few limitations to the API to prevent abuse and to keep the server healthy. Almost all of these settings
|
There are a few limitations to the API to prevent abuse and to keep the server healthy. Almost all of these settings
|
||||||
are configurable via the server side [rate limiting settings](config.md#rate-limiting). Most of these limits you won't run into,
|
are configurable via the server side [rate limiting settings](config.md#rate-limiting). Most of these limits you won't run into,
|
||||||
|
|||||||
Reference in New Issue
Block a user