This commit is contained in:
parent
bd093bb00f
commit
5f7d710404
46
.drone.yml
Normal file
46
.drone.yml
Normal file
@ -0,0 +1,46 @@
|
||||
---
|
||||
kind: pipeline
|
||||
name: publish
|
||||
|
||||
trigger:
|
||||
event:
|
||||
- push
|
||||
- tag
|
||||
refs:
|
||||
- refs/heads/master
|
||||
- refs/tags/v*
|
||||
|
||||
steps:
|
||||
- name: Build and publish
|
||||
image: woodpeckerci/plugin-docker-buildx
|
||||
settings:
|
||||
repo: iamthefij/unifi-traffic-routes
|
||||
auto_tag: true
|
||||
username:
|
||||
from_secret: docker_username
|
||||
password:
|
||||
from_secret: docker_password
|
||||
|
||||
---
|
||||
kind: pipeline
|
||||
name: notify
|
||||
|
||||
depends_on:
|
||||
- publish
|
||||
|
||||
trigger:
|
||||
status:
|
||||
- failure
|
||||
|
||||
steps:
|
||||
|
||||
- name: notify
|
||||
image: drillster/drone-email
|
||||
settings:
|
||||
host:
|
||||
from_secret: SMTP_HOST # pragma: whitelist secret
|
||||
username:
|
||||
from_secret: SMTP_USER # pragma: whitelist secret
|
||||
password:
|
||||
from_secret: SMTP_PASS # pragma: whitelist secret
|
||||
from: drone@iamthefij.com
|
9
Dockerfile
Normal file
9
Dockerfile
Normal file
@ -0,0 +1,9 @@
|
||||
FROM python:3
|
||||
|
||||
COPY requirements.txt /src/
|
||||
RUN pip install --no-cache-dir -r /src/requirements.txt
|
||||
|
||||
COPY main.py /src/
|
||||
|
||||
ENTRYPOINT ["/usr/local/bin/python"]
|
||||
CMD ["/src/main.py"]
|
15
README.md
15
README.md
@ -1,3 +1,16 @@
|
||||
# unfifi-traffic-routes-domain-ip
|
||||
|
||||
Look up and set traffic route IPs based on domains for clients not using the UniFi DNS
|
||||
Look up and set traffic route IPs based on domains for clients not using the UniFi DNS
|
||||
|
||||
## Configuration
|
||||
|
||||
Set the following as environment variables
|
||||
|
||||
| Variable | Description | Default |
|
||||
| -------- | ----------- | ------- |
|
||||
| `UNIFI_HOST` | Unifi network hostname | `192.168.1.1` |
|
||||
| `UNIFI_PORT` | Unifi network port number | `443` |
|
||||
| `UNIFI_USER` | Unifi username | |
|
||||
| `UNIFI_PASS` | Unifi pass | |
|
||||
|
||||
The user must have access to Manage the Network application to update the Traffic Routes.
|
||||
|
122
main.py
Normal file
122
main.py
Normal file
@ -0,0 +1,122 @@
|
||||
import asyncio
|
||||
import logging
|
||||
import os
|
||||
import socket
|
||||
from asyncio.timeouts import timeout
|
||||
|
||||
import aiohttp
|
||||
import aiounifi
|
||||
from aiounifi.controller import Controller
|
||||
from aiounifi.models.configuration import Configuration
|
||||
from aiounifi.models.traffic_route import IPAddress
|
||||
from aiounifi.models.traffic_route import MatchingTarget
|
||||
|
||||
HOST = os.getenv("UNIFI_HOST", "192.168.1.1")
|
||||
PORT = int(os.getenv("UNIFI_PORT", 443))
|
||||
USERNAME = os.getenv("UNIFI_USER")
|
||||
PASSWORD = os.getenv("UNIFI_PASSWORD")
|
||||
|
||||
|
||||
LOGGER = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def get_configuration(session: aiohttp.ClientSession) -> Configuration:
|
||||
return Configuration(
|
||||
session,
|
||||
HOST,
|
||||
username=USERNAME,
|
||||
password=PASSWORD,
|
||||
port=PORT,
|
||||
)
|
||||
|
||||
|
||||
async def get_controller(config: Configuration) -> Controller | None:
|
||||
controller = Controller(config)
|
||||
|
||||
try:
|
||||
async with timeout(10):
|
||||
await controller.login()
|
||||
return controller
|
||||
except aiounifi.LoginRequired:
|
||||
LOGGER.error("Connected to UniFi at %s:%s but couldn't log in", config.host, config.port)
|
||||
|
||||
except aiounifi.Unauthorized:
|
||||
LOGGER.error("Connected to UniFi at %s:%s but not registered", config.host, config.port)
|
||||
|
||||
except (asyncio.TimeoutError, aiounifi.RequestError):
|
||||
LOGGER.exception("Error connecting to the UniFi controller at %s:%s", config.host, config.port)
|
||||
|
||||
except aiounifi.AiounifiException:
|
||||
LOGGER.exception("Unknown UniFi communication error occurred")
|
||||
|
||||
return None
|
||||
|
||||
|
||||
async def main():
|
||||
session = aiohttp.ClientSession(cookie_jar=aiohttp.CookieJar(unsafe=True))
|
||||
config = get_configuration(session)
|
||||
controller = await get_controller(config)
|
||||
if not controller:
|
||||
LOGGER.error("Couldn't connect")
|
||||
await session.close()
|
||||
return
|
||||
|
||||
try:
|
||||
await controller.initialize()
|
||||
|
||||
print("Initialized!!")
|
||||
|
||||
await controller.traffic_routes.update()
|
||||
for item in controller.traffic_routes.values():
|
||||
if item.domains and item.matching_target == MatchingTarget.IP:
|
||||
print(item.description)
|
||||
|
||||
# Look up unique ip addresses
|
||||
addresses = set()
|
||||
for domain in item.domains:
|
||||
print(domain["domain"])
|
||||
addresses.update(
|
||||
result[4][0]
|
||||
for result in socket.getaddrinfo(
|
||||
domain["domain"],
|
||||
80,
|
||||
type=socket.SOCK_STREAM,
|
||||
proto=socket.IPPROTO_IP,
|
||||
)
|
||||
)
|
||||
|
||||
# Sort addresses by type and value
|
||||
sorted_addresses = list(
|
||||
sorted(
|
||||
addresses, key=lambda a: f"{'ip4' if '.' in a else 'ip6'}-{a}"
|
||||
)
|
||||
)
|
||||
print(sorted_addresses)
|
||||
|
||||
# Build addresses objects
|
||||
ip_addresses = [
|
||||
IPAddress(
|
||||
ip_or_subnet=a,
|
||||
ip_version="v4" if "." in a else "v6",
|
||||
port_ranges=[],
|
||||
ports=[],
|
||||
)
|
||||
for a in sorted_addresses
|
||||
]
|
||||
|
||||
# Check for change
|
||||
if ip_addresses == item.raw["ip_addresses"]:
|
||||
print("already up to date")
|
||||
continue
|
||||
|
||||
# Update while preserving current state
|
||||
item.raw["ip_addresses"] = ip_addresses
|
||||
result = await controller.traffic_routes.save(item)
|
||||
print(result)
|
||||
|
||||
finally:
|
||||
await session.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@ -0,0 +1,2 @@
|
||||
# aiounifi
|
||||
git+https://github.com/ViViDboarder/aiounifi.git@traffic-routes
|
Loading…
Reference in New Issue
Block a user