Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature Request]: API PUT request support on Organizaiton & Network #380

Closed
daymr opened this issue Apr 9, 2024 · 2 comments
Closed

[Feature Request]: API PUT request support on Organizaiton & Network #380

daymr opened this issue Apr 9, 2024 · 2 comments
Labels
enhancement New feature or request

Comments

@daymr
Copy link

daymr commented Apr 9, 2024

🚀 Feature Summary

API PUT request support on Organizaiton & Network

📝 Detailed Description

currently API endpoint only supports POST which overwrites the JSON payload entirely. For example. I would like the functionality to "add" a route and dns server via cURL.

🎯 Use Case

Here is a bash script for Unifi Dream Machine I am working on. It currently can authorize and name a node based on host name. But I would like it to be able to pass its static route of its native LAN and DNS. But currently with POST only supported and not PUT, I cannot seem to get it to work the way I envisioned. I have attempted to perform a pre-emptive GET and extrapolate data, then concatonate a new variable, to no avail. I assume PUT support would rememedy this problem?

#!/bin/bash

# ZeroTier API token and identifiers
API_TOKEN=""
ZT_NETWORK_ID=$(zerotier-cli listnetworks | awk 'NR==2 {print $3}')
ZT_ORGANIZATION=""
URL=""
# Find the interface name that starts with "zt"
INTERFACE=$(ip link show | grep -o '^.*: zt[^:]*' | awk '{print $2}' | tr -d ':')

# Retrieve the IP address of the interface
if [[ ! -z "$INTERFACE" ]]; then
    # Assuming you're interested in the IPv4 address
    ZT_IP=$(ip addr show $INTERFACE | grep 'inet ' | awk '{print $2}' | cut -d'/' -f1)
else
    echo "No ZeroTier interface found."
    exit 1
fi




# Retrieve the FQDN hostname
FQDN_HOSTNAME=$(hostname -f)

# Retrieve the Node ID using zerotier-cli
NODE_ID=$(sudo cat /mnt/.rwfs/data/config/zerotier-one/identity.public | cut -d ':' -f 1)

# Create a JSON payload with the specified structure
json_payload=$(jq -n --arg name "$FQDN_HOSTNAME" --arg authorized "true" '{name: $name, authorized: $authorized}')

# Make the POST request using cURL with verbose output and handle redirection
echo "Authorizing node in ZeroTier network..."
response=$(curl -L -X POST \
  -H "Content-Type: application/json" \
  -H "x-ztnet-auth: $API_TOKEN" \
  -H "Accept: application/json" \
  -d "$json_payload" \
  "https://$URL/api/v1/org/$ZT_ORGANIZATION/network/$ZT_NETWORK_ID/member/$NODE_ID")

# Check for errors
if [[ $response != *"200"* ]]; then
    echo "SUCCESSFUL: HTTP $response"
fi

echo "Authorization successful."




# DNS servers and routes configuration
# Example DNS servers: ""
# Example route: target network "10.0.0.0/24" with an optional via address (leave empty if not needed)
dns_servers="192.168.0.6"
route_target="192.168.0.0/24"
route_via="$ZT_IP"  # Leave empty if there's no specific gateway for the route



# Assuming dns_servers is a single IP or a comma-separated list of DNS IPs
dns_array=$(echo $dns_servers | jq -R 'split(",") | if length==0 then empty else . end')

# Adjust the route_via to handle null if empty
route_via_adjusted=$(if [ -z "$ZT_IP" ]; then echo "null"; else echo "\"$ZT_IP\""; fi)

# Construct the JSON payload with DNS and Routes according to the documentation

json_payload=$(jq -n --arg dns_servers "$dns_servers" --arg route_target "$route_target" --arg ZT_IP "$ZT_IP" '{dns: [$dns_servers], routes: [{target: $route_target, via: $ZT_IP}]}')

response=$(curl -L -X POST \
  -H "Content-Type: application/json" \
  -H "x-ztnet-auth: $API_TOKEN" \
  -H "Accept: application/json" \
  -d "$json_payload" \
  "https://$URL/api/v1/org/$ZT_ORGANIZATION/network/$ZT_NETWORK_ID")


# Check for success (HTTP status code 200)
if [[ $response -ne 200 ]]; then
    echo "Error updating network: HTTP $response"
    exit 1
else
    echo "Network updated successfully."
fi

💡 Willing to Contribute

Yes, I could help with testing

@daymr daymr added the enhancement New feature or request label Apr 9, 2024
@daymr
Copy link
Author

daymr commented Apr 12, 2024

I came up with a workaround to accomplish this.. here is the sample bash script... I am actually passing variables from a windows machine with powershell to make everything full automated. but here is the BASH portion. You will need to adjust the path for identity.public when obtaining the NODE_ID as it is unique to Unifi UDM.

#!/bin/bash


ZT_NETWORK_ID=""
ZT_ORGANIZATION=""
API_TOKEN=""
domain=""
URL=""
dns_servers=""
route_target=""


# Retrieve the FQDN hostname
FQDN_HOSTNAME=$(hostname -f)

# Retrieve the Node ID using zerotier-cli
NODE_ID=$(sudo cat /mnt/.rwfs/data/config/zerotier-one/identity.public | cut -d ':' -f 1)

# Create a JSON payload with the specified structure
json_payload=$(jq -n --arg name "$FQDN_HOSTNAME" --arg authorized "true" '{name: $name, authorized: $authorized}')

# Make the POST request using cURL with verbose output and handle redirection
echo "Authorizing node in ZeroTier network..."
response=$(curl -L -X POST \
  -H "Content-Type: application/json" \
  -H "x-ztnet-auth: $API_TOKEN" \
  -H "Accept: application/json" \
  -d "$json_payload" \
  "$URL/api/v1/org/$ZT_ORGANIZATION/network/$ZT_NETWORK_ID/member/$NODE_ID")

# Check for errors
if [[ $response != *"200"* ]]; then
    echo "SUCCESSFUL: HTTP $response"
fi

echo "Authorization successful."


# Find ZeroTier interfaces that start with 'zt'
INTERFACES=$(ip link show | awk '$2 ~ /^zt/ {print substr($2, 1, length($2)-1)}')

# Check if any ZeroTier interfaces were found
if [[ -z "$INTERFACES" ]]; then
    echo "No ZeroTier interfaces found."
else
    echo "Found ZeroTier interfaces: $INTERFACES"
    # Loop through each found interface
    for INTERFACE in $INTERFACES; do
        echo "Processing interface: $INTERFACE"
        # Extracting the IP address from the discovered interface
        ZT_IP=$(ip addr show $INTERFACE | awk '/inet / && !/inet6/ {print $2}' | cut -d'/' -f1)
        if [[ -z "$ZT_IP" ]]; then
            echo "Failed to extract IP. No IPv4 address assigned or incorrect parsing for interface $INTERFACE."
        else
            echo "ZeroTier IP for $INTERFACE: $ZT_IP"
        fi
    done
fi


# Now, ZT_IP variable holds the IP address
# You can use $ZT_IP in your script below this point

########################################################################################################

# Get data from API
response=$(curl -s -L -X GET "$URL/api/v1/org/$ZT_ORGANIZATION/network/$ZT_NETWORK_ID/" \
    -H "Accept: application/json" \
    -H "x-ztnet-auth: $API_TOKEN")

# Extract DNS servers and domain using jq
fetched_dns_servers=$(echo $response | jq -r '.dns.servers | join(",")')
fetched_domain=$(echo $response | jq -r '.dns.domain')

# Update the domain only if the fetched domain is not empty or null
if [[ -n "$fetched_domain" && "$domain" != "$fetched_domain" ]]; then
    domain="$fetched_domain"  # Update directly with the new domain
    echo "Domain updated to: $domain"
else
    echo "No valid domain fetched or no change needed; keeping existing domain: $domain"
fi

# Update DNS servers only with new and non-empty entries
IFS=',' read -ra ADDR <<< "$fetched_dns_servers"
for i in "${ADDR[@]}"; do
    if [[ -n "$i" && ! "$dns_servers" =~ "$i" ]]; then
        dns_servers="${dns_servers:+$dns_servers,}$i"
    fi
done
echo "Updated DNS servers: $dns_servers"

dns_servers=$(echo $dns_servers | sed 's/^,//')  # Remove leading comma if present

# Extract and process routes from the API response
fetched_routes=$(echo "$response" | jq '.routes')

# Add your existing routes with $ZT_IP as 'via' where appropriate
IFS=',' read -ra ADDR <<< "$route_target"
for addr in "${ADDR[@]}"; do
    if [[ -n "$ZT_IP" ]]; then
        fetched_routes=$(echo "$fetched_routes" | jq --arg target "$addr" --arg via "$ZT_IP" '. + [{"target": $target, "via": $via}]')
    else
        fetched_routes=$(echo "$fetched_routes" | jq --arg target "$addr" '. + [{"target": $target, "via": null}]')
    fi
done

# Construct the JSON payload with DNS and Routes using jq directly
json_payload=$(jq -n --argjson dnsServers "$(echo "$dns_servers" | jq -R 'split(",")')" \
                     --arg dnsDomain "$domain" \
                     --argjson routes "$fetched_routes" \
  '{
    dns: {
      domain: $dnsDomain,
      servers: $dnsServers
    },
    routes: $routes
  }')

# Make the POST request to update network information
curl -L -X POST "$URL/api/v1/org/$ZT_ORGANIZATION/network/$ZT_NETWORK_ID/" \
  -H "Content-Type: application/json" \
  -H "x-ztnet-auth: $API_TOKEN" \
  -H "Accept: application/json" \
  -d "$json_payload" \
  -w "%{http_code}" -o response_body.txt

# Check for success (HTTP status code 200)
response=$(<response_body.txt)
if [[ $response -ne 200 ]]; then
    echo "Error updating network: HTTP $response"
    cat response_body.txt  # Show the response body for debugging
else
    echo "Network updated successfully."
fi

# Cleanup
rm response_body.txt
#########################################################
```############

@sinamics
Copy link
Owner

Glad you found a solution.
Im not sure if i want to go down that path to add api for adding config params.

However, i do see the API docs has an error regards the DNS entries.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants