Skip to content

Commit 0a4e6ad

Browse files
Merge pull request #2 from Burzah/proxyDaemon
Adds ACL cleanup job
2 parents 9b8d49a + dda20ba commit 0a4e6ad

5 files changed

Lines changed: 219 additions & 1 deletion

File tree

TaskDaemon.Core/src/me/aa07/paradise/taskdaemon/core/Core.java

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import java.util.Optional;
66
import me.aa07.paradise.taskdaemon.core.config.ConfigHolder;
77
import me.aa07.paradise.taskdaemon.core.database.DbCore;
8+
import me.aa07.paradise.taskdaemon.core.modules.aclcleanup.AclCleanupJob;
89
import me.aa07.paradise.taskdaemon.core.modules.bouncerrestart.BouncerRestartJob;
910
import me.aa07.paradise.taskdaemon.core.modules.ip2asn.Ip2AsnJob;
1011
import me.aa07.paradise.taskdaemon.core.modules.profilercleanup.ProfilerCleanupJob;
@@ -81,6 +82,20 @@ private void setupJobs(Scheduler scheduler, DbCore dbCore, ConfigHolder config,
8182
// See below for CRON format
8283
// https://www.quartz-scheduler.org/documentation/quartz-2.3.0/tutorials/crontrigger.html
8384

85+
// ACL cleanup
86+
JobDataMap jdm_aclcleanup = new JobDataMap();
87+
jdm_aclcleanup.put("LOGGER", logger);
88+
jdm_aclcleanup.put("DBCORE", dbCore);
89+
jdm_aclcleanup.put("PFSENSE_CFG", config.pfsense);
90+
JobDetail jd_aclcleanup = JobBuilder.newJob(AclCleanupJob.class)
91+
.withIdentity("aclcleanup", "aclcleanup")
92+
.usingJobData(jdm_aclcleanup)
93+
.build();
94+
CronTrigger ct_aclcleanup = TriggerBuilder.newTrigger()
95+
.withIdentity("aclcleanup", "aclcleanup")
96+
.withSchedule(CronScheduleBuilder.cronSchedule("0 */10 * * * ?"))
97+
.build();
98+
8499
// Bouncer restart
85100
JobDataMap jdm_bouncerrestart = new JobDataMap();
86101
jdm_bouncerrestart.put("LOGGER", logger);
@@ -121,8 +136,8 @@ private void setupJobs(Scheduler scheduler, DbCore dbCore, ConfigHolder config,
121136
.withSchedule(CronScheduleBuilder.cronSchedule("0 0 8 * * ?")) // Every day - 8AM
122137
.build();
123138

124-
125139
// Schedule all
140+
scheduler.scheduleJob(jd_aclcleanup, ct_aclcleanup);
126141
scheduler.scheduleJob(jd_bouncerrestart, ct_bouncerrestart);
127142
scheduler.scheduleJob(jd_ip2asn, ct_ip2asn);
128143
scheduler.scheduleJob(jd_profilercleanup, ct_profilercleanup);

TaskDaemon.Core/src/me/aa07/paradise/taskdaemon/core/config/ConfigHolder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
public class ConfigHolder {
44
public DatabaseConfig gameDatabase;
55
public Ip2AsnSerivceConfig ip2asn;
6+
public PfsenseConfig pfsense;
67
public DatabaseConfig profilerDatabase;
78
public RedisConfig redis;
89
public TgsConfig tgs;
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package me.aa07.paradise.taskdaemon.core.config;
2+
3+
public class PfsenseConfig{
4+
public String host;
5+
public int port;
6+
}
Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
package me.aa07.paradise.taskdaemon.core.modules.aclcleanup;
2+
3+
import java.io.IOException;
4+
import java.io.InputStream;
5+
import java.io.OutputStream;
6+
import java.net.Socket;
7+
import java.time.LocalDateTime;
8+
import java.util.ArrayList;
9+
import java.util.List;
10+
import java.util.Optional;
11+
12+
import me.aa07.paradise.taskdaemon.core.config.PfsenseConfig;
13+
import me.aa07.paradise.taskdaemon.core.database.DatabaseType;
14+
import me.aa07.paradise.taskdaemon.core.database.DbCore;
15+
import me.aa07.paradise.taskdaemon.database.gamedb.Tables;
16+
import org.apache.logging.log4j.Logger;
17+
import org.jooq.DSLContext;
18+
import org.quartz.Job;
19+
import org.quartz.JobDataMap;
20+
import org.quartz.JobExecutionContext;
21+
import org.quartz.JobExecutionException;
22+
23+
/**
24+
* Scheduled job that cleans up ACL by removing inactive player IPs
25+
*/
26+
public class AclCleanupJob implements Job {
27+
28+
/**
29+
* Main job execution - fetches current ACL, checks database for inactive IPs, and removes them
30+
*/
31+
@Override
32+
public void execute(JobExecutionContext context) throws JobExecutionException {
33+
JobDataMap datamap = context.getMergedJobDataMap();
34+
35+
// Get our logger - important
36+
Object raw_logger = datamap.get("LOGGER");
37+
Optional<Logger> logger_holder = Optional.empty();
38+
39+
if (raw_logger instanceof Logger l2) {
40+
logger_holder = Optional.of(l2);
41+
}
42+
43+
if (!logger_holder.isPresent()) {
44+
System.out.println("[AclCleanup] LOGGER WAS SOMEHOW NULL - THIS IS VERY BAD");
45+
return;
46+
}
47+
48+
Logger logger = logger_holder.get();
49+
50+
// Now get our DB
51+
Object raw_db = datamap.get("DBCORE");
52+
Optional<DbCore> dbcore_holder = Optional.empty();
53+
54+
if (raw_db instanceof DbCore db2) {
55+
dbcore_holder = Optional.of(db2);
56+
}
57+
58+
if (!dbcore_holder.isPresent()) {
59+
logger.error("[AclCleanup] DBCORE WAS SOMEHOW NULL - THIS IS VERY BAD");
60+
return;
61+
}
62+
63+
DbCore dbcore = dbcore_holder.get();
64+
65+
// Get pfSense configuration
66+
Object raw_pfs_cfg = datamap.get("PFSENSE_CFG");
67+
Optional<PfsenseConfig> pfs_cfg_holder = Optional.empty();
68+
69+
if (raw_pfs_cfg instanceof PfsenseConfig pfsCfg) {
70+
pfs_cfg_holder = Optional.of(pfsCfg);
71+
}
72+
73+
if (pfs_cfg_holder.isEmpty()) {
74+
logger.error("[AclCleanup] PFSENSE_CFG WAS SOMEHOW NULL - THIS IS VERY BAD");
75+
return;
76+
}
77+
78+
PfsenseConfig pfs_cfg = pfs_cfg_holder.get();
79+
80+
try {
81+
List<String> current_acl_ips = fetchAcl(logger, pfs_cfg);
82+
logger.info("IPs in ACL ({}): {}", current_acl_ips.size(), String.join(" ", current_acl_ips));
83+
84+
List<String> ips_to_remove = checkIpsInDatabase(current_acl_ips, logger, dbcore);
85+
86+
if (ips_to_remove.isEmpty()) {
87+
logger.info("No IPs to remove from ACL");
88+
} else {
89+
logger.info("IPs to remove from ACL ({}): {}", ips_to_remove.size(), String.join(" ", ips_to_remove));
90+
91+
for (String ip : ips_to_remove) {
92+
removeIpFromAcl(ip, logger, pfs_cfg);
93+
}
94+
95+
logger.info("Removed {} IPs from ACL", ips_to_remove.size());
96+
}
97+
98+
List<String> updated_acl_ips = fetchAcl(logger, pfs_cfg);
99+
logger.info("IPs now in ACL ({}): {}", updated_acl_ips.size(), String.join(" ", updated_acl_ips));
100+
101+
} catch (IOException e) {
102+
logger.error("Error in AclCleanupJob!");
103+
logger.error(e);
104+
}
105+
}
106+
107+
/**
108+
* Checks which IPs should be removed from ACL based on database activity
109+
* Removes IPs that don't exist in DB or haven't been seen in last 10 minutes
110+
*/
111+
public List<String> checkIpsInDatabase(List<String> ips, Logger logger, DbCore dbcore) {
112+
List<String> to_remove = new ArrayList<>();
113+
114+
DSLContext ctx = dbcore.jooq(DatabaseType.GameDb);
115+
LocalDateTime ten_mins_ago = dbcore.now().minusMinutes(10);
116+
117+
for (String ip : ips) {
118+
if (!ctx.fetchExists(ctx.select(Tables.PLAYER.CKEY).from(Tables.PLAYER).where(Tables.PLAYER.IP.eq(ip)))) {
119+
logger.info("IP {} no longer in database.", ip);
120+
to_remove.add(ip);
121+
continue;
122+
}
123+
124+
boolean seen_in_last_10m = ctx.fetchExists(
125+
ctx.select(Tables.PLAYER.CKEY).from(Tables.PLAYER).where(Tables.PLAYER.IP.eq(ip))
126+
.and(Tables.PLAYER.LASTSEEN.lt(ten_mins_ago))
127+
);
128+
129+
if (!seen_in_last_10m) {
130+
logger.info("IP {} not active within last 10 minutes.", ip);
131+
to_remove.add(ip);
132+
}
133+
}
134+
135+
return to_remove;
136+
}
137+
138+
/**
139+
* Retrieves current IP list from ACL via socket connection
140+
*/
141+
public List<String> fetchAcl(Logger logger, PfsenseConfig cfg) throws IOException {
142+
List<String> ip_list = new ArrayList<>();
143+
144+
Socket socket = new Socket(cfg.host, cfg.port);
145+
socket.setSoTimeout(10000);
146+
147+
OutputStream out = socket.getOutputStream();
148+
InputStream in = socket.getInputStream();
149+
150+
String command = "show acl #1\n";
151+
out.write(command.getBytes("ascii"));
152+
153+
StringBuilder retval = new StringBuilder();
154+
byte[] buffer = new byte[16];
155+
int bytes_read;
156+
157+
while ((bytes_read = in.read(buffer)) != -1) {
158+
retval.append(new String(buffer, 0, bytes_read));
159+
}
160+
161+
socket.close();
162+
163+
String[] raw_ips = retval.toString().split("\n");
164+
for (String ip : raw_ips) {
165+
if (ip.trim().isEmpty()) {
166+
continue;
167+
}
168+
169+
String[] parts = ip.split(" ");
170+
if (parts.length >= 2) {
171+
ip_list.add(parts[1]);
172+
}
173+
}
174+
175+
return ip_list;
176+
}
177+
178+
/**
179+
* Removes a specific IP from ACL #1 via socket command
180+
*/
181+
public void removeIpFromAcl(String ip, Logger logger, PfsenseConfig cfg) throws IOException {
182+
logger.info("Removing {} from ACL", ip);
183+
184+
Socket socket = new Socket(cfg.host, cfg.port);
185+
OutputStream out = socket.getOutputStream();
186+
187+
String command = "del acl #1 " + ip + "\n";
188+
out.write(command.getBytes("ascii"));
189+
190+
socket.close();
191+
}
192+
}

config.toml.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ database = "paradise_profilerdaemon"
99
[ip2asn]
1010
host = "http://127.0.0.1:53661/v1/as/ip/" # Fill out URL - IP will be appended at the end
1111

12+
[pfsense]
13+
host = "127.0.0.1"
14+
port = 1234
15+
1216
[profilerDatabase]
1317
host = "172.16.0.200"
1418
username = "myuser"

0 commit comments

Comments
 (0)