mirror of
https://github.com/jung-geun/policy-routing.git
synced 2025-12-20 02:34:39 +09:00
Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 383f7cd2f2 | |||
| 7839226e72 | |||
| 0ae3cfb507 | |||
| 1d27875bd9 | |||
| 607dac267e | |||
| 4be94ade77 | |||
| 77af62ed56 | |||
| 2e67e8f777 | |||
| c8b62a71e1 | |||
| 38e473e336 | |||
| 16a19e8da9 | |||
| 3bc1a9cf33 | |||
| 1da112f914 |
33
.gitlab-ci.yml
Normal file
33
.gitlab-ci.yml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
# You can override the included template(s) by including variable overrides
|
||||||
|
# SAST customization: https://docs.gitlab.com/ee/user/application_security/sast/#customizing-the-sast-settings
|
||||||
|
# Secret Detection customization: https://docs.gitlab.com/ee/user/application_security/secret_detection/pipeline/#customization
|
||||||
|
# Dependency Scanning customization: https://docs.gitlab.com/ee/user/application_security/dependency_scanning/#customizing-the-dependency-scanning-settings
|
||||||
|
# Container Scanning customization: https://docs.gitlab.com/ee/user/application_security/container_scanning/#customizing-the-container-scanning-settings
|
||||||
|
# Note that environment variables can be set in several places
|
||||||
|
# See https://docs.gitlab.com/ee/ci/variables/#cicd-variable-precedence
|
||||||
|
stages:
|
||||||
|
- build
|
||||||
|
- test
|
||||||
|
- deploy
|
||||||
|
- review
|
||||||
|
- dast
|
||||||
|
- staging
|
||||||
|
- canary
|
||||||
|
- production
|
||||||
|
- incremental rollout 10%
|
||||||
|
- incremental rollout 25%
|
||||||
|
- incremental rollout 50%
|
||||||
|
- incremental rollout 100%
|
||||||
|
- performance
|
||||||
|
- cleanup
|
||||||
|
sast:
|
||||||
|
stage: test
|
||||||
|
include:
|
||||||
|
- template: Auto-DevOps.gitlab-ci.yml
|
||||||
|
|
||||||
|
python_tests:
|
||||||
|
stage: test
|
||||||
|
image: python:3.9-slim-buster
|
||||||
|
script:
|
||||||
|
- pip install pytest
|
||||||
|
- pytest test_policy_routing.py
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2025 봉정근
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
42
README.md
Normal file
42
README.md
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
# Policy Routing
|
||||||
|
|
||||||
|
이 프로젝트는 정책 기반 라우팅을 구현하기 위한 Python 스크립트입니다. 이 스크립트는 특정 IP 주소에 대해 지정된 게이트웨이를 사용하여 패킷을 라우팅합니다.
|
||||||
|
|
||||||
|
사전 조건으로는 `iproute2` 패키지가 설치되어 있어야 하며, 이 패키지는 Linux 시스템에서 네트워크 인터페이스와 라우팅 테이블을 관리하는 데 사용됩니다.
|
||||||
|
NIC 의 ip 설정이 미리 되어 있어야 합니다.
|
||||||
|
|
||||||
|
## 기능
|
||||||
|
|
||||||
|
- 특정 IP 주소에 대해 지정된 게이트웨이를 사용하여 패킷 라우팅
|
||||||
|
- 라우팅 테이블을 생성하고, 해당 테이블에 규칙을 추가하여 정책 기반 라우팅을 구현
|
||||||
|
- 자동으로 NIC를 검색하고, 해당 NIC에 대한 라우팅 테이블을 설정
|
||||||
|
|
||||||
|
# 사용 방법
|
||||||
|
|
||||||
|
## 로컬에 자동 PBR 시스템 구성
|
||||||
|
|
||||||
|
스크립트는 아래 명령어로 다운로드 받을 수 있습니다
|
||||||
|
|
||||||
|
```bash
|
||||||
|
wget -O policy_routing.py https://git.dmslab.xyz/dmslab/policy-routing/-/raw/main/policy_routing.py
|
||||||
|
# or
|
||||||
|
curl -o policy_routing.py https://git.dmslab.xyz/dmslab/policy-routing/-/raw/main/policy_routing.py
|
||||||
|
```
|
||||||
|
|
||||||
|
다운로드한 스크립트를 install 옵션으로 시스템 데몬으로 설치할 수 있습니다
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo python3 policy_routing.py install
|
||||||
|
```
|
||||||
|
|
||||||
|
실제 인터페이스가 감지되고 수행이 되었는지 체크할 수 있습니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo python3 policy_routing.py status
|
||||||
|
```
|
||||||
|
|
||||||
|
ip rule 을 확인하여 정책 기반 라우팅이 설정되었는지 확인할 수 있습니다.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ip rule ls
|
||||||
|
```
|
||||||
65
packer-openstack-ubuntu.json
Normal file
65
packer-openstack-ubuntu.json
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"variables": {
|
||||||
|
"openstack_auth_url": "{{env `OS_AUTH_URL`}}",
|
||||||
|
"openstack_username": "{{env `OS_USERNAME`}}",
|
||||||
|
"openstack_password": "{{env `OS_PASSWORD`}}",
|
||||||
|
"openstack_tenant_name": "{{env `OS_TENANT_NAME`}}",
|
||||||
|
"openstack_domain_name": "{{env `OS_USER_DOMAIN_NAME`}}",
|
||||||
|
"openstack_region": "{{env `OS_REGION_NAME`}}",
|
||||||
|
"source_image_id": "{{env `OS_SOURCE_IMAGE_ID`}}",
|
||||||
|
"flavor_name": "cpu.2c_2g",
|
||||||
|
"network_name": "{{env `OS_NETWORK_NAME`}}",
|
||||||
|
"image_name": "ubuntu 22.04-{{timestamp}} server",
|
||||||
|
"floating_ip_pool": "{{env `OS_FLOATING_IP_POOL`}}",
|
||||||
|
"ssh_username": "ubuntu"
|
||||||
|
},
|
||||||
|
"builders": [
|
||||||
|
{
|
||||||
|
"type": "openstack",
|
||||||
|
"identity_endpoint": "{{user `openstack_auth_url`}}",
|
||||||
|
"username": "{{user `openstack_username`}}",
|
||||||
|
"password": "{{user `openstack_password`}}",
|
||||||
|
"tenant_name": "{{user `openstack_tenant_name`}}",
|
||||||
|
"domain_name": "{{user `openstack_domain_name`}}",
|
||||||
|
"region": "{{user `openstack_region`}}",
|
||||||
|
"image_name": "{{user `image_name`}}",
|
||||||
|
"source_image": "{{user `source_image_id`}}",
|
||||||
|
"flavor": "{{user `flavor_name`}}",
|
||||||
|
"networks": [
|
||||||
|
"{{user `network_name`}}"
|
||||||
|
],
|
||||||
|
"ssh_username": "{{user `ssh_username`}}",
|
||||||
|
"security_groups": [
|
||||||
|
"default"
|
||||||
|
],
|
||||||
|
"floating_ip_pool": "private_provider",
|
||||||
|
"use_floating_ip": true,
|
||||||
|
"ssh_timeout": "10m",
|
||||||
|
"image_disk_format": "raw",
|
||||||
|
"use_blockstorage_volume": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"provisioners": [
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"inline": [
|
||||||
|
"sudo apt-get update",
|
||||||
|
"sudo apt-get upgrade -y",
|
||||||
|
"sudo apt-get autoremove -y",
|
||||||
|
"echo 'Initial system updates and cleanup complete.'"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "file",
|
||||||
|
"source": "pbr-script-cloud-init.yaml",
|
||||||
|
"destination": "/tmp/pbr-script-cloud-init.yaml"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell",
|
||||||
|
"inline": [
|
||||||
|
"sudo mv /tmp/pbr-script-cloud-init.yaml /etc/cloud/cloud.cfg.d/99-custom-pbr-script.cfg",
|
||||||
|
"echo 'Cloud-init configuration moved to /etc/cloud/cloud.cfg.d/'"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
45
pbr-script-cloud-init.yaml
Normal file
45
pbr-script-cloud-init.yaml
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
#cloud-config
|
||||||
|
|
||||||
|
write_files:
|
||||||
|
- path: /tmp/pbr-script-cloud-init.sh
|
||||||
|
permissions: '0755'
|
||||||
|
owner: root:root
|
||||||
|
content: |
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# GitLab 스크립트 URL (공개 저장소 또는 접근 가능한 URL)
|
||||||
|
# 예시: GitLab Pages, Raw 파일 URL 등
|
||||||
|
# private repository인 경우 인증 관련 부분을 추가해야 합니다. (아래 설명)
|
||||||
|
SCRIPT_URL="https://git.dmslab.xyz/dmslab/policy-routing/-/raw/main/policy_routing.py"
|
||||||
|
DEST_PATH="/opt/PBR/routing.py"
|
||||||
|
|
||||||
|
# 스크립트 저장될 디렉토리 생성 (필요하다면)
|
||||||
|
mkdir -p $(dirname "${DEST_PATH}")
|
||||||
|
|
||||||
|
echo "Downloading script from ${SCRIPT_URL}..."
|
||||||
|
# wget 또는 curl 사용
|
||||||
|
# wget이 일반적으로 더 많이 사용됨
|
||||||
|
if command -v wget &> /dev/null
|
||||||
|
then
|
||||||
|
wget -O "${DEST_PATH}" "${SCRIPT_URL}"
|
||||||
|
elif command -v curl &> /dev/null
|
||||||
|
then
|
||||||
|
curl -o "${DEST_PATH}" "${SCRIPT_URL}"
|
||||||
|
else
|
||||||
|
echo "Error: Neither wget nor curl found. Cannot download script."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
echo "Script downloaded successfully to ${DEST_PATH}. Executing..."
|
||||||
|
chmod +x "${DEST_PATH}" # 실행 권한 부여
|
||||||
|
"${DEST_PATH}" install # 스크립트 실행
|
||||||
|
else
|
||||||
|
echo "Error: Failed to download script from ${SCRIPT_URL}."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Script execution finished."
|
||||||
|
|
||||||
|
runcmd:
|
||||||
|
- /tmp/pbr-script-cloud-init.sh
|
||||||
1582
policy_routing.py
1582
policy_routing.py
File diff suppressed because it is too large
Load Diff
@@ -206,6 +206,7 @@ class TestPolicyRoutingManager(unittest.TestCase):
|
|||||||
}
|
}
|
||||||
table_id = 101
|
table_id = 101
|
||||||
priority = 30100
|
priority = 30100
|
||||||
|
metric = 1000 # Add metric for testing
|
||||||
|
|
||||||
# run_command의 side_effect를 설정하여 각 호출에 대한 응답을 시뮬레이션
|
# run_command의 side_effect를 설정하여 각 호출에 대한 응답을 시뮬레이션
|
||||||
# 순서대로 호출될 명령에 대한 응답을 정의
|
# 순서대로 호출될 명령에 대한 응답을 정의
|
||||||
@@ -213,14 +214,14 @@ class TestPolicyRoutingManager(unittest.TestCase):
|
|||||||
(True, ""), # 1. ip rule del from 192.168.1.10/32
|
(True, ""), # 1. ip rule del from 192.168.1.10/32
|
||||||
(True, ""), # 2. ip route show table 101 (empty, so flush is skipped)
|
(True, ""), # 2. ip route show table 101 (empty, so flush is skipped)
|
||||||
(True, ""), # 3. ip route add network
|
(True, ""), # 3. ip route add network
|
||||||
(True, ""), # 4. ip route add default
|
(True, ""), # 4. ip route add default (with metric)
|
||||||
(True, ""), # 5. ip rule add from
|
(True, ""), # 5. ip rule add from
|
||||||
(True, ""), # 6. ip rule add iif
|
(True, ""), # 6. ip rule add iif
|
||||||
(True, "30100: from 192.168.1.10 lookup 101"), # 7. ip rule show (verification)
|
(True, "30100: from 192.168.1.10 lookup 101"), # 7. ip rule show (verification)
|
||||||
(True, "default via 192.168.1.1 dev eth0 table 101") # 8. ip route show table 101 (verification)
|
(True, "default via 192.168.1.1 dev eth0 table 101 metric 1000") # 8. ip route show table 101 (verification)
|
||||||
]
|
]
|
||||||
|
|
||||||
success = self.manager.apply_interface_routing(interface_info, table_id, priority)
|
success = self.manager.apply_interface_routing(interface_info, table_id, priority, metric)
|
||||||
|
|
||||||
self.assertTrue(success)
|
self.assertTrue(success)
|
||||||
self.manager.logger.error.assert_not_called()
|
self.manager.logger.error.assert_not_called()
|
||||||
@@ -231,7 +232,7 @@ class TestPolicyRoutingManager(unittest.TestCase):
|
|||||||
mock.call(['ip', 'rule', 'del', 'from', '192.168.1.10/32'], ignore_errors=['No such file or directory']), # This is the second call
|
mock.call(['ip', 'rule', 'del', 'from', '192.168.1.10/32'], ignore_errors=['No such file or directory']), # This is the second call
|
||||||
# mock.call(['ip', 'route', 'flush', 'table', '101'], ignore_errors=['No such file or directory']), # Removed, as table is empty
|
# mock.call(['ip', 'route', 'flush', 'table', '101'], ignore_errors=['No such file or directory']), # Removed, as table is empty
|
||||||
mock.call(['ip', 'route', 'add', '192.168.1.0/24', 'dev', 'eth0', 'src', '192.168.1.10', 'table', '101'], ignore_errors=['File exists']),
|
mock.call(['ip', 'route', 'add', '192.168.1.0/24', 'dev', 'eth0', 'src', '192.168.1.10', 'table', '101'], ignore_errors=['File exists']),
|
||||||
mock.call(['ip', 'route', 'add', 'default', 'via', '192.168.1.1', 'dev', 'eth0', 'table', '101'], ignore_errors=['File exists']),
|
mock.call(['ip', 'route', 'add', 'default', 'via', '192.168.1.1', 'dev', 'eth0', 'table', '101', 'metric', '1000'], ignore_errors=['File exists']),
|
||||||
mock.call(['ip', 'rule', 'add', 'from', '192.168.1.10/32', 'table', '101', 'pref', '30100'], ignore_errors=['File exists']),
|
mock.call(['ip', 'rule', 'add', 'from', '192.168.1.10/32', 'table', '101', 'pref', '30100'], ignore_errors=['File exists']),
|
||||||
mock.call(['ip', 'rule', 'add', 'iif', 'eth0', 'table', '101', 'pref', '30101'], ignore_errors=['File exists']),
|
mock.call(['ip', 'rule', 'add', 'iif', 'eth0', 'table', '101', 'pref', '30101'], ignore_errors=['File exists']),
|
||||||
mock.call(['ip', 'rule', 'show']),
|
mock.call(['ip', 'rule', 'show']),
|
||||||
|
|||||||
Reference in New Issue
Block a user