Selaa lähdekoodia

Preserve literal all in firewall rule fields

Proxmox Router 2 päivää sitten
vanhempi
commit
a41d02e39c

+ 3 - 3
README.md

@@ -2,7 +2,7 @@
 
 DebTik is a Debian/Proxmox router appliance with an embedded web interface, REST API and RouterOS-style CLI. It manages network interfaces, routing, firewall/NAT, DHCP/DNS, WireGuard, IPsec, PPP, VRRP and AeroTUN. AeroTUN v3 uses pinned peer identities and encrypted tunnels; it works with native TUN or a veth fallback when `/dev/net/tun` is unavailable.
 
-Current release: **1.3.6**. See [functionality corrections and verification](docs/functionality-fixes.md) for network persistence, firewall isolation/IPv6, service error handling and certificate renewal changes.
+Current release: **1.3.7**. See [functionality corrections and verification](docs/functionality-fixes.md) for network persistence, firewall isolation/IPv6, service error handling and certificate renewal changes.
 
 ## Install
 
@@ -21,10 +21,10 @@ The internal Git server uses a private CA. Trust that CA before cloning, or repl
 ```sh
 git -c http.sslVerify=false \
   -c 'http.pinnedpubkey=sha256//u4xyt0njDzn4BAQnS6OTGrXBxVE8vMg5BSNIuryUsMo=' \
-  clone --depth 1 --branch v1.3.6 https://gogs.eparmak.local/eparmak/DebTik.git
+  clone --depth 1 --branch v1.3.7 https://gogs.eparmak.local/eparmak/DebTik.git
 ```
 
-This verifies the server's pinned key for this command; it does not change global Git settings. Reverify the pin if the server's TLS key changes. For a reproducible fresh installation, `v1.3.6` contains the complete dependency installer and native Linux firewall corrections.
+This verifies the server's pinned key for this command; it does not change global Git settings. Reverify the pin if the server's TLS key changes. For a reproducible fresh installation, `v1.3.7` contains the complete dependency installer and native Linux firewall corrections.
 
 `install.sh` installs and verifies the dependencies listed in [Dependencies](docs/dependencies.md), including Go, WireGuard, firewall, BIRD, DHCP/DNS, PPP clients and servers, strongSwan plugins, diagnostics and the RSTP daemon. It builds `proxmox-router` and `debtik-cli`, installs the updater helpers, and starts `proxmox-router.service`. It can be run again; existing `/etc/proxmox-router` files and tunnel keys are preserved. Previous binaries are saved as `/usr/local/bin/*.previous`. For native TUN in an LXC container, enable `/dev/net/tun` on the Proxmox host; otherwise AeroTUN uses veth.
 

+ 1 - 1
VERSION

@@ -1 +1 @@
-1.3.6
+1.3.7

+ 2 - 0
docs/firewall-native.md

@@ -116,3 +116,5 @@ non-inverted TCP or UDP protocol: configure separate rules for both. Existing
 saved rules keep their prior interpretation until edited. Invalid jumps to
 built-in chains are rejected. A custom chain receives packets only when reached
 through an explicit jump. Address lists, GeoIP and conntrack remain available.
+
+Version 1.3.7 preserves the literal value `all` in non-protocol fields when editing rules. Mangle TTL target examples use `64` to set, `-1` to decrement, and `+1` to increment.

+ 12 - 0
internal/firewall/native_test.go

@@ -208,3 +208,15 @@ func TestNATPortProtocolMustBeExplicit(t *testing.T) {
 		}
 	}
 }
+
+func TestTTLTargetSyntax(t *testing.T) {
+	for _, tc := range []struct{ value, want string }{{"64", "--ttl-set 64"}, {"-1", "--ttl-dec 1"}, {"+1", "--ttl-inc 1"}} {
+		r := MangleRule{Chain: "output", Action: "change-ttl", NewTTL: tc.value, Passthrough: true}
+		if err := ValidateRule("mangle", r); err != nil {
+			t.Fatalf("%s: %v", tc.value, err)
+		}
+		if payload := BuildIPTablesRestore(nil, nil, []MangleRule{r}); !strings.Contains(payload, tc.want) {
+			t.Errorf("%s missing %s", tc.value, tc.want)
+		}
+	}
+}

+ 3 - 3
internal/web/static/app.js

@@ -4129,7 +4129,7 @@ function toggleFwField(fieldKey, expand) {
 
 function setFwFieldVal(fieldKey, value, hasInvert = false) {
   if (fieldKey === 'ipsec' && typeof value === 'string') value = value.replace(':', ',');
-  if (value !== undefined && value !== null && value !== '' && value !== 'all') {
+  if (value !== undefined && value !== null && value !== '' && !(fieldKey === 'proto' && value === 'all')) {
     toggleFwField(fieldKey, true);
     let strVal = String(value);
     if (hasInvert) {
@@ -4869,7 +4869,7 @@ function toggleNATField(fieldKey, expand) {
 
 function setNATFieldVal(fieldKey, value, hasInvert = false) {
   if (fieldKey === 'ipsec' && typeof value === 'string') value = value.replace(':', ',');
-  if (value !== undefined && value !== null && value !== '' && value !== 'all') {
+  if (value !== undefined && value !== null && value !== '' && !(fieldKey === 'proto' && value === 'all')) {
     toggleNATField(fieldKey, true);
     let strVal = String(value);
     if (hasInvert) {
@@ -5517,7 +5517,7 @@ function toggleMangleField(fieldKey, expand) {
 
 function setMangleFieldVal(fieldKey, value, hasInvert = false) {
   if (fieldKey === 'ipsec' && typeof value === 'string') value = value.replace(':', ',');
-  if (value !== undefined && value !== null && value !== '' && value !== 'all') {
+  if (value !== undefined && value !== null && value !== '' && !(fieldKey === 'proto' && value === 'all')) {
     toggleMangleField(fieldKey, true);
     let strVal = String(value);
     if (hasInvert) {

+ 1 - 1
internal/web/static/index.html

@@ -8241,7 +8241,7 @@
             <div id="mangle-row-newttl" class="hidden flex items-center text-xs py-1">
               <label class="w-36 sm:w-44 text-right pr-3.5 text-slate-300 shrink-0 select-none">New TTL</label>
               <div class="flex-1">
-                <input type="text" id="mangle-modal-newttl" placeholder="set:64, decrement:1, increment:1" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
+                <input type="text" id="mangle-modal-newttl" placeholder="64 (set), -1 (decrease), +1 (increase)" class="bg-[#15202e] border border-[#26374d] rounded px-2.5 py-1 text-xs text-white font-mono focus:outline-none focus:border-sky-500 w-full">
               </div>
             </div>
 

+ 6 - 0
scripts/firewall-smoke.cjs

@@ -14,6 +14,12 @@ const assert=require('node:assert/strict');const fs=require('fs');
    const chain=await page.evaluate(async({type,open,apply})=>{const key=type==='filter'?'filterRules':type==='nat'?'natRules':'mangleRules';state[key]=[{id:type+'-preview',chain:'CUSTOM_TEST',action:'accept',disabled:false,passthrough:true}];window[open](type+'-preview');const value=document.getElementById(type+'-modal-chain').value;await window[apply](true);return value},{type,open,apply});
    assert.equal(chain,'CUSTOM_TEST');assert.equal(writes.at(-1).body.chain,'CUSTOM_TEST');
   }passed.push('Custom chain preserved in all three forms');
+  for(const [type,prefix,open,apply] of [['filter','fw','openEditFilterModal','applyFilterRule'],['nat','nat','openEditNATModal','applyNATRule'],['mangle','mangle','openEditMangleModal','applyMangleRule']]){
+   const fields=await page.evaluate(async({type,prefix,open,apply})=>{const key=type==='filter'?'filterRules':type==='nat'?'natRules':'mangleRules';state[key]=[{id:type+'-all',chain:type==='nat'?'prerouting':'forward',action:'accept',protocol:'all',content:'all',packet_mark:'all',passthrough:true}];window[open](type+'-all');const v={content:document.getElementById(prefix+'-val-content').value,contentEnabled:!document.getElementById(prefix+'-field-content').classList.contains('hidden'),mark:document.getElementById(prefix+'-val-pktmark').value,markEnabled:!document.getElementById(prefix+'-field-pktmark').classList.contains('hidden'),protocolEnabled:!document.getElementById(prefix+'-field-proto').classList.contains('hidden')};await window[apply](true);return v},{type,prefix,open,apply});
+   assert.deepEqual(fields,{content:'all',contentEnabled:true,mark:'all',markEnabled:true,protocolEnabled:false});
+   assert.equal(writes.at(-1).body.content,'all');assert.equal(writes.at(-1).body.packet_mark,'all');assert.equal(writes.at(-1).body.protocol,'all');
+  }passed.push('Literal all survives edit and save for all three tables');
+  assert.equal(await page.locator('#mangle-modal-newttl').getAttribute('placeholder'),'64 (set), -1 (decrease), +1 (increase)');passed.push('TTL examples use supported syntax');
   for(const [type,prefix,open,apply]of [['filter','fw','openAddFilterModal','applyFilterRule'],['nat','nat','openAddNATModal','applyNATRule']]){
    await page.evaluate(async({type,prefix,open,apply})=>{window[open]();window[prefix==='fw'?'toggleFwField':'toggleNATField']('days',true);for(const d of ['sun','mon','tue','wed','thu','fri','sat'])document.getElementById(prefix+'-day-'+d).checked=d==='fri';await window[apply](true)},{type,prefix,open,apply});assert.equal(writes.at(-1).body.days,'fri');
   }passed.push('Weekdays serialized for Filter and NAT');