diff --git a/code/game/jobs/job/support.dm b/code/game/jobs/job/support.dm
index 12edcc77a66..315171fbdb9 100644
--- a/code/game/jobs/job/support.dm
+++ b/code/game/jobs/job/support.dm
@@ -407,6 +407,7 @@
l_ear = /obj/item/radio/headset/headset_service
id = /obj/item/card/id/janitor
pda = /obj/item/pda/janitor
+ r_pocket = /obj/item/door_remote/janikeyring
//More or less assistants
diff --git a/code/game/objects/items/control_wand.dm b/code/game/objects/items/control_wand.dm
index a0235f9f4a7..561fb62fd87 100644
--- a/code/game/objects/items/control_wand.dm
+++ b/code/game/objects/items/control_wand.dm
@@ -155,7 +155,82 @@
busy = FALSE
icon_state = "hacktool"
+/// How long before you can "jangle" your keyring again (to prevent spam)
+#define JANGLE_COOLDOWN 10 SECONDS
+
+/obj/item/door_remote/janikeyring
+ name = "janitor's keyring"
+ desc = "An absolutely unwieldy set of keys attached to a metal ring. The keys on the ring allow you to access most Departmental entries and the Service Department!"
+ icon_state = "keyring"
+ item_state = "keyring"
+ /// Are you already using the keyring?
+ var/busy = FALSE
+ /// This prevents spamming the key-shake.
+ var/cooldown = 0
+ /// How fast does the keyring open an airlock. It is not set here so that it can be set via the user's role.
+ var/hack_speed
+ additional_access = list(ACCESS_MEDICAL, ACCESS_RESEARCH, ACCESS_CONSTRUCTION, ACCESS_CARGO, ACCESS_MINING, ACCESS_KITCHEN, ACCESS_BAR, ACCESS_JANITOR, ACCESS_CHAPEL_OFFICE)
+
+/obj/item/door_remote/janikeyring/examine(mob/user)
+ . = ..()
+ . += "This keyring has access to Medbay, Science, Engineering, Cargo, the Bar and the Kitchen!"
+
+/obj/item/door_remote/janikeyring/attack_self(mob/user)
+ if(cooldown > world.time)
+ return
+ to_chat(user, "You shake [src]!")
+ playsound(src, 'sound/items/keyring_shake.ogg', 50)
+ cooldown = world.time + JANGLE_COOLDOWN
+
+/obj/item/door_remote/janikeyring/afterattack(obj/machinery/door/airlock/D, mob/user, proximity)
+ if(!proximity)
+ return
+ if(!istype(D))
+ return
+ if(busy)
+ to_chat(user, "You are already using [src] on the [D] airlock's access panel!")
+ return
+ busy = TRUE
+ to_chat(user, "You fiddle with [src], trying different keys to open the [D] airlock...")
+ playsound(src, 'sound/items/keyring_unlock.ogg', 50)
+
+ var/mob/living/carbon/human/H = user
+ if(H.mind.assigned_role != "Janitor")
+ hack_speed = rand(30, 60) SECONDS
+ else
+ hack_speed = rand(5, 20) SECONDS
+
+ if(!do_after(user, hack_speed, target = D, progress = 0))
+ return
+ busy = FALSE
+
+ if(!istype(D))
+ return
+
+ if(HAS_TRAIT(D, TRAIT_CMAGGED))
+ to_chat(user, "[src] won't fit in the [D] airlock's access panel, there's slime everywhere!")
+ return
+
+ if(D.is_special)
+ to_chat(user, "[src] cannot fit in the [D] airlock's access panel!")
+ return
+
+ if(!D.arePowerSystemsOn())
+ to_chat(user, "The [D] airlock has no power!")
+ return
+
+ if(D.check_access(ID))
+ D.add_hiddenprint(user)
+ if(D.density)
+ D.open()
+ else
+ to_chat(user, "The [D] airlock is already open!")
+
+ else
+ to_chat(user, "[src] does not seem to have a key for the [D] airlock's access panel!")
+
#undef WAND_OPEN
#undef WAND_BOLT
#undef WAND_EMERGENCY
#undef WAND_SPEED
+#undef JANGLE_COOLDOWN
diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi
index c1417bef93b..93d003964bc 100644
Binary files a/icons/obj/device.dmi and b/icons/obj/device.dmi differ
diff --git a/sound/items/keyring_shake.ogg b/sound/items/keyring_shake.ogg
new file mode 100644
index 00000000000..1014b34150f
Binary files /dev/null and b/sound/items/keyring_shake.ogg differ
diff --git a/sound/items/keyring_unlock.ogg b/sound/items/keyring_unlock.ogg
new file mode 100644
index 00000000000..7810f10d259
Binary files /dev/null and b/sound/items/keyring_unlock.ogg differ