mirror of
https://github.com/0x1d/rcond.git
synced 2025-12-14 18:25:21 +01:00
feat: add API token authentication
This commit is contained in:
5
Makefile
5
Makefile
@@ -1,3 +1,4 @@
|
|||||||
|
SHELL := bash
|
||||||
ARCH ?= arm64
|
ARCH ?= arm64
|
||||||
ADDR ?= 0.0.0.0:8080
|
ADDR ?= 0.0.0.0:8080
|
||||||
|
|
||||||
@@ -10,10 +11,10 @@ build:
|
|||||||
env GOOS=linux GOARCH=${ARCH} go build -o bin/rcond-${ARCH} ./cmd/rcond/main.go
|
env GOOS=linux GOARCH=${ARCH} go build -o bin/rcond-${ARCH} ./cmd/rcond/main.go
|
||||||
|
|
||||||
run:
|
run:
|
||||||
bin/rcond-${ARCH} ${ADDR}
|
source .env && bin/rcond-${ARCH} ${ADDR}
|
||||||
|
|
||||||
dev:
|
dev:
|
||||||
go run cmd/rcond/main.go ${ADDR}
|
RCOND_API_TOKEN=1234567890 go run cmd/rcond/main.go
|
||||||
|
|
||||||
upload:
|
upload:
|
||||||
scp rcond-${ARCH} pi@rpi-40ac:/home/pi/rcond
|
scp rcond-${ARCH} pi@rpi-40ac:/home/pi/rcond
|
||||||
|
|||||||
19
README.md
19
README.md
@@ -1,13 +1,14 @@
|
|||||||
# rcond
|
# rcond
|
||||||
|
|
||||||
A simple daemon to manage network connections through NetworkManager's D-Bus interface.
|
A simple daemon to manage
|
||||||
|
- network connections through NetworkManager's D-Bus interface
|
||||||
|
- system hostname through the hostname1 service
|
||||||
|
|
||||||
It provides a REST API to:
|
It provides a REST API to:
|
||||||
- Create and activate WiFi connections
|
- Create and activate WiFi connections
|
||||||
- Deactivate WiFi connections
|
- Deactivate WiFi connections
|
||||||
- Remove stored connection profiles
|
- Remove stored connection profiles
|
||||||
|
- Get and set the system hostname
|
||||||
The daemon is designed to run on Linux systems with NetworkManager.
|
|
||||||
|
|
||||||
## Build and Run
|
## Build and Run
|
||||||
|
|
||||||
@@ -46,6 +47,7 @@ All endpoints use JSON for request and response payloads.
|
|||||||
```bash
|
```bash
|
||||||
curl -v -X POST http://localhost:8080/network/up \
|
curl -v -X POST http://localhost:8080/network/up \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Token: 1234567890" \
|
||||||
-d '{
|
-d '{
|
||||||
"interface": "wlan0",
|
"interface": "wlan0",
|
||||||
"ssid": "MyNetworkSSID",
|
"ssid": "MyNetworkSSID",
|
||||||
@@ -58,6 +60,7 @@ curl -v -X POST http://localhost:8080/network/up \
|
|||||||
```bash
|
```bash
|
||||||
curl -v -X POST http://localhost:8080/network/down \
|
curl -v -X POST http://localhost:8080/network/down \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Token: 1234567890" \
|
||||||
-d '{
|
-d '{
|
||||||
"interface": "wlan0"
|
"interface": "wlan0"
|
||||||
}'
|
}'
|
||||||
@@ -66,13 +69,18 @@ curl -v -X POST http://localhost:8080/network/down \
|
|||||||
### Remove the stored connection
|
### Remove the stored connection
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -v -X POST http://localhost:8080/network/remove
|
curl -v -X POST http://localhost:8080/network/remove \
|
||||||
|
-H "X-API-Token: 1234567890" \
|
||||||
|
-d '{
|
||||||
|
"interface": "wlan0"
|
||||||
|
}'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Get the hostname
|
### Get the hostname
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -v http://localhost:8080/hostname
|
curl -v http://localhost:8080/hostname \
|
||||||
|
-H "X-API-Token: 1234567890"
|
||||||
```
|
```
|
||||||
|
|
||||||
### Set the hostname
|
### Set the hostname
|
||||||
@@ -80,6 +88,7 @@ curl -v http://localhost:8080/hostname
|
|||||||
```bash
|
```bash
|
||||||
curl -v -X POST http://localhost:8080/hostname \
|
curl -v -X POST http://localhost:8080/hostname \
|
||||||
-H "Content-Type: application/json" \
|
-H "Content-Type: application/json" \
|
||||||
|
-H "X-API-Token: 1234567890" \
|
||||||
-d '{
|
-d '{
|
||||||
"hostname": "MyHostname"
|
"hostname": "MyHostname"
|
||||||
}'
|
}'
|
||||||
|
|||||||
@@ -8,6 +8,17 @@ servers:
|
|||||||
- url: http://localhost:8080
|
- url: http://localhost:8080
|
||||||
description: Local development server
|
description: Local development server
|
||||||
|
|
||||||
|
components:
|
||||||
|
securitySchemes:
|
||||||
|
ApiKeyAuth:
|
||||||
|
type: apiKey
|
||||||
|
in: header
|
||||||
|
name: X-API-Token
|
||||||
|
description: API token for authentication
|
||||||
|
|
||||||
|
security:
|
||||||
|
- ApiKeyAuth: []
|
||||||
|
|
||||||
paths:
|
paths:
|
||||||
/health:
|
/health:
|
||||||
get:
|
get:
|
||||||
@@ -57,6 +68,8 @@ paths:
|
|||||||
description: Network interface brought up successfully
|
description: Network interface brought up successfully
|
||||||
'400':
|
'400':
|
||||||
description: Invalid request payload
|
description: Invalid request payload
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - invalid or missing API token
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
|
|
||||||
@@ -82,6 +95,8 @@ paths:
|
|||||||
description: Network interface brought down successfully
|
description: Network interface brought down successfully
|
||||||
'400':
|
'400':
|
||||||
description: Invalid request payload
|
description: Invalid request payload
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - invalid or missing API token
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
|
|
||||||
@@ -92,6 +107,8 @@ paths:
|
|||||||
responses:
|
responses:
|
||||||
'200':
|
'200':
|
||||||
description: Connection profile removed successfully
|
description: Connection profile removed successfully
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - invalid or missing API token
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
|
|
||||||
@@ -108,6 +125,8 @@ paths:
|
|||||||
type: string
|
type: string
|
||||||
description: Current hostname
|
description: Current hostname
|
||||||
example: "MyHostname"
|
example: "MyHostname"
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - invalid or missing API token
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
post:
|
post:
|
||||||
@@ -131,6 +150,7 @@ paths:
|
|||||||
description: Hostname set successfully
|
description: Hostname set successfully
|
||||||
'400':
|
'400':
|
||||||
description: Invalid request payload
|
description: Invalid request payload
|
||||||
|
'401':
|
||||||
|
description: Unauthorized - invalid or missing API token
|
||||||
'500':
|
'500':
|
||||||
description: Internal server error
|
description: Internal server error
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// Usage: rcond <address>
|
// Usage: rcond <address> <api-token>
|
||||||
|
|
||||||
package main
|
package main
|
||||||
|
|
||||||
@@ -20,12 +20,17 @@ func usage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if len(os.Args) < 2 {
|
|
||||||
usage()
|
addr := "0.0.0.0:8080"
|
||||||
|
if len(os.Args) > 1 {
|
||||||
|
addr = os.Args[1]
|
||||||
|
}
|
||||||
|
apiToken := os.Getenv("RCOND_API_TOKEN")
|
||||||
|
if apiToken == "" {
|
||||||
|
log.Fatal("RCOND_API_TOKEN environment variable not set")
|
||||||
}
|
}
|
||||||
|
|
||||||
addr := os.Args[1]
|
srv := http.NewServer(addr, apiToken)
|
||||||
srv := http.NewServer(addr)
|
|
||||||
srv.RegisterRoutes()
|
srv.RegisterRoutes()
|
||||||
|
|
||||||
log.Printf("Starting server on %s", addr)
|
log.Printf("Starting server on %s", addr)
|
||||||
|
|||||||
@@ -10,11 +10,12 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Server struct {
|
type Server struct {
|
||||||
router *mux.Router
|
router *mux.Router
|
||||||
srv *http.Server
|
srv *http.Server
|
||||||
|
apiToken string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServer(addr string) *Server {
|
func NewServer(addr string, apiToken string) *Server {
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
|
|
||||||
srv := &http.Server{
|
srv := &http.Server{
|
||||||
@@ -25,8 +26,9 @@ func NewServer(addr string) *Server {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return &Server{
|
return &Server{
|
||||||
router: router,
|
router: router,
|
||||||
srv: srv,
|
srv: srv,
|
||||||
|
apiToken: apiToken,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,13 +40,24 @@ func (s *Server) Shutdown(ctx context.Context) error {
|
|||||||
return s.srv.Shutdown(ctx)
|
return s.srv.Shutdown(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Server) verifyToken(next http.HandlerFunc) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
token := r.Header.Get("X-API-Token")
|
||||||
|
if token == "" || token != s.apiToken {
|
||||||
|
http.Error(w, "Unauthorized", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next(w, r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Server) RegisterRoutes() {
|
func (s *Server) RegisterRoutes() {
|
||||||
s.router.HandleFunc("/health", s.healthHandler).Methods(http.MethodGet)
|
s.router.HandleFunc("/health", s.healthHandler).Methods(http.MethodGet)
|
||||||
s.router.HandleFunc("/network/up", HandleNetworkUp).Methods(http.MethodPost)
|
s.router.HandleFunc("/network/up", s.verifyToken(HandleNetworkUp)).Methods(http.MethodPost)
|
||||||
s.router.HandleFunc("/network/down", HandleNetworkDown).Methods(http.MethodPost)
|
s.router.HandleFunc("/network/down", s.verifyToken(HandleNetworkDown)).Methods(http.MethodPost)
|
||||||
s.router.HandleFunc("/network/remove", HandleNetworkRemove).Methods(http.MethodPost)
|
s.router.HandleFunc("/network/remove", s.verifyToken(HandleNetworkRemove)).Methods(http.MethodPost)
|
||||||
s.router.HandleFunc("/hostname", HandleGetHostname).Methods(http.MethodGet)
|
s.router.HandleFunc("/hostname", s.verifyToken(HandleGetHostname)).Methods(http.MethodGet)
|
||||||
s.router.HandleFunc("/hostname", HandleSetHostname).Methods(http.MethodPost)
|
s.router.HandleFunc("/hostname", s.verifyToken(HandleSetHostname)).Methods(http.MethodPost)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
|
func (s *Server) healthHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
|
|||||||
Reference in New Issue
Block a user